1614 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1614 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.7 2003/11/17 22:20:28 dawes Exp $ */
 | |
| 
 | |
| /***************************************************************
 | |
|  * 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;
 | |
| /*
 | |
|  * 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);		
 | |
| }		
 |