1618 lines
39 KiB
C
1618 lines
39 KiB
C
/* $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);
|
|
}
|