diff options
Diffstat (limited to 'nx-X11/programs/Xserver/Xext/xtest1dd.c')
-rw-r--r-- | nx-X11/programs/Xserver/Xext/xtest1dd.c | 1617 |
1 files changed, 1617 insertions, 0 deletions
diff --git a/nx-X11/programs/Xserver/Xext/xtest1dd.c b/nx-X11/programs/Xserver/Xext/xtest1dd.c new file mode 100644 index 000000000..81d283c36 --- /dev/null +++ b/nx-X11/programs/Xserver/Xext/xtest1dd.c @@ -0,0 +1,1617 @@ +/* $Xorg: xtest1dd.c,v 1.4 2001/02/09 02:04:33 xorgcvs Exp $ */ +/* + * File: xtest1dd.c + * + * This file contains the device dependent parts of the input + * synthesis extension. + */ + +/* + + +Copyright 1986, 1987, 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +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 +OPEN GROUP 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 Open Group 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 Open Group. + + +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. + +*/ +/* $XFree86: xc/programs/Xserver/Xext/xtest1dd.c,v 3.6 2003/10/28 23:08:44 tsi Exp $ */ + +/*************************************************************** + * include files + ***************************************************************/ + +#define NEED_EVENTS +#define NEED_REPLIES + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <stdio.h> +#include <X11/Xos.h> +#include <X11/X.h> +#include <X11/Xmd.h> +#include <X11/Xproto.h> +#include "misc.h" +#include "dixstruct.h" +#define XTestSERVER_SIDE +#include <X11/extensions/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; +/* + * 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 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( + XTestKeyInfo * /* fkey */ + ); +static void parse_motion_fake( + XTestMotionInfo * /* fmotion */ + ); +static void parse_jump_fake( + XTestJumpInfo * /* fjump */ + ); +static void parse_delay_fake( + XTestDelayInfo * /* tevent */ + ); +static void send_ack( + ClientPtr /* client */ + ); +static void start_play_clock( + void + ); +static void compute_action_time( + struct timeval * /* rtime */ + ); +static int find_residual_time( + struct timeval * /* rtime */ + ); + +static CARD16 check_time_event( + void + ); +static CARD32 current_ms( + struct timeval * /* otime */ + ); +static int there_is_room( + int /* actsize */ + ); + +/****************************************************************************** + * + * 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(¤t_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_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 + */ +int jx; +int 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(¤t_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) + { + 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 + */ +int dx; +int dy; +/* + * which locator did the moving + */ +int dev_type; +/* + * the x and y position of the locator before the delta motion + */ +int mx; +int 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 + */ +unsigned keycode; +/* + * whether the key/button was pressed or released + */ +int keystate; +/* + * which device caused the input action + */ +int dev_type; +/* + * the x and y coordinates of the locator when the action happenned + */ +int locx; +int locy; +{ + /* + * pointer to key/button motion input action + */ + XTestKeyInfo *kp; + /* + * time delta from previous event + */ + CARD16 tchar; + char keytrans = 0; + + /* + * 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); + } + 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); +} |