]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/programs/Xserver/Xext/xtest1dd.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / programs / Xserver / Xext / xtest1dd.c
diff --git a/Xserver/programs/Xserver/Xext/xtest1dd.c b/Xserver/programs/Xserver/Xext/xtest1dd.c
new file mode 100644 (file)
index 0000000..8edcff9
--- /dev/null
@@ -0,0 +1,1653 @@
+/* $XConsortium: xtest1dd.c,v 1.14 94/04/17 20:33:00 gildea Exp $ */
+/* $XFree86: xc/programs/Xserver/Xext/xtest1dd.c,v 3.0 1996/05/06 05:55:42 dawes Exp $ */
+/*
+ *     File: xtest1dd.c
+ *
+ *     This file contains the device dependent parts of the input
+ *     synthesis extension.
+ */
+
+/*
+
+
+Copyright (c) 1986, 1987, 1988   X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1986, 1987, 1988 by Hewlett-Packard Corporation
+
+Permission to use, copy, modify, and distribute this
+software and its documentation for any purpose and without
+fee is hereby granted, provided that the above copyright
+notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting
+documentation, and that the name of Hewlett-Packard not be used in
+advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+Hewlett-Packard makes no representations about the 
+suitability of this software for any purpose.  It is provided 
+"as is" without express or implied warranty.
+
+This software is not subject to any license of the American
+Telephone and Telegraph Company or of the Regents of the
+University of California.
+
+*/
+
+/***************************************************************
+ * include files
+ ***************************************************************/
+
+#define        NEED_EVENTS
+#define        NEED_REPLIES
+
+#include <stdio.h>
+#include "Xos.h"
+#include "X.h"
+#include "Xmd.h"
+#include "Xproto.h"
+#include "misc.h"
+#include "dixstruct.h"
+#define  XTestSERVER_SIDE
+#include "xtestext1.h" 
+
+#include "xtest1dd.h"
+
+/***************************************************************
+ * defines
+ ***************************************************************/
+
+/*
+ * the size of the fake input action array
+ */
+#define ACTION_ARRAY_SIZE      100
+
+/***************************************************************
+ * externals
+ ***************************************************************/
+
+/*
+ * Holds the xTestInputAction event type code.
+ * This is defined in xtestext1di.c.
+ */
+extern int                     XTestInputActionType;
+/*
+ * Holds the xTestFakeAck event type code.
+ * This is defined in xtestext1di.c.
+ */
+extern int                     XTestFakeAckType;
+/*
+ * used in the WriteReplyToClient macro
+ */
+extern int                     exclusive_steal;
+
+/***************************************************************
+ * variables
+ ***************************************************************/
+
+/*
+ * array to hold fake input actions
+ */
+struct {
+       /*
+        * holds the action type, one of: XTestDELAY_ACTION,
+        * XTestKEY_ACTION, XTestMOTION_ACTION, XTestJUMP_ACTION
+        */
+       CARD8   type;   
+       /*
+        * holds the device type, in the range 0 to 15
+        */
+       CARD8   device;
+       /*
+        * for XTestKEY_ACTION type, holds the keycode
+        */
+       CARD8   keycode;
+       /*
+        * for XTestKEY_ACTION type, holds the key up/down state
+        */
+       CARD8   keystate;
+       /*
+        * for XTestMOTION_ACTION and XTestJUMP_ACTION types,
+        * holds the x and y coordinates to move the mouse to
+        */
+       int     x;
+       int     y;
+       /*
+        * holds the time to delay (in milliseconds) before performing
+        * the action
+        */
+       CARD32  delay_time;
+}action_array[ACTION_ARRAY_SIZE];
+
+/*
+ * write index for input action array
+ */
+static int                     write_index = 0;
+/*
+ * read index for input action array
+ */
+static int                     read_index = 0;
+/*
+ * this is where the input actions are accumulated until they are sent
+ * to a client (in a wire event)
+ */
+static xTestInputActionEvent   input_action_packet;
+/*
+ * holds the index (in bytes) into the input actions buffer in the
+ * current input action event
+ */
+static int                     packet_index;
+/*
+ * set to 1 when the input action event is full and needs to be sent to the 
+ * client
+ */
+static int                     input_action_event_full = 0;
+/*
+ * logical x position of the mouse during input action gathering
+ */
+short                          xtest_mousex;
+/*
+ * logical y position of the mouse during input action gathering
+ */
+short                          xtest_mousey;
+/*
+ * logical x position of the mouse during input action playback
+ */
+static short                   mx;
+/*
+ * logical y position of the mouse during input action playback
+ */
+static short                   my;
+/*
+ * logical x position of the mouse while we are reading fake input actions
+ * from the client and putting them into the fake input action array
+ */
+static short                   pmousex;
+/*
+ * logical y position of the mouse while we are reading fake input actions
+ * from the client and putting them into the fake input action array
+ */
+static short                   pmousey;
+/*
+ * The playback_on flag is set to 1 while there are input actions in the 
+ * input action array.  It is set to 0 when the server has received all of
+ * the user actions.
+ */
+int                    playback_on = 0;
+/*
+ * identity of the client using XTestGetInput to get user input actions
+ */
+ClientPtr              current_xtest_client;
+/*
+ * if 1 send multiple input actions per XTestInputAction event;
+ * if 0 send one input action per XTestInputAction event
+ */
+static char                    packed_mode;
+/*
+ * identity of the client using the XTestFakeInput function to send some
+ * fake input actions to the server
+ */
+ClientPtr              playback_client = NULL;
+/*
+ * Set to 1 when the XTestFAKE_ACK_REQUEST flag is set in a XTestFakeInput
+ * request.  Set back to 0 when all of the input actions have been sent
+ * to the server.
+ */
+static int                     acknowledge = 0;
+/*
+ * The server's idea of the current time is saved in these variables when
+ * a XTestFakeInput request is received.  It is restored when all fake input
+ * actions are sent to the server or when the playback client disconnects.
+ */
+static int                     saved_sec;
+static int                     saved_usec;
+/*
+ * Set to 1 when there is a valid time in saved_sec and saved_usec.
+ */
+static int                     time_saved = 0;
+/*
+ * holds the extension's notion of what the current time is while it is 
+ * sending input actions to a client
+ */
+static struct timeval          current_time;
+/*
+ * holds the time when the extension should place the next fake input action
+ * into the server's normal events queue
+ */
+static struct timeval          play_time;
+/*
+ * set to 1 when play_time is first set, cleared to 0 when the
+ * client using the extension disconnects, or when XTestReset is called
+ */
+static char                    play_clock = 0;
+/*
+ * holds the amount of time left until the next input action from the
+ * input action array can be sent to the server
+ */
+static struct timeval          rtime;
+/*
+ * Set to 1 after the extension is done waiting for the correct time delay
+ * for an input action to be sent to the server.  Remains a 1 until the time
+ * delay for the next input action is computed.  Then set to 0 if the
+ * extension has to wait for the correct time delay.
+ */
+static int                     go_for_next = 1;
+/*
+ * needed to restore waitime if playback is to be aborted
+ */
+static struct timeval          *restorewait;
+/*
+ * tmon special command key
+ *
+ * To use the test monitor program (called tmon) efficiently, it is
+ * desirable to have the extension be able to recognize a special "trigger"
+ * key.  If the extension did not do this, tmon would have to have the
+ * extension send all keyboard user input actions exclusively to tmon,
+ * only to have tmon send them right back if they were not the command key.
+ *
+ * If the extension can recognize the command key, then tmon can let the
+ * extension handle keyboard user input actions normally until the command
+ * key is pressed (and released), and only then have the extension start
+ * sending keyboard user input actions exclusively to tmon.
+ *
+ * Any key on the keyboard can be used for this command key.  It is most
+ * convenient if it is a low-frequency key.  If you want to generate a
+ * normal occurrance of this key to a client, just hit it twice.  Tmon
+ * will recognize the first occurrance of the key, take control of the input
+ * actions, and wait for certain keys.  If it sees another occurrance of the
+ * command key, it will send one occurrance of the command key to the
+ * extension, and go back to waiting.
+ *
+ * set and also referenced in device layer
+ * XXX there should be a way to set this through the protocol
+ */
+KeyCode                        xtest_command_key = 0;
+
+/***************************************************************
+ * function declarations
+ ***************************************************************/
+
+static void    parse_key_fake(
+#if NeedFunctionPrototypes
+                       XTestKeyInfo    * /* fkey */
+#endif
+                       );
+static void    parse_motion_fake(
+#if NeedFunctionPrototypes
+                       XTestMotionInfo * /* fmotion */
+#endif
+                       );
+static void    parse_jump_fake(
+#if NeedFunctionPrototypes
+                       XTestJumpInfo   * /* fjump */
+#endif
+                       );
+static void    parse_delay_fake(
+#if NeedFunctionPrototypes
+                       XTestDelayInfo  * /* tevent */
+#endif
+                       );
+static void    send_ack(
+#if NeedFunctionPrototypes
+                       ClientPtr        /* client */
+#endif
+                       );
+static void    start_play_clock(
+#if NeedFunctionPrototypes
+                       void
+#endif
+                       );
+static void    compute_action_time(
+#if NeedFunctionPrototypes
+                       struct timeval  * /* rtime */
+#endif
+                       );
+static int     find_residual_time(
+#if NeedFunctionPrototypes
+                       struct timeval  * /* rtime */
+#endif
+                       );
+
+static CARD16  check_time_event(
+#if NeedFunctionPrototypes
+                       void
+#endif
+                       );
+static CARD32  current_ms(
+#if NeedFunctionPrototypes
+                       struct timeval  * /* otime */
+#endif
+                       );
+static int     there_is_room(
+#if NeedFunctionPrototypes
+                       int     /* actsize */
+#endif
+                       );
+
+/******************************************************************************
+ *
+ *     stop_stealing_input
+ *
+ *     Stop stealing input actions.
+ */
+void
+stop_stealing_input()
+{
+/*
+ * put any code that you might need to stop stealing input actions here
+ */
+       if (packet_index != 0) 
+       {
+               /*
+                * if there is a partially full input action event waiting
+                * when this function is called, send it to the client
+                */
+               flush_input_actions();
+       }
+}
+
+/******************************************************************************
+ *
+ *     steal_input
+ *
+ *     Start stealing input actions and sending them to the passed-in client.
+ */
+void
+steal_input(client, mode)
+/*
+ * which client is to receive the input action events
+ */
+ClientPtr      client;
+/*
+ * what input action packing mode to use.  one of 0, XTestPACKED_MOTION,
+ * or XTestPACKED_ACTIONS; optionally 'or'ed with XTestEXCLUSIVE,
+ */
+CARD32         mode;
+{
+       if (packet_index != 0) 
+       {
+               /*
+                * if there is a partially full input action event waiting
+                * when this function is called, send it to the client
+                */
+               flush_input_actions();
+       }
+       else
+       {       
+               /*
+                * otherwise, set up a new input action event
+                */
+               input_action_packet.type = XTestInputActionType;
+               packet_index = 0;
+       }
+       /*
+        * set up the new input action packing mode
+        */
+       packed_mode = mode & ~(XTestEXCLUSIVE);
+       /*
+        * keep track of where the mouse is
+        */
+       XTestGetPointerPos(&xtest_mousex, &xtest_mousey);
+       /*
+        * keep track of which client is getting input actions
+        */
+       current_xtest_client = client;
+       /*
+        * find out what time it is
+        */
+       X_GETTIMEOFDAY(&current_time);
+       /*
+        * jump to the initial position of the mouse, using a device type of 0.
+        */
+       XTestStealJumpData(xtest_mousex, xtest_mousey, 0);
+}
+       
+/******************************************************************************
+ *
+ *     flush_input_actions
+ *
+ *     Write the input actions event to the current requesting client
+ *     and re-initialize the input action event.
+ */
+void
+flush_input_actions()
+{
+       /*
+        * pointer to the input action event
+        */
+       char                    *rep;
+       /*
+        * loop index
+        */
+       int                     i;
+
+       if (packet_index == 0)
+       {
+               /*
+                * empty input actions event 
+                */
+               return;
+       }
+       else if (packet_index < XTestACTIONS_SIZE)
+       {
+               /*
+                * fill to the end of the input actions event with 0's
+                */
+               for (i = packet_index; i <XTestACTIONS_SIZE; i++)
+               {
+                       input_action_packet.actions[i] = 0;
+               }
+       }
+       rep = (char *) (&input_action_packet);
+
+       /*
+        * set the serial number of the input action event
+        */
+       input_action_packet.sequenceNumber = current_xtest_client->sequence;
+       /*
+        * send the input action event to the client
+        */
+       WriteEventsToClient(current_xtest_client, 1, (xEvent *) rep);
+       /*
+        * re-initialize the input action event
+        */
+       input_action_event_full = 0;
+       input_action_packet.type = XTestInputActionType;
+       packet_index = 0;
+}      
+
+/******************************************************************************
+ *
+ *     XTestStealJumpData
+ *
+ *     Create one or more input actions and put them in the input action
+ *     event.  The input actions will be an (maybe) XTestDELAY_ACTION
+ *     and an XTestJUMP_ACTION.
+ */
+void
+XTestStealJumpData(jx, jy, dev_type)
+/*
+ * the x and y coordinates to jump to
+ */
+short  jx;
+short  jy;
+/*
+ * which device caused the jump
+ */
+int    dev_type;
+{      
+       XTestJumpInfo   *jmp_ptr;
+       /*
+        * time delta (in ms) from previous event
+        */
+       CARD16                  tchar;
+
+       /*
+        * Get the time delta from the previous event.  If needed,
+        * the check_time_event routine will put an XTestDELAY_ACTION
+        * type action in the input action event.
+        */
+       tchar = check_time_event();
+       if (!there_is_room(sizeof(XTestJumpInfo)))
+       {
+               /*
+                * If there isn't room in the input action event for
+                * an XTestJUMP_ACTION, then send that event to the
+                * client and start filling an empty one.
+                */
+               flush_input_actions();
+       }
+       /*
+        * update the logical mouse position
+        */
+       xtest_mousex = jx;
+       xtest_mousey = jy;
+       /*
+        * point jmp_ptr to the correct place in the input action event
+        */
+       jmp_ptr = (XTestJumpInfo *)
+                 &(input_action_packet.actions[packet_index]);
+       /*
+        * compute the input action header
+        */
+       jmp_ptr->header = (XTestPackDeviceID(dev_type) | XTestJUMP_ACTION);     
+       /*
+        * set the x and y coordinates to jump to in the input action
+        */
+       jmp_ptr->jumpx = jx;
+       jmp_ptr->jumpy = jy;
+       /*
+        * set the delay time in the input action
+        */
+       jmp_ptr->delay_time = tchar;
+       /*
+        * increment the packet index by the size of the input action
+        */
+       packet_index = packet_index + sizeof(XTestJumpInfo);
+       if (packed_mode == 0)
+       {
+               /*
+                * if input actions are not packed, send the input
+                * action event to the client
+                */
+               flush_input_actions();
+       }
+}      
+
+/******************************************************************************
+ *
+ *     current_ms
+ *
+ *     Returns the number of milliseconds from the passed-in time to the
+ *     current time, and then updates the passed-in time to the current time.
+ */
+static CARD32
+current_ms(otime)
+struct timeval *otime;
+{      
+       struct timeval  tval;
+       unsigned long   the_ms;
+       unsigned long   sec;
+       unsigned long   usec;
+
+       /*
+        * get the current time
+        */
+       X_GETTIMEOFDAY(&tval);
+       if (tval.tv_usec < otime->tv_usec)
+       {
+               /*
+                * borrow a second's worth of microseconds if needed
+                */
+               usec = tval.tv_usec - otime->tv_usec + 1000000;
+               sec = tval.tv_sec - 1 - otime->tv_sec;
+       }
+       else
+       {
+               usec = tval.tv_usec - otime->tv_usec;
+               sec = tval.tv_sec - otime->tv_sec;
+       }
+       /*
+        * update the passed-in time to the new time
+        */
+       *otime = tval;
+       /*
+        * compute the number of milliseconds contained in
+        * 'sec' seconds and 'usec' microseconds
+        */
+       the_ms = (sec * 1000000L + usec) / 1000L;
+       return (the_ms);
+}
+
+/******************************************************************************
+ *
+ *     check_time_event
+ *
+ *     If time delta is > XTestSHORT_DELAY_TIME then insert a time event
+ *     and return 0; else return the delay time.
+ */
+static CARD16
+check_time_event()
+{
+       CARD32          tstamp;
+       CARD16          tchar;
+       XTestDelayInfo  *tptr;
+
+       /*
+        * get the number of milliseconds between input actions
+        */
+       tstamp = current_ms(&current_time);
+       /*
+        * if the number of milliseconds is too large to fit in a CARD16,
+        * then add a XTestDELAY_ACTION to the input action event.
+        */
+       if (tstamp > XTestSHORT_DELAY_TIME)
+       {
+               /*
+                * If there isn't room in the input action event for
+                * an XTestDELAY_ACTION, then send that event to the
+                * client and start filling an empty one.
+                */
+               if (!there_is_room(sizeof(XTestDelayInfo)))
+               {
+                       flush_input_actions();
+               }
+               /*
+                * point tptr to the correct place in the input action event
+                */
+               tptr = (XTestDelayInfo *)
+                      (&(input_action_packet.actions[packet_index]));
+               /*
+                * compute the input action header
+                */
+               tptr->header = XTestPackDeviceID(XTestDELAY_DEVICE_ID) |
+                              XTestDELAY_ACTION;
+               /*
+                * set the delay time in the input action
+                */
+               tptr->delay_time = tstamp;
+               /*
+                * increment the packet index by the size of the input action
+                */
+               packet_index = packet_index + (sizeof(XTestDelayInfo));
+               if (packed_mode != XTestPACKED_ACTIONS) 
+               {
+                       /*
+                        * if input actions are not packed, send the input
+                        * action event to the client
+                        */
+                       flush_input_actions();
+               }
+               /*
+                * set the returned delay time to 0
+                */
+               tchar = 0;
+       }
+       else
+       {
+               /*
+                * set the returned delay time to the computed delay time
+                */
+               tchar = tstamp;
+       }
+       return(tchar);
+}
+
+/******************************************************************************
+ *
+ *     there_is_room
+ *
+ *     Checks if there is room in the input_action_packet for an input action
+ *     of the size actsize bytes.  Returns 1 if there is space, 0 otherwise.
+ *
+ */
+static int
+there_is_room(actsize)
+/*
+ * the number of bytes of space needed
+ */
+int    actsize;
+{
+       if ((packet_index + actsize) > XTestACTIONS_SIZE)
+       { 
+               input_action_event_full = 1;
+               return(0);
+       }
+       else
+       {
+               return(1);
+       }
+}
+
+/******************************************************************************
+ *
+ *     XTestStealMotionData
+ *
+ *     Put motion information from the locator into an input action.
+ *
+ *     called from x_hil.c
+ */
+void
+XTestStealMotionData(dx, dy, dev_type, mx, my)
+/*
+ * the x and y delta motion of the locator
+ */
+short  dx;
+short  dy;
+/*
+ * which locator did the moving
+ */
+int    dev_type;
+/*
+ * the x and y position of the locator before the delta motion
+ */
+short  mx;
+short  my;
+{
+       /*
+        * pointer to a XTestMOTION_ACTION input action
+        */
+       XTestMotionInfo *fm;
+       /*
+        * time delta from previous event
+        */
+       CARD16                  tchar;
+
+       /*
+        * if the current position of the locator is not the same as
+        * the logical position, then update the logical position
+        */
+       if ((mx != xtest_mousex) || (my != xtest_mousey))
+       {
+               XTestStealJumpData(mx, my, dev_type);
+       }
+       /*
+        * if the delta motion is outside the range that can
+        * be held in a motion input action, use a jump input action
+        */
+       if ((dx > XTestMOTION_MAX) || (dx < XTestMOTION_MIN) ||
+           (dy > XTestMOTION_MAX) || (dy < XTestMOTION_MIN))
+       {
+               XTestStealJumpData((xtest_mousex + dx),
+                                  (xtest_mousey + dy), dev_type);
+       }
+       else
+       { 
+               /*
+                * compute the new logical position of the mouse
+                */
+               xtest_mousex += dx;
+               xtest_mousey += dy;
+               /*
+                * Get the time delta from the previous event.  If needed,
+                * the check_time_event routine will put an XTestDELAY_ACTION
+                * type action in the input action event.
+                */
+               tchar = check_time_event();
+               /*
+                * If there isn't room in the input action event for
+                * an XTestDELAY_ACTION, then send that event to the
+                * client and start filling an empty one.
+                */
+               if (!there_is_room(sizeof(XTestMotionInfo)))
+               {
+                       flush_input_actions();
+               /*
+                * point fm to the correct place in the input action event
+                */
+               }
+               fm = (XTestMotionInfo *)
+                    &(input_action_packet.actions[packet_index]);
+               /*
+                * compute the input action header
+                */
+               fm->header = XTestMOTION_ACTION;
+               if (dx < 0)     
+               {  
+                       fm->header |= XTestX_NEGATIVE;
+                       dx = abs(dx);
+               }
+               if (dy < 0)   
+               {  
+                       fm->header |= XTestY_NEGATIVE;
+                       dy = abs(dy);
+               }
+               fm->header |= XTestPackDeviceID(dev_type);
+               /*
+                * compute the motion data byte
+                */
+               fm->motion_data = XTestPackYMotionValue(dy);
+               fm->motion_data |= XTestPackXMotionValue(dx);
+               /*
+                * set the delay time in the input action
+                */
+               fm->delay_time = tchar;
+               /*
+                * increment the packet index by the size of the input action
+                */
+               packet_index = packet_index + sizeof(XTestMotionInfo);
+               if (packed_mode == 0)
+               {
+                       /*
+                        * if input actions are not packed, send the input
+                        * action event to the client
+                        */
+                       flush_input_actions();
+               }
+
+       }   
+}
+
+/******************************************************************************
+ *
+ *     XTestStealKeyData
+ *
+ *     Place this key data in the input_action_packet.
+ *
+ */
+Bool
+XTestStealKeyData(keycode, keystate, dev_type, locx, locy)
+/*
+ * which key/button moved
+ */
+CARD8  keycode;
+/*
+ * whether the key/button was pressed or released
+ */
+char   keystate;
+/*
+ * which device caused the input action
+ */
+int    dev_type;
+/*
+ * the x and y coordinates of the locator when the action happenned
+ */
+short  locx;
+short  locy;
+{
+       /*
+        * pointer to key/button motion input action
+        */
+       XTestKeyInfo    *kp;
+       /*
+        * time delta from previous event
+        */
+       CARD16                  tchar;
+       char            keytrans;
+
+       /*
+        * update the logical position of the locator if the physical position
+        * of the locator is not the same as the logical position.
+        */
+       if ((locx != xtest_mousex) || (locy != xtest_mousey))
+       {
+               XTestStealJumpData(locx, locy, dev_type);
+       }
+       /*
+        * Get the time delta from the previous event.  If needed,
+        * the check_time_event routine will put an XTestDELAY_ACTION
+        * type action in the input action event.
+        */
+       tchar = check_time_event();
+       if (!there_is_room(sizeof(XTestKeyInfo)))
+       {
+               /*
+                * If there isn't room in the input action event for
+                * an XTestDELAY_ACTION, then send that event to the
+                * client and start filling an empty one.
+                */
+               flush_input_actions();
+       }
+       /*
+        * point kp to the correct place in the input action event
+        */
+       kp = (XTestKeyInfo *)
+            (&(input_action_packet.actions[packet_index]));
+       /*
+        * compute the input action header
+        */
+       kp->header = XTestPackDeviceID(dev_type);
+       if ((keystate == KeyRelease) || (keystate == ButtonRelease))
+       {
+               keytrans = XTestKEY_UP;
+       }
+       else if ((keystate == KeyPress) || (keystate == ButtonPress))
+       {
+               keytrans = XTestKEY_DOWN;
+       }
+       else
+       {
+               printf("%s: invalid key/button state %d.\n",
+                      XTestEXTENSION_NAME,
+                      keystate);
+       }
+       kp->header = kp->header | keytrans | XTestKEY_ACTION;
+       /*
+        * set the keycode in the input action
+        */
+       kp->keycode = keycode;
+       /*
+        * set the delay time in the input action
+        */
+       kp->delay_time = tchar;
+       /*
+        * increment the packet index by the size of the input action
+        */
+       packet_index = packet_index + sizeof(XTestKeyInfo);
+       /*
+        * if the command key has been released or input actions are not
+        * packed, send the input action event to the client
+        */
+       if(((keycode == xtest_command_key) && (keystate == KeyRelease)) ||
+          (packed_mode != XTestPACKED_ACTIONS))
+       {       
+               flush_input_actions();
+       }
+       /* return TRUE if the event should be passed on to DIX */
+       if (exclusive_steal)
+               return ((keystate == KeyRelease) &&
+                       (keycode == xtest_command_key));
+       else
+               return ((keystate != KeyRelease) ||
+                       (keycode != xtest_command_key));
+}
+
+/******************************************************************************
+ *
+ *     parse_fake_input
+ *
+ *     Parsing routine for a XTestFakeInput request.  It will take a request
+ *     and parse its contents into the input action array.  Eventually the
+ *     XTestProcessInputAction routine will be called to take input actions
+ *     from the input action array and send them to the server to be handled.
+ */
+void
+parse_fake_input(client, req)
+/*
+ * which client did the XTestFakeInput request
+ */
+ClientPtr      client;
+/*
+ * a pointer to the xTestFakeInputReq structure sent by the client
+ */
+char           *req;
+{      
+       /*
+        * if set to 1, done processing input actions from the request
+        */
+       int                     done = 0;
+       /*
+        * type of input action
+        */
+       CARD8                   action_type;
+       /*
+        * device type
+        */
+       CARD8                   dev_type;
+       /*
+        * pointer to an xTestFakeInputReq structure
+        */
+       xTestFakeInputReq       *request;
+       /*
+        * holds the index into the action list in the request
+        */
+       int                     parse_index;    
+
+       /*
+        * get a correct-type pointer to the client-supplied request data
+        */
+       request = (xTestFakeInputReq *) req;
+       /*
+        * save the acknowledge requested state for use in
+        * XTestProcessInputAction
+        */
+       acknowledge = request->ack;
+       /*
+        * set up an index into the action list in the request
+        */
+       parse_index = 0;
+       if (write_index >= ACTION_ARRAY_SIZE)
+       {
+               /*
+                * if the input action array is full, don't add any more
+                */
+               done = 1;
+       }
+       while (!done)
+       { 
+               /*
+                * get the type of input action in the list
+                */
+               action_type = (request->action_list[parse_index])
+                             & XTestACTION_TYPE_MASK;
+               /*
+                * get the type of device in the list
+                */
+               dev_type = XTestUnpackDeviceID(request->action_list[parse_index]);
+               /*
+                * process the input action appropriately
+                */
+               switch (action_type)
+               { 
+               case XTestKEY_ACTION:
+                       parse_key_fake((XTestKeyInfo *)
+                                      &(request->action_list[parse_index]));
+                       parse_index = parse_index + sizeof(XTestKeyInfo);
+                       break;
+               case XTestMOTION_ACTION:
+                       parse_motion_fake((XTestMotionInfo *)
+                                         &(request->action_list[parse_index]));
+                       parse_index = parse_index + sizeof(XTestMotionInfo);
+                       break;
+               case XTestJUMP_ACTION:
+                       parse_jump_fake((XTestJumpInfo *)
+                                       &(request->action_list[parse_index]));
+                       parse_index = parse_index + sizeof(XTestJumpInfo);
+                       break;
+               case XTestDELAY_ACTION:
+                       if (dev_type == XTestDELAY_DEVICE_ID)
+                       { 
+                               parse_delay_fake((XTestDelayInfo *)
+                                                &(request->action_list[parse_index]));
+                               parse_index = parse_index +
+                                             sizeof(XTestDelayInfo);
+                       }
+                       else
+                       { 
+                               /*
+                                * An invalid input action header byte has
+                                * been detected, so there are no more
+                                * input actions in this request.
+                                * The intended invalid action header byte
+                                * for this case should have a value of 0.
+                                */
+                               done = 1;
+                       }
+                       break;
+               }
+               if (parse_index >= XTestMAX_ACTION_LIST_SIZE)
+               {
+                       /*
+                        * entire XTestFakeInput request has been processed
+                        */
+                       done = 1;
+               }
+               if (write_index >= ACTION_ARRAY_SIZE) 
+               {
+                       /*
+                        * no room in the input actions array
+                        */
+                       done = 1;
+               }
+       }
+       if (write_index > read_index)
+       { 
+               /*
+                * there are fake input actions in the input action array
+                * to be given to the server
+                */
+               playback_on = 1;
+               playback_client = client;
+       } 
+}
+
+/******************************************************************************
+ *
+ *     parse_key_fake
+ *
+ *     Called from parse_fake_input.
+ *
+ *     Copy the fake key input action from its packed form into the array of
+ *     pending input events.
+ */
+static void
+parse_key_fake(fkey)
+XTestKeyInfo   *fkey;
+{      
+       action_array[write_index].type = XTestKEY_ACTION;
+       action_array[write_index].device = XTestUnpackDeviceID(fkey->header);
+       action_array[write_index].keycode = fkey->keycode;
+       action_array[write_index].keystate = fkey->header & XTestKEY_STATE_MASK;
+       action_array[write_index].delay_time = fkey->delay_time;
+       write_index++;
+}
+
+/******************************************************************************
+ *
+ *     parse_motion_fake
+ *
+ *     Called from parse_fake_input.
+ *
+ *     Copy the fake motion input action from its packed form into the array of
+ *     pending input events.
+ */
+static void
+parse_motion_fake(fmotion)
+XTestMotionInfo        *fmotion;
+{      
+       int     dx;
+       int     dy;
+
+       dx = (XTestUnpackXMotionValue(fmotion->motion_data));
+       dy = (XTestUnpackYMotionValue(fmotion->motion_data));
+       if (((fmotion->header) & XTestX_SIGN_BIT_MASK) == XTestX_NEGATIVE)
+       {
+               pmousex -= dx;
+       }
+       else
+       {
+               pmousex += dx;
+       }
+       if (((fmotion->header) & XTestY_SIGN_BIT_MASK) == XTestY_NEGATIVE)
+       {
+               pmousey -= dy;
+       }
+       else 
+       {
+               pmousey += dy;
+       }
+       action_array[write_index].type = XTestJUMP_ACTION;
+       action_array[write_index].device = XTestUnpackDeviceID(fmotion->header);
+       action_array[write_index].x = pmousex;
+       action_array[write_index].y = pmousey;
+       action_array[write_index].delay_time = fmotion->delay_time;
+       write_index++;
+}
+
+/******************************************************************************
+ *
+ *     parse_jump_fake
+ *
+ *     Called from parse_fake_input.
+ *
+ *     Copy the fake jump input action from its packed form into the array of
+ *     pending input events.
+ */
+static void
+parse_jump_fake(fjump)
+XTestJumpInfo  *fjump;
+{
+       pmousex = fjump->jumpx;
+       pmousey = fjump->jumpy;
+       action_array[write_index].type = XTestJUMP_ACTION;
+       action_array[write_index].device = XTestUnpackDeviceID(fjump->header);
+       action_array[write_index].x = pmousex;
+       action_array[write_index].y = pmousey;
+       action_array[write_index].delay_time = fjump->delay_time;
+       write_index++;
+}
+
+/******************************************************************************
+ *
+ *     parse_delay_fake
+ *
+ *     Called from parse_fake_input.
+ *
+ *     Copy the fake delay input action from its packed form into the array of
+ *     pending input events.
+ */
+static void
+parse_delay_fake(tevent)
+XTestDelayInfo *tevent;
+{
+       action_array[write_index].type = XTestDELAY_ACTION;
+       action_array[write_index].delay_time = tevent->delay_time;
+       write_index++;
+}
+
+/******************************************************************************
+ *
+ *     XTestComputeWaitTime
+ *
+ *     Compute the amount of time the server should wait before sending the
+ *     next monitor event in playback mode.
+ */
+void
+XTestComputeWaitTime(waittime)
+struct timeval *waittime;
+{      
+       /*
+        * The playback_on flag is set to 1 in parse_fake_input.  It is set to
+        * 0 in XTestProcessInputAction if the server has replayed all input
+        * actions.
+        */
+       if (playback_on)
+       {  
+               if (!play_clock)
+               {
+                       /*
+                        * if the playback clock has never been set,
+                        * then do it now
+                        */
+                       start_play_clock();
+               }
+               /*
+                * We need to save the waittime the first time through.  This
+                * is a value the server uses, and we have to restore it when
+                * all of the input actions are processed by the server.
+                */
+               if (!time_saved)
+               {
+                       saved_sec = waittime->tv_sec;
+                       saved_usec = waittime->tv_usec; 
+                       time_saved = 1;
+               }       
+               if (go_for_next) 
+               {
+                       /*
+                        * if we just processed an input action, figure out
+                        * how long to wait for the next input action
+                        */
+                       compute_action_time(&rtime);
+               }
+               else  
+               {
+                       /*
+                        * else just find out how much more time to wait
+                        * on the current input action
+                        */
+                       (void)find_residual_time(&rtime);
+               }
+               waittime->tv_sec = rtime.tv_sec;
+               waittime->tv_usec = rtime.tv_usec;
+       }
+}
+
+/******************************************************************************
+ *
+ *     XTestProcessInputAction
+ *
+ *     If there are any input actions in the input action array,
+ *     then take one out and process it.
+ *
+ */
+int
+XTestProcessInputAction(readable, waittime)
+/*
+ * This is the value that a 'select' function returned just before this
+ * routine was called.  If the select timed out, this value will be 0.
+ *
+ * This extension modifies the select call's timeout value to cause the
+ * select to time out when the next input action is ready to given to
+ * the server.  This routine is called immediately after the select, to 
+ * give it a chance to process an input action.  If we have an input action
+ * to process and the only reason that the select returned was because it
+ * timed out, then we change the select value to 1 and return 1 instead of 0.
+ */
+int            readable;
+/*
+ * this is the timeout value that the select was called with
+ */
+struct timeval *waittime;
+{      
+int mousex, mousey;
+       /*
+        * if playback_on is 0, then the input action array is empty
+        */
+       if (playback_on)
+       { 
+               restorewait = waittime;
+               /*
+                * figure out if we need to wait for the next input action
+                */
+               if (find_residual_time(&rtime) > 0) 
+               {
+                       /*
+                        * still have to wait before processing the current
+                        * input action
+                        */
+                       go_for_next = 0;
+               }
+               else 
+               {
+                       /*
+                        * don't have to wait any longer before processing
+                        * the current input action
+                        */
+                       go_for_next = 1;
+               }
+               /*
+                * if we have an input action to process and the only reason
+                * that the select returned was because it timed out, then we
+                * change the select value to 1 and return 1 instead of 0
+                */
+               if (readable == 0) 
+               {
+                       readable++;                     
+               }
+               /*
+                * if we don't need to wait, then get an input action from
+                * the input action array and process it
+                */
+               if (go_for_next)
+               {  
+                       /*
+                        * There are three possible types of input actions in
+                        * the input action array (motion input actions are
+                        * converted to jump input actions before being put
+                        * into the input action array).  Delay input actions 
+                        * are processed by the compute_action_time function
+                        * which is called from XTestComputeWaitTime.  The
+                        * other two types of input actions are processed here.
+                        */
+                       if (action_array[read_index].type == XTestJUMP_ACTION)
+                       {       
+                               XTestJumpPointer(
+                                       action_array[read_index].x, 
+                                       action_array[read_index].y, 
+                                       action_array[read_index].device);
+                               mx = action_array[read_index].x;
+                               my = action_array[read_index].y;
+                       }
+                       if (action_array[read_index].type == XTestKEY_ACTION)
+                           {
+                           GetSpritePosition(&mousex, &mousey);
+                           XTestGenerateEvent(
+                                    action_array[read_index].device, 
+                                    action_array[read_index].keycode, 
+                                    action_array[read_index].keystate,
+                                    mousex,
+                                    mousey);
+                           }
+                       read_index++;
+                       /*
+                        * if all input actions are processed, then restore 
+                        * the server state 
+                        */
+                       if (read_index >= write_index)
+                       { 
+                               waittime->tv_sec = saved_sec;
+                               waittime->tv_usec = saved_usec;
+                               time_saved = 0;
+                               playback_on = 0;
+                               if (acknowledge) 
+                               { 
+                                       /*
+                                        * if the playback client is waiting
+                                        * for an xTestFakeAck event, send
+                                        * it to him
+                                        */
+                                       send_ack(playback_client);              
+                                       acknowledge = 0;
+                               }
+                               write_index = 0;
+                               read_index = 0;
+                               playback_client = (ClientPtr) NULL;
+                               play_clock = 0;
+                       }
+               }
+       }
+       return(readable);
+}
+
+/******************************************************************************
+ *
+ *     send_ack
+ *
+ *     send an xTestFakeAck event to the client
+ */
+static void
+send_ack(client)
+ClientPtr      client;
+{
+       xTestFakeAckEvent  rep;
+
+       /*
+        * set the serial number of the xTestFakeAck event
+        */
+       rep.sequenceNumber = client->sequence;
+       rep.type = XTestFakeAckType;
+       WriteEventsToClient(client, 1, (xEvent *) &rep);                
+}              
+
+/******************************************************************************
+ *
+ *     start_play_clock
+ *
+ *     start the clock for play back.
+ */
+static void
+start_play_clock()
+{
+       X_GETTIMEOFDAY(&play_time);
+       /*
+        * flag that play_time is valid
+        */
+       play_clock = 1;
+}
+
+/******************************************************************************
+ *
+ *     compute_action_time
+ *
+ *     Set the play clock to the time when the next input action should be put
+ *     into the server's input queue.  Fill the rtime structure with values
+ *     for the delta until the time for the next input action.
+ */
+static void
+compute_action_time(rtime)
+struct timeval *rtime;
+{
+       /*
+        * holds the delay time in milliseconds
+        */
+       unsigned long   dtime;
+       /*
+        * holds the number of microseconds in the sum of the dtime value
+        * and the play_time value
+        */
+       unsigned long   tot_usec;
+       /*
+        * holds the number of seconds and microseconds in the
+        * dtime value
+        */
+       unsigned long   sec;
+       unsigned long   usec;
+       /*
+        * holds the current time
+        */
+       struct timeval  btime;
+
+       /*
+        * Put the time from the current input action in dtime
+        */
+       dtime = action_array[read_index].delay_time;
+       /*
+        * If the current input action is a delay input action,
+        * add in the time from the following input action.
+        */
+       if ((action_array[read_index].type == XTestDELAY_ACTION) &&
+           ((read_index + 1) < write_index))
+       {  
+               read_index++;
+               dtime = dtime + action_array[read_index].delay_time;
+       }
+       /*
+        * compute the number of seconds and microseconds in the
+        * dtime value
+        */
+       sec = dtime / 1000;
+       usec = (dtime % 1000) * 1000;
+       /*
+        * get the current time in btime
+        */
+       X_GETTIMEOFDAY(&btime);
+       /*
+        * compute the number of microseconds in the sum of the dtime value
+        * and the current usec value
+        */
+       tot_usec = btime.tv_usec + usec;
+       /*
+        * if it is greater than one second's worth, adjust the seconds
+        */
+       if (tot_usec >= 1000000)
+       { 
+               tot_usec -= 1000000;
+               sec++;
+       }
+       play_time.tv_usec = tot_usec;
+       play_time.tv_sec = btime.tv_sec + sec;
+       /*
+        * put the time until the next input action in rtime
+        */
+       rtime->tv_sec = sec;
+       rtime->tv_usec = usec;
+}
+
+/******************************************************************************
+ *
+ *     find_residual_time
+ *
+ *     Find the time interval from the current time to the value in play_time.
+ *     This is the time to wait till putting the next input action into the
+ *     server's input queue.  If the time is already up, reset play_time to
+ *     the current time.
+ */
+static int
+find_residual_time(the_residual)
+struct timeval *the_residual;
+{
+       /*
+        * if > 0, there is time to wait.  If < 0, then don't wait
+        */
+       int             wait = 1;
+       /*
+        * holds the current time
+        */
+       struct timeval  btime;
+       /*
+        * holds the current time in seconds and microseconds
+        */
+       unsigned long   bsec;
+       unsigned long   busec;
+       /*
+        * holds the playback time in seconds and microseconds
+        */
+       unsigned long   psec;
+       unsigned long   pusec;
+
+       /*
+        * get the current time in btime
+        */
+       X_GETTIMEOFDAY(&btime);
+       /*
+        * get the current time in seconds and microseconds
+        */
+       bsec = btime.tv_sec;
+       busec = btime.tv_usec;
+       /*
+        * get the playback time in seconds and microseconds
+        */
+       psec = play_time.tv_sec;
+       pusec = play_time.tv_usec;
+       /*
+        * if the current time is already later than the playback time,
+        * we don't need to wait
+        */
+       if (bsec > psec)        
+       {
+           wait = -1;
+       }
+       else
+       { 
+               if (bsec == psec)
+               { 
+                       /*
+                        * if the current and playback times have the same
+                        * second value, then compare the microsecond values
+                        */
+                       if ( busec >= pusec) 
+                       { 
+                               /*
+                                * if the current time is already later than
+                                * the playback time, we don't need to wait
+                                */
+                               wait = -1;
+                       }
+                       else
+                       { 
+                               the_residual->tv_usec = pusec - busec;
+                               the_residual->tv_sec = 0;
+                       }
+               }
+               else    
+               { 
+                       if (busec > pusec)
+                       { 
+                               /*
+                                * 'borrow' a second's worth of microseconds
+                                * from the seconds left to wait
+                                */
+                               the_residual->tv_usec = 1000000 - busec + pusec;
+                               psec--;
+                               the_residual->tv_sec = psec - bsec;
+                       }
+                       else
+                       { 
+                               the_residual->tv_sec = psec - bsec;
+                               the_residual->tv_usec = pusec - busec;
+                       }
+               }
+       }
+       if (wait < 0)
+       { 
+               /*
+                * if don't need to wait, set the playback time
+                * to the current time
+                */
+               X_GETTIMEOFDAY(&play_time);
+               /*
+                * set the time to wait to 0
+                */
+               the_residual->tv_sec = 0;
+               the_residual->tv_usec = 0;
+       }
+       return(wait);
+}
+       
+/******************************************************************************
+ *
+ *     abort_play_back
+ */
+void
+abort_play_back()
+{
+       /*
+        * If we were playing back input actions at the time of the abort,
+        * restore the original wait time for the select in the main wait
+        * loop of the server
+        */
+       if (playback_on)
+       {
+               restorewait->tv_sec = saved_sec;
+               restorewait->tv_usec = saved_usec;
+       }
+       /*
+        * make the input action array empty
+        */
+       read_index = 0;
+       write_index = 0;
+       /*
+        * we are no longer playing back anything
+        */
+       playback_on = 0;
+       play_clock = 0;
+       go_for_next = 1;
+       /*
+        * there is no valid wait time saved any more
+        */
+       time_saved = 0;
+       /*
+        * there are no valid clients using this extension
+        */
+       playback_client = (ClientPtr) NULL;
+       current_xtest_client = (ClientPtr) NULL;
+}
+
+/******************************************************************************
+ *
+ *     return_input_array_size
+ *
+ *     Return the number of input actions in the input action array.
+ */
+void
+return_input_array_size(client)
+/*
+ * which client to send the reply to
+ */
+ClientPtr      client;
+{
+       xTestQueryInputSizeReply  rep;
+
+       rep.type = X_Reply;
+       /*
+        * set the serial number of the reply
+        */
+       rep.sequenceNumber = client->sequence;
+       rep.length = 0;
+       rep.size_return = ACTION_ARRAY_SIZE;
+       WriteReplyToClient(client,
+                          sizeof(xTestQueryInputSizeReply),
+                          (pointer) &rep);             
+}