2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* txCommands.c --
|
|
|
|
|
*
|
|
|
|
|
* Reads commands from devices and sends them to the window package,
|
2020-05-23 23:13:14 +02:00
|
|
|
* which sends them to a particular window.
|
|
|
|
|
*
|
|
|
|
|
* *********************************************************************
|
|
|
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
|
|
|
* * 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. The University of California *
|
|
|
|
|
* * makes no representations about the suitability of this *
|
|
|
|
|
* * software for any purpose. It is provided "as is" without *
|
|
|
|
|
* * express or implied warranty. Export of this software outside *
|
|
|
|
|
* * of the United States of America may require an export license. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) ="$Header: /usr/cvsroot/magic-8.0/textio/txCommands.c,v 1.6 2009/09/11 16:09:49 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <signal.h>
|
2024-10-12 14:57:08 +02:00
|
|
|
#ifdef TIME_WITH_SYS_TIME
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# include <time.h>
|
|
|
|
|
#else
|
|
|
|
|
# ifdef HAVE_SYS_TIME_H
|
|
|
|
|
# include <sys/time.h>
|
|
|
|
|
# else
|
|
|
|
|
# include <time.h>
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2018-06-17 21:12:00 +02:00
|
|
|
#include "tcltk/tclmagic.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "utils/magsgtty.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "textio/txcommands.h"
|
|
|
|
|
#include "textio/textioInt.h"
|
|
|
|
|
#include "utils/macros.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "drc/drc.h"
|
|
|
|
|
#include "utils/signals.h"
|
|
|
|
|
#include "graphics/graphics.h"
|
|
|
|
|
#include "utils/dqueue.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "utils/utils.h"
|
2024-10-16 19:24:13 +02:00
|
|
|
#ifdef SCHEME_INTERPRETER
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "lisp/lisp.h"
|
2024-10-16 19:24:13 +02:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "windows/windows.h"
|
2022-10-28 07:50:29 +02:00
|
|
|
#include "utils/main.h"
|
2022-10-10 11:50:15 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Turning this flag on prints out input events and commands as they
|
|
|
|
|
* are processed.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool TxDebug = FALSE;
|
|
|
|
|
|
|
|
|
|
/* A mask of the file descriptors for all input devices.
|
|
|
|
|
*/
|
|
|
|
|
static fd_set txInputDescriptors;
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
/* Used for the 'nfds' field for select() syscall, the highest
|
|
|
|
|
* fd number that is set in the txInputDescriptors bitmask.
|
|
|
|
|
* Don't forget to plus 1 for select().
|
|
|
|
|
*/
|
|
|
|
|
static int txInputDescriptors_nfds;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
#define TX_MAX_INPUT_DEVICES 20
|
2017-04-25 14:41:48 +02:00
|
|
|
/* A table of all input devices.
|
|
|
|
|
*/
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
static txInputDevRec txInputDevice[TX_MAX_INPUT_DEVICES];
|
|
|
|
|
static int txLastInputEntry;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* The current point -- reset by the 'setpoint' command and for each
|
|
|
|
|
* interactive command. Calls to TxClearPoint clear previous setpoints,
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Each input point is associated with a window, as windows may use
|
2017-04-25 14:41:48 +02:00
|
|
|
* different coordinate systems.
|
|
|
|
|
*
|
|
|
|
|
* Also, keep around the last input event.
|
|
|
|
|
*/
|
|
|
|
|
static bool txHaveCurrentPoint = FALSE;
|
|
|
|
|
static Point txCurrentPoint = {100, 100};
|
|
|
|
|
static int txCurrentWindowID = WIND_UNKNOWN_WINDOW;
|
|
|
|
|
TxInputEvent txLastEvent;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Input queues. We have an input queue for low-level input events, and
|
2020-05-23 23:13:14 +02:00
|
|
|
* a queue for assembled interactive commands and file commands. Also, there
|
2017-04-25 14:41:48 +02:00
|
|
|
* are free lists for these structures.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
extern void send_interpreter(char *);
|
|
|
|
|
|
|
|
|
|
char *TxBuffer; /* For use with special stdin processing */
|
|
|
|
|
unsigned char TxInputRedirect; /* For redirecting Tk events to the terminal */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
DQueue txInputEvents;
|
|
|
|
|
DQueue txFreeEvents;
|
|
|
|
|
int txNumInputEvents; /* Number of events recieved by Magic so far. */
|
|
|
|
|
|
|
|
|
|
DQueue txFreeCommands;
|
|
|
|
|
|
|
|
|
|
/* A zero time value, used for doing a poll via 'select()'.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static struct timeval txZeroTime;
|
|
|
|
|
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/* Mask of buttons down, as of the last command in the queue (not the last
|
2017-04-25 14:41:48 +02:00
|
|
|
* command executed).
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
int TxCurButtons = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Commands are numbered sequentially starting at zero. This number says
|
|
|
|
|
* which command we are collecting or executing.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int TxCommandNumber = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The "cmd" structure is shared by lisp and eval using this pointer
|
|
|
|
|
*/
|
|
|
|
|
static TxCommand *lisp_cur_cmd = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* FD_IsZero --
|
|
|
|
|
* FD_OrSet --
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
* FD_MaxFd --
|
|
|
|
|
* Returns the highest 'fd' in the mask for select(2) nfds argument, or
|
|
|
|
|
* -1 when 'fdmask' is empty.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Routines for manipulating set of file descriptors.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2025-01-31 18:13:02 +01:00
|
|
|
FD_IsZero(
|
|
|
|
|
const fd_set *fdmask)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
int fd;
|
|
|
|
|
for (fd = 0; fd < FD_SETSIZE; fd++)
|
|
|
|
|
if (FD_ISSET(fd, fdmask)) return FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
FD_OrSet(
|
|
|
|
|
const fd_set *fdmask,
|
|
|
|
|
fd_set *dst)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
int fd;
|
|
|
|
|
for (fd = 0; fd < FD_SETSIZE; fd++)
|
|
|
|
|
if (FD_ISSET(fd, fdmask)) FD_SET(fd, dst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A bitmask find max bit set operation */
|
|
|
|
|
int
|
|
|
|
|
FD_MaxFd(fdmask)
|
|
|
|
|
const fd_set *fdmask;
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
for (fd = FD_SETSIZE-1; fd >= 0; fd--) /* backwards */
|
|
|
|
|
if (FD_ISSET(fd, fdmask)) return fd;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* update txInputDescriptors_nfds with the correct value
|
|
|
|
|
* call this everytime txInputDescriptors is modified
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
TxInputDescriptorsRecalc(void)
|
|
|
|
|
{
|
|
|
|
|
int nfds = FD_MaxFd(&txInputDescriptors);
|
|
|
|
|
txInputDescriptors_nfds = (nfds >= 0) ? nfds : 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxReleaseButton --
|
|
|
|
|
*
|
|
|
|
|
* Pretend that a certain button is up, even though we think that it is
|
|
|
|
|
* down. Used only in rare circumstances, such as when SunWindows
|
|
|
|
|
* reads a button push behind our back.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxReleaseButton(
|
|
|
|
|
int but)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxCurButtons &= ~but;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxPrintEvent --
|
|
|
|
|
*
|
|
|
|
|
* Print an event's contents to stderr.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Text appears on the terminal.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxPrintEvent(
|
|
|
|
|
TxInputEvent *event)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-01-06 17:25:38 +01:00
|
|
|
TxError("Input event at %p\n ", (void *) event);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (event->txe_button == TX_EOF) {
|
|
|
|
|
TxError("EOF event");
|
|
|
|
|
} else if (event->txe_button == TX_BYPASS) {
|
|
|
|
|
TxError("Bypass event");
|
|
|
|
|
} else if (event->txe_button == TX_CHARACTER) {
|
|
|
|
|
char *strc = MacroName(event->txe_ch);
|
|
|
|
|
TxError("Character '%s'", strc);
|
|
|
|
|
freeMagic(strc);
|
|
|
|
|
} else {
|
|
|
|
|
switch (event->txe_button) {
|
|
|
|
|
case TX_LEFT_BUTTON: {TxError("Left button"); break;};
|
|
|
|
|
case TX_MIDDLE_BUTTON: {TxError("Middle button"); break;};
|
|
|
|
|
case TX_RIGHT_BUTTON: {TxError("Right button"); break;};
|
|
|
|
|
default: {TxError("UNKNOWN button"); break;};
|
|
|
|
|
}
|
|
|
|
|
switch (event->txe_buttonAction) {
|
|
|
|
|
case TX_BUTTON_UP: {TxError(" up"); break;};
|
|
|
|
|
case TX_BUTTON_DOWN: {TxError(" down"); break;};
|
|
|
|
|
default: {TxError(" UNKNOWN-ACTION"); break;};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TxError(" at (%d, %d)\n Window: ", event->txe_p.p_x, event->txe_p.p_y);
|
|
|
|
|
switch (event->txe_wid) {
|
|
|
|
|
case WIND_UNKNOWN_WINDOW: {TxError("unknown\n"); break;};
|
|
|
|
|
case WIND_NO_WINDOW: {TxError("none\n"); break;};
|
|
|
|
|
default: {TxError("%d\n", event->txe_wid); break;};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxPrintCommand --
|
|
|
|
|
*
|
|
|
|
|
* Print a command's contents to stderr.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Text appears on the terminal.
|
|
|
|
|
* Repaired so that unprintable text does NOT appear (March 2000, Tim)
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxPrintCommand(
|
|
|
|
|
TxCommand *cmd)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int i, j;
|
|
|
|
|
char TxTemp[200];
|
|
|
|
|
|
2025-01-06 17:25:38 +01:00
|
|
|
TxError("Command at %p\n ", (void *) cmd);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (cmd->tx_button == TX_CHARACTER) {
|
|
|
|
|
TxError("Text command with %d words: ", cmd->tx_argc);
|
|
|
|
|
for (i = 0; i < cmd->tx_argc; i++) {
|
|
|
|
|
for (j = 0; cmd->tx_argv[i][j] && (j < 199); j++)
|
|
|
|
|
TxTemp[j] = isprint(cmd->tx_argv[i][j]) ? cmd->tx_argv[i][j] : '*';
|
|
|
|
|
TxTemp[j] = '\0';
|
|
|
|
|
TxError(" \"%s\"", TxTemp);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
switch (cmd->tx_button) {
|
|
|
|
|
case TX_LEFT_BUTTON: {TxError("Left button"); break;};
|
|
|
|
|
case TX_MIDDLE_BUTTON: {TxError("Middle button"); break;};
|
|
|
|
|
case TX_RIGHT_BUTTON: {TxError("Right button"); break;};
|
|
|
|
|
default: {TxError("UNKNOWN button"); break;};
|
|
|
|
|
}
|
|
|
|
|
switch (cmd->tx_buttonAction) {
|
|
|
|
|
case TX_BUTTON_UP: {TxError(" up"); break;};
|
|
|
|
|
case TX_BUTTON_DOWN: {TxError(" down"); break;};
|
|
|
|
|
default: {TxError(" UNKNOWN-ACTION"); break;};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TxError(" at (%d, %d)\n Window: ", cmd->tx_p.p_x, cmd->tx_p.p_y);
|
|
|
|
|
switch (cmd->tx_wid) {
|
|
|
|
|
case WIND_UNKNOWN_WINDOW: {TxError("unknown\n"); break;};
|
|
|
|
|
case WIND_NO_WINDOW: {TxError("none\n"); break;};
|
|
|
|
|
default: {TxError("%d\n", cmd->tx_wid); break;};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxNewEvent --
|
|
|
|
|
*
|
|
|
|
|
* Get a new event, ready to be filled in.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* A pointer to a new event.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
TxInputEvent *
|
2025-01-31 18:13:02 +01:00
|
|
|
TxNewEvent(void)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxInputEvent *event;
|
|
|
|
|
event = (TxInputEvent *) DQPopFront(&txFreeEvents);
|
|
|
|
|
if (event == NULL) event = (TxInputEvent *) mallocMagic(sizeof(TxInputEvent));
|
|
|
|
|
event->txe_button = TX_CHARACTER;
|
|
|
|
|
event->txe_buttonAction = TX_BUTTON_UP;
|
|
|
|
|
event->txe_wid = WIND_UNKNOWN_WINDOW;
|
|
|
|
|
event->txe_p.p_x = GR_CURSOR_X;
|
|
|
|
|
event->txe_p.p_y = GR_CURSOR_Y;
|
|
|
|
|
event->txe_ch = 0;
|
|
|
|
|
return event;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxAddEvent --
|
|
|
|
|
*
|
|
|
|
|
* Add a new event into our input queue.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxAddEvent(
|
|
|
|
|
TxInputEvent *event)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
ASSERT(event != NULL, "TxAddEvent");
|
|
|
|
|
DQPushRear(&txInputEvents, (ClientData) event);
|
|
|
|
|
txNumInputEvents++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxFreeEvent --
|
|
|
|
|
*
|
|
|
|
|
* Free an event.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxFreeEvent(
|
|
|
|
|
TxInputEvent *event)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
ASSERT(event != NULL, "TxFreeEvent");
|
|
|
|
|
DQPushRear(&txFreeEvents, (ClientData) event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxNewCommand --
|
|
|
|
|
*
|
|
|
|
|
* Get a new command, ready to be filled in.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* A pointer to a new command.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
TxCommand *
|
2025-01-31 18:13:02 +01:00
|
|
|
TxNewCommand(void)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxCommand *command;
|
|
|
|
|
command = (TxCommand *) DQPopFront(&txFreeCommands);
|
2020-05-23 23:13:14 +02:00
|
|
|
if (command == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
command = (TxCommand *) mallocMagic(sizeof(TxCommand));
|
|
|
|
|
command->tx_button = TX_CHARACTER;
|
|
|
|
|
return command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxFreeCommand --
|
|
|
|
|
*
|
|
|
|
|
* Free a command.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxFreeCommand(
|
|
|
|
|
TxCommand *command)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
ASSERT(command != NULL, "TxFreeCommand");
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
/* No Tx Queue in MAGIC_WRAPPER; call is just to free memory */
|
|
|
|
|
freeMagic((char *)command);
|
|
|
|
|
#else
|
|
|
|
|
DQPushRear(&txFreeCommands, (ClientData) command);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxAddInputDevice --
|
|
|
|
|
* TxAdd1InputDevice --
|
|
|
|
|
*
|
|
|
|
|
* Add a device which is able to deliever commands to magic.
|
|
|
|
|
* The caller must ensure that SIGIO signals will be send whenever
|
|
|
|
|
* this file descriptor becomes ready.
|
|
|
|
|
*
|
|
|
|
|
* The usual form of inputProc is:
|
|
|
|
|
*
|
|
|
|
|
* proc(fd, cdata)
|
|
|
|
|
* int fd; -- file descriptor that is ready
|
|
|
|
|
* ClientData; -- ClientData as passed to TxAddInputDevice()
|
|
|
|
|
* {
|
|
|
|
|
* TxInputEvent *event;
|
|
|
|
|
* event = TxNewEvent();
|
|
|
|
|
* -- read fd here, and fill in event
|
|
|
|
|
* TxAddEvent(event);
|
|
|
|
|
* -- might do this more than once for multiple events
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Modifies internal tables.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxAddInputDevice(
|
|
|
|
|
const fd_set *fdmask, /* A mask of file descriptors that this
|
2017-04-25 14:41:48 +02:00
|
|
|
* device will handle.
|
|
|
|
|
*/
|
2025-01-31 18:29:41 +01:00
|
|
|
const cb_textio_input_t inputProc,
|
|
|
|
|
/* A routine to call. This routine will
|
2017-04-25 14:41:48 +02:00
|
|
|
* be passed a single file descriptor that
|
|
|
|
|
* is ready, and should read that file and
|
2020-05-23 23:13:14 +02:00
|
|
|
* add events(s) by calling TxNewEvent()
|
2017-04-25 14:41:48 +02:00
|
|
|
* followed by TxAddEvent().
|
|
|
|
|
*/
|
2025-01-31 18:13:02 +01:00
|
|
|
ClientData cdata) /* Will be passed back to the proc whenever
|
2017-04-25 14:41:48 +02:00
|
|
|
* it is called.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
TxDeleteInputDevice(fdmask);
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
if (txLastInputEntry == TX_MAX_INPUT_DEVICES)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxError("Too many input devices.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
|
2025-02-13 09:25:02 +01:00
|
|
|
txInputDevice[txLastInputEntry].tx_fdmask = *fdmask;
|
2017-04-25 14:41:48 +02:00
|
|
|
txInputDevice[txLastInputEntry].tx_inputProc = inputProc;
|
|
|
|
|
txInputDevice[txLastInputEntry].tx_cdata = cdata;
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
txLastInputEntry++;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
FD_OrSet(fdmask, &txInputDescriptors);
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
TxInputDescriptorsRecalc();
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxAdd1InputDevice(
|
|
|
|
|
int fd,
|
2025-01-31 18:29:41 +01:00
|
|
|
const cb_textio_input_t inputProc,
|
2025-01-31 18:13:02 +01:00
|
|
|
ClientData cdata)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
fd_set fs;
|
2025-02-24 09:45:23 +01:00
|
|
|
ASSERT(fd >= 0 && fd < FD_SETSIZE, "fd>=0&&fd<FD_SETSIZE");
|
|
|
|
|
if (fd < 0 || fd >= FD_SETSIZE)
|
|
|
|
|
{
|
|
|
|
|
TxError("WARNING: TxAdd1InputDevice(fd=%d) called with fd out of range 0..%d\n", fd, FD_SETSIZE-1);
|
|
|
|
|
return; /* allowing things to continue is UB */
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
FD_ZERO(&fs);
|
|
|
|
|
FD_SET(fd, &fs);
|
2025-02-13 09:25:02 +01:00
|
|
|
TxAddInputDevice(&fs, inputProc, cdata);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxDeleteInputDevice --
|
|
|
|
|
*
|
|
|
|
|
* Delete an input device from our tables.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* modifies internal tables.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxDeleteInputDevice(
|
|
|
|
|
const fd_set *fdmask) /* A mask of file descriptors that are
|
2017-04-25 14:41:48 +02:00
|
|
|
* no longer active.
|
|
|
|
|
*/
|
|
|
|
|
{
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
int fd;
|
|
|
|
|
for (fd = 0; fd < FD_SETSIZE; fd++)
|
|
|
|
|
if (FD_ISSET(fd, fdmask)) TxDelete1InputDevice(fd);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxDelete1InputDevice(
|
|
|
|
|
int fd)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-24 09:45:23 +01:00
|
|
|
ASSERT(fd >= 0 && fd < FD_SETSIZE, "fd>=0&&fd<FD_SETSIZE");
|
|
|
|
|
if (fd < 0 || fd >= FD_SETSIZE)
|
|
|
|
|
{
|
|
|
|
|
TxError("WARNING: TxDelete1InputDevice(fd=%d) called with fd out of range 0..%d\n", fd, FD_SETSIZE-1);
|
|
|
|
|
return; /* allowing things to continue is UB */
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
restart:
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < txLastInputEntry; i++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
FD_CLR(fd, &txInputDevice[i].tx_fdmask);
|
|
|
|
|
if (FD_IsZero(&txInputDevice[i].tx_fdmask))
|
|
|
|
|
{
|
|
|
|
|
int j;
|
|
|
|
|
for (j = i+1; j < txLastInputEntry; j++)
|
|
|
|
|
txInputDevice[j-1] = txInputDevice[j];
|
|
|
|
|
txLastInputEntry--;
|
|
|
|
|
/* we shuffled entries down one, so txInputDevice[i] now
|
|
|
|
|
* looks at potential next one to inspect, but we're about
|
|
|
|
|
* to i++ (if we continue) and that will incorrectly skip
|
|
|
|
|
* inspection of it.
|
|
|
|
|
* lets just start over
|
|
|
|
|
*/
|
|
|
|
|
goto restart;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FD_CLR(fd, &txInputDescriptors);
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
TxInputDescriptorsRecalc();
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxSetPoint --
|
|
|
|
|
*
|
|
|
|
|
* Set the point and window that will be used for the next command.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Changes global set point variables.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxSetPoint(
|
|
|
|
|
int x,
|
|
|
|
|
int y,
|
|
|
|
|
int wid)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
txHaveCurrentPoint = TRUE;
|
|
|
|
|
txCurrentPoint.p_x = x;
|
|
|
|
|
txCurrentPoint.p_y = y;
|
|
|
|
|
txCurrentWindowID = wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxGetPoint --
|
|
|
|
|
*
|
|
|
|
|
* Return the window ID and set the point (if non-NULL) set for the
|
|
|
|
|
* next command.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* integer ID of the current window, or -1 if txHaveCurrentPoint is
|
|
|
|
|
* TX_POINT_CLEAR
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Current point goes into
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-31 18:13:02 +01:00
|
|
|
TxGetPoint(
|
|
|
|
|
Point *tx_p)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (txHaveCurrentPoint)
|
|
|
|
|
{
|
|
|
|
|
if (tx_p != (Point *)NULL)
|
|
|
|
|
*tx_p = txCurrentPoint;
|
|
|
|
|
return txCurrentWindowID;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxClearPoint --
|
|
|
|
|
*
|
|
|
|
|
* Clear the point that will be used for the next command, forcing it to
|
|
|
|
|
* be read from the mouse or button push.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Nullifies the effect of any previous TxSetPoint() call.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxClearPoint(void)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
txHaveCurrentPoint = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static FILE *txLogFile = NULL;
|
2023-06-29 03:31:24 +02:00
|
|
|
unsigned char txLogFlags;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
2023-06-29 03:31:24 +02:00
|
|
|
* TxLogStart --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Log all further commands to the given file name. If the file is NULL,
|
|
|
|
|
* turn off logging.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* File IO.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxLogStart(
|
2025-01-31 18:15:29 +01:00
|
|
|
const char *fileName,
|
2025-01-31 18:13:02 +01:00
|
|
|
MagWindow *mw) /* Window commands are logged from */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
if (txLogFile != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-09-30 04:48:52 +02:00
|
|
|
TxError("There is already a log file open!\n");
|
2023-06-29 03:31:24 +02:00
|
|
|
return;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-29 03:31:24 +02:00
|
|
|
txLogFlags = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
txLogFile = fopen(fileName, "w");
|
2020-05-23 23:13:14 +02:00
|
|
|
if (txLogFile == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("Could not open file '%s' for writing.\n", fileName);
|
2023-06-29 03:31:24 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
time_t t_stamp = time((time_t *)NULL);
|
|
|
|
|
struct tm *clock = localtime(&t_stamp);
|
|
|
|
|
char *now = ctime(&t_stamp);
|
|
|
|
|
|
|
|
|
|
TxPrintf("Logging commands to file \"%s\"\n", fileName);
|
|
|
|
|
/* Write comment line header into command file and log the current
|
|
|
|
|
* window view position so that cursor positions in the file match
|
|
|
|
|
* the layout. If the cursor box is defined, then issue a "box
|
|
|
|
|
* values" command so that relative box adjustments are correct.
|
|
|
|
|
*/
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
fprintf(txLogFile, "# Magic command log file\n");
|
|
|
|
|
fprintf(txLogFile, "# Using technology: %s\n", DBTechName);
|
|
|
|
|
if (mw != NULL)
|
|
|
|
|
fprintf(txLogFile, "# Title: %s\n", mw->w_caption);
|
|
|
|
|
fprintf(txLogFile, "# Date: %s", now);
|
|
|
|
|
#endif
|
|
|
|
|
if (mw != NULL)
|
|
|
|
|
{
|
|
|
|
|
CellDef *rootBoxDef;
|
|
|
|
|
Rect rootBox;
|
|
|
|
|
|
|
|
|
|
#ifndef MAGIC_WRAPPER
|
|
|
|
|
// Colon preceeds commands in the non-Tcl verson of the log file.
|
|
|
|
|
fprintf(txLogFile, ":");
|
|
|
|
|
#endif
|
|
|
|
|
fprintf(txLogFile, "view %di %di %di %di\n",
|
|
|
|
|
mw->w_surfaceArea.r_xbot, mw->w_surfaceArea.r_ybot,
|
|
|
|
|
mw->w_surfaceArea.r_xtop, mw->w_surfaceArea.r_ytop);
|
|
|
|
|
if (ToolGetBox(&rootBoxDef, &rootBox))
|
|
|
|
|
{
|
|
|
|
|
fprintf(txLogFile, "box values %di %di %di %di\n",
|
|
|
|
|
rootBox.r_xbot, rootBox.r_ybot,
|
|
|
|
|
rootBox.r_xtop, rootBox.r_ytop);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxLogStop --
|
|
|
|
|
*
|
|
|
|
|
* Turn off logging.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* File IO.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxLogStop(void)
|
2023-06-29 03:31:24 +02:00
|
|
|
{
|
|
|
|
|
if (txLogFile != NULL)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Ending command logging to file.\n");
|
|
|
|
|
fclose(txLogFile);
|
|
|
|
|
txLogFile = NULL;
|
|
|
|
|
txLogFlags = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxLogUpdate --
|
|
|
|
|
*
|
|
|
|
|
* Set the log file flags to force a display refresh after logged commands
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* File IO.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxLogUpdate(void)
|
2023-06-29 03:31:24 +02:00
|
|
|
{
|
|
|
|
|
if (txLogFile == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("There is no log file to set an update flag on.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (txLogFlags & TX_LOG_UPDATE)
|
|
|
|
|
{
|
|
|
|
|
txLogFlags &= ~TX_LOG_UPDATE;
|
|
|
|
|
TxPrintf("No display refresh after logged commands.\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
txLogFlags |= TX_LOG_UPDATE;
|
|
|
|
|
TxPrintf("Forcing display refresh after logged commands.\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxLogSuspend --
|
|
|
|
|
*
|
|
|
|
|
* Suspend command logging
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* File IO.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxLogSuspend(void)
|
2023-06-29 03:31:24 +02:00
|
|
|
{
|
|
|
|
|
if (txLogFile == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
txLogFlags |= TX_LOG_SUSPEND;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxLogResume --
|
|
|
|
|
*
|
|
|
|
|
* Resume command logging
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* File IO.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxLogResume(void)
|
2023-06-29 03:31:24 +02:00
|
|
|
{
|
|
|
|
|
if (txLogFile == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
txLogFlags &= ~TX_LOG_SUSPEND;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* txLogCommand --
|
|
|
|
|
*
|
|
|
|
|
* Log a command in the log file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
txLogCommand(
|
|
|
|
|
TxCommand *cmd)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-01-31 18:18:27 +01:00
|
|
|
static const char *txButTable[] =
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
"left",
|
|
|
|
|
"middle",
|
|
|
|
|
"right",
|
|
|
|
|
0
|
|
|
|
|
};
|
2025-01-31 18:18:27 +01:00
|
|
|
static const char *txActTable[] =
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
"down",
|
|
|
|
|
"up",
|
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
|
2023-06-29 03:31:24 +02:00
|
|
|
#ifdef MAGIC_WRAPPER
|
2025-01-31 18:18:27 +01:00
|
|
|
const char *pfix = "";
|
2023-06-29 03:31:24 +02:00
|
|
|
#else
|
2025-01-31 18:18:27 +01:00
|
|
|
const char *pfix = ":";
|
2023-06-29 03:31:24 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Do not do anything if there is no log file */
|
2017-04-25 14:41:48 +02:00
|
|
|
if (txLogFile == (FILE *) NULL) return;
|
|
|
|
|
|
2023-06-29 03:31:24 +02:00
|
|
|
/* Do not write anything if the log file is suspended */
|
|
|
|
|
if (txLogFlags & TX_LOG_SUSPEND) return;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (cmd->tx_argc > 0)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2023-06-29 03:31:24 +02:00
|
|
|
char *postns;
|
|
|
|
|
|
|
|
|
|
/* Do not output "logcommand" commands to the log file.
|
|
|
|
|
* Do not output "*bypass" commands to the log file.
|
2025-02-22 23:06:06 +01:00
|
|
|
* Do not output "setpoint" commands to the log file.
|
|
|
|
|
* Do not output "wire show" commands to the log file (excessive output).
|
2023-06-29 03:31:24 +02:00
|
|
|
*/
|
|
|
|
|
postns = strstr(cmd->tx_argv[0], "::");
|
|
|
|
|
if (postns == NULL)
|
|
|
|
|
postns = cmd->tx_argv[0];
|
|
|
|
|
else
|
|
|
|
|
postns += 2;
|
|
|
|
|
|
|
|
|
|
if (!strncmp(postns, "logc", 4))
|
|
|
|
|
return;
|
|
|
|
|
else if (!strcmp(postns, "*bypass"))
|
|
|
|
|
return;
|
2025-02-22 23:06:06 +01:00
|
|
|
else if (!strcmp(postns, "setpoint"))
|
|
|
|
|
return;
|
|
|
|
|
else if (!strcmp(postns, "wire") && !strcmp(cmd->tx_argv[1], "show"))
|
|
|
|
|
return;
|
2023-06-29 03:31:24 +02:00
|
|
|
|
|
|
|
|
fprintf(txLogFile, "%s%s", pfix, cmd->tx_argv[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
for (i = 1; i < cmd->tx_argc; i++)
|
|
|
|
|
{
|
2023-07-08 23:34:00 +02:00
|
|
|
bool needQuotes = (strchr(cmd->tx_argv[i], ' ') == NULL) ? FALSE : TRUE;
|
|
|
|
|
fprintf(txLogFile, " ");
|
|
|
|
|
if (needQuotes) fprintf(txLogFile, "\"");
|
|
|
|
|
fprintf(txLogFile, "%s", cmd->tx_argv[i]);
|
|
|
|
|
if (needQuotes) fprintf(txLogFile, "\"");
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
fprintf(txLogFile, "\n");
|
|
|
|
|
}
|
|
|
|
|
else if (cmd->tx_button == TX_CHARACTER) {
|
|
|
|
|
/* its a no-op command */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
int but, act;
|
|
|
|
|
|
|
|
|
|
switch(cmd->tx_button) {
|
|
|
|
|
case TX_LEFT_BUTTON: { but = 0; break; };
|
|
|
|
|
case TX_MIDDLE_BUTTON: { but = 1; break; };
|
|
|
|
|
case TX_RIGHT_BUTTON: { but = 2; break; };
|
2024-10-04 20:45:52 +02:00
|
|
|
default: {ASSERT(FALSE, "txLogCommand"); but = -1; break; };
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
switch(cmd->tx_buttonAction) {
|
|
|
|
|
case TX_BUTTON_DOWN: { act = 0; break; };
|
|
|
|
|
case TX_BUTTON_UP: { act = 1; break; };
|
2024-10-04 20:45:52 +02:00
|
|
|
default: {ASSERT(FALSE, "txLogCommand"); act = -1; break; };
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2024-10-04 20:45:52 +02:00
|
|
|
if (but >= 0 && act >= 0)
|
|
|
|
|
fprintf(txLogFile, "%spushbutton %s %s\n",
|
2023-06-29 03:31:24 +02:00
|
|
|
pfix, txButTable[but], txActTable[act]);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2023-06-29 03:31:24 +02:00
|
|
|
if (txLogFlags & TX_LOG_UPDATE)
|
|
|
|
|
fprintf(txLogFile, "%supdatedisplay\n", pfix);
|
|
|
|
|
|
|
|
|
|
fflush(txLogFile);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxGetInputEvent --
|
|
|
|
|
*
|
|
|
|
|
* Get some events and put them into the event queue. If returnOnSigWinch
|
|
|
|
|
* is on, we will return early if some event (such as a SigWinch) requires
|
|
|
|
|
* immediate attention. In that case, the input event queue may be empty
|
|
|
|
|
* even if block is TRUE.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* "TRUE" if it got some input.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Some IO may be done, and things may appear in the event queue.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2025-01-31 18:13:02 +01:00
|
|
|
TxGetInputEvent(
|
|
|
|
|
bool block, /* If TRUE, we will wait for an event. Otherwise, we
|
2017-04-25 14:41:48 +02:00
|
|
|
* just poll.
|
|
|
|
|
*/
|
2025-01-31 18:13:02 +01:00
|
|
|
bool returnOnSigWinch)
|
2017-04-25 14:41:48 +02:00
|
|
|
/* If we get a Sig-Winch signal, should we abondon
|
|
|
|
|
* our quest to read an input event and return
|
|
|
|
|
* immediately instead?
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
struct timeval *waitTime;
|
|
|
|
|
fd_set inputs;
|
|
|
|
|
int numReady;
|
|
|
|
|
bool gotSome;
|
|
|
|
|
int i, fd, lastNum;
|
|
|
|
|
|
2025-02-13 09:25:02 +01:00
|
|
|
ASSERT(!FD_IsZero(&txInputDescriptors), "TxGetInputEvent");
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (block)
|
|
|
|
|
waitTime = NULL;
|
|
|
|
|
else
|
|
|
|
|
waitTime = &txZeroTime;
|
|
|
|
|
|
|
|
|
|
gotSome = FALSE;
|
|
|
|
|
do {
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (returnOnSigWinch && SigGotSigWinch) return gotSome;
|
|
|
|
|
inputs = txInputDescriptors;
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
numReady = select(txInputDescriptors_nfds + 1, &inputs, (fd_set *)NULL,
|
2017-04-25 14:41:48 +02:00
|
|
|
(fd_set *)NULL, waitTime);
|
|
|
|
|
if (numReady <= 0) FD_ZERO(&inputs); /* no fd is ready */
|
|
|
|
|
} while ((numReady <= 0) && (errno == EINTR));
|
|
|
|
|
|
|
|
|
|
if ((numReady < 0) && (errno != EINTR))
|
|
|
|
|
{
|
|
|
|
|
perror("magic");
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-24 16:24:59 +01:00
|
|
|
/* 0..1023 using numReady==0 to terminate early */
|
|
|
|
|
for (fd = 0; numReady && fd <= txInputDescriptors_nfds; fd++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-24 16:24:59 +01:00
|
|
|
if (!FD_ISSET(fd, &inputs))
|
|
|
|
|
continue; /* this fd is was not monitored or is not ready */
|
|
|
|
|
|
|
|
|
|
/* find the input device receiver entry */
|
|
|
|
|
for (i = 0; i < txLastInputEntry; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!FD_ISSET(fd, &txInputDevice[i].tx_fdmask))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* This device has data on its file descriptor, call
|
|
|
|
|
* it so that it can add events to the input queue.
|
|
|
|
|
*/
|
|
|
|
|
lastNum = txNumInputEvents;
|
|
|
|
|
|
|
|
|
|
(*txInputDevice[i].tx_inputProc)(fd, txInputDevice[i].tx_cdata);
|
|
|
|
|
|
|
|
|
|
/* Did this driver choose to add an event? */
|
|
|
|
|
if (txNumInputEvents != lastNum) gotSome = TRUE;
|
|
|
|
|
numReady--;
|
|
|
|
|
|
|
|
|
|
/* original code would FD_CLR() which would effectively break here
|
|
|
|
|
* so this only allows the first found receiver to receive the data
|
|
|
|
|
*/
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* At this point we have handled all the bits in 'inputs' -- almost.
|
2020-05-23 23:13:14 +02:00
|
|
|
* It is possible for an input handler to remove or add other handlers
|
2017-04-25 14:41:48 +02:00
|
|
|
* via calls to TxDeleteDevice or TxAddDevice. Therefore, there may be
|
|
|
|
|
* bits in inputs that haven't been turned off.
|
|
|
|
|
*/
|
|
|
|
|
} while (block && !gotSome);
|
|
|
|
|
return gotSome;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef MAGIC_WRAPPER
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
2025-01-31 18:39:24 +01:00
|
|
|
* TxParseString_internal --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Parse a string into commands, and add them to the rear of a queue.
|
|
|
|
|
* The commands in the queue should eventually be freed by the caller
|
|
|
|
|
* via TxFreeCommand().
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
2025-01-31 18:39:24 +01:00
|
|
|
* Module internal API. See TxParseString() for public version.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2025-01-31 18:39:24 +01:00
|
|
|
static void
|
|
|
|
|
TxParseString_internal(
|
2025-01-31 18:15:29 +01:00
|
|
|
const char *str, /* The string to be parsed. */
|
2025-01-31 18:13:02 +01:00
|
|
|
DQueue *q, /* Add to the tail of this queue. */
|
|
|
|
|
TxInputEvent *event) /* An event to supply the point, window ID,
|
2017-04-25 14:41:48 +02:00
|
|
|
* etc. . If NULL, we will use the last
|
|
|
|
|
* event processed.
|
|
|
|
|
*/
|
|
|
|
|
{
|
2025-08-25 12:49:47 +02:00
|
|
|
const char *remainder;
|
2017-04-25 14:41:48 +02:00
|
|
|
TxCommand *cmd;
|
|
|
|
|
|
|
|
|
|
if (event == NULL) event = &txLastEvent;
|
|
|
|
|
|
|
|
|
|
remainder = str;
|
|
|
|
|
while (remainder != NULL) {
|
|
|
|
|
/* Parse a single command. */
|
|
|
|
|
cmd = TxNewCommand();
|
|
|
|
|
strncpy(cmd->tx_argstring, remainder, TX_MAX_CMDLEN);
|
|
|
|
|
cmd->tx_argstring[TX_MAX_CMDLEN - 1] = '\0';
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (ParsSplit(cmd->tx_argstring, TX_MAXARGS,
|
2017-04-25 14:41:48 +02:00
|
|
|
&(cmd->tx_argc), cmd->tx_argv, &remainder) )
|
|
|
|
|
{
|
|
|
|
|
if (event == NULL) event = &txLastEvent;
|
|
|
|
|
cmd->tx_button = TX_CHARACTER;
|
|
|
|
|
cmd->tx_p = event->txe_p;
|
|
|
|
|
cmd->tx_wid = event->txe_wid;
|
|
|
|
|
DQPushRear(q, (ClientData) cmd);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
TxError("Unable to completely parse command line '%s'\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
str);
|
|
|
|
|
TxFreeCommand(cmd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-31 18:39:24 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* TxParseString --
|
|
|
|
|
*
|
|
|
|
|
* Parse a string into commands, and add them to the rear of a queue.
|
|
|
|
|
* The commands in the queue should eventually be freed by the caller
|
|
|
|
|
* via TxFreeCommand().
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Public API. See also TxParseString_internal().
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
TxParseString(
|
|
|
|
|
const char *str) /* The string to be parsed. */
|
|
|
|
|
{
|
|
|
|
|
TxParseString_internal(str, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
/* tclmagic.c defines TxParseString() so we need a shim to other way
|
|
|
|
|
*
|
|
|
|
|
* FIXME it feels like there is some design error here, only this
|
|
|
|
|
* file cares about DQueue/TxInputEvent arguments but they are
|
|
|
|
|
* passed but not used by tclmagic.c TxParseString()
|
|
|
|
|
*
|
|
|
|
|
* It must be that !MAGIC_WRAPPER needs the DQueue/TxInputEvent for
|
|
|
|
|
* the other builds, such as WASM.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
TxParseString_internal(
|
|
|
|
|
const char *str, /* The string to be parsed. */
|
|
|
|
|
DQueue *q,
|
|
|
|
|
TxInputEvent *event)
|
|
|
|
|
{
|
|
|
|
|
TxParseString(str);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif /* !MAGIC_WRAPPER */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* txGetInteractiveCommand:
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Get one or more commands from one of the interactive input devices,
|
2017-04-25 14:41:48 +02:00
|
|
|
* and put it into the input queue.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
txGetInteractiveCommand(
|
|
|
|
|
bool block, /* If TRUE, then wait until we have a command.
|
2017-04-25 14:41:48 +02:00
|
|
|
* If FALSE, then get one only if input is
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
2025-01-31 18:13:02 +01:00
|
|
|
DQueue *queue) /* Queue to receive the new commands. */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
static char inputLine[TX_MAX_CMDLEN] = "";
|
|
|
|
|
TxInputEvent *event;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
int ch;
|
|
|
|
|
|
|
|
|
|
/* Get one new event and return (except for text commands, then collect
|
|
|
|
|
* an entire line.
|
|
|
|
|
*/
|
|
|
|
|
if (DQIsEmpty(&txInputEvents)) TxGetInputEvent(block, TRUE);
|
|
|
|
|
if (DQIsEmpty(&txInputEvents)) return;
|
|
|
|
|
|
|
|
|
|
event = (TxInputEvent *) DQPopFront(&txInputEvents);
|
|
|
|
|
txLastEvent = *event;
|
|
|
|
|
if (TxDebug) TxPrintEvent(event);
|
|
|
|
|
ASSERT(event != NULL, "txGetInteractiveCommand");
|
|
|
|
|
|
|
|
|
|
if (event->txe_button == TX_EOF) {
|
|
|
|
|
/* We have an end of file. Put it at the end of our command queue,
|
|
|
|
|
* but only if we are waiting for a command. We don't want to
|
|
|
|
|
* put in EOF's if we are just polling.
|
|
|
|
|
*/
|
|
|
|
|
if (block)
|
|
|
|
|
{
|
|
|
|
|
cmd = TxNewCommand();
|
|
|
|
|
cmd->tx_button = TX_EOF;
|
|
|
|
|
cmd->tx_p = event->txe_p;
|
|
|
|
|
cmd->tx_wid = event->txe_wid;
|
|
|
|
|
cmd->tx_argc = 0;
|
|
|
|
|
cmd->tx_argv[0] = NULL;
|
|
|
|
|
DQPushRear(queue, (ClientData) cmd);
|
|
|
|
|
}
|
|
|
|
|
TxFreeEvent(event);
|
|
|
|
|
} else if ((TxCurButtons != 0) && (event->txe_button == TX_CHARACTER)) {
|
|
|
|
|
/* We have a keyboard character, but buttons are still down.
|
|
|
|
|
* Push fake button up commands into the queue in front of this
|
|
|
|
|
* event, so that all buttons will be up by the time that we get
|
|
|
|
|
* to the character event. This is needed to avoid strange things
|
|
|
|
|
* when the user pushes a buttons down on a Sun 160 (or other device
|
|
|
|
|
* with a non-Magic window package) and then releases it outside of
|
|
|
|
|
* the Magic windows.
|
|
|
|
|
*/
|
|
|
|
|
TxInputEvent *newEvent;
|
|
|
|
|
int ourbuts;
|
|
|
|
|
|
|
|
|
|
ourbuts = TxCurButtons;
|
|
|
|
|
DQPushFront(&txInputEvents, event);
|
|
|
|
|
while (ourbuts != 0) {
|
|
|
|
|
newEvent = TxNewEvent();
|
|
|
|
|
newEvent->txe_p = event->txe_p;
|
|
|
|
|
newEvent->txe_wid = event->txe_wid;
|
|
|
|
|
/* Get rightmost '1' bit of ourbuts. */
|
|
|
|
|
newEvent->txe_button = LAST_BIT_OF(ourbuts);
|
|
|
|
|
newEvent->txe_buttonAction = TX_BUTTON_UP;
|
|
|
|
|
newEvent->txe_ch = 0;
|
|
|
|
|
ourbuts &= ~newEvent->txe_button;
|
|
|
|
|
DQPushFront(&txInputEvents, newEvent);
|
|
|
|
|
}
|
|
|
|
|
/* Now, go around the loop and process these things just like
|
|
|
|
|
* the user had typed them.
|
|
|
|
|
*/
|
|
|
|
|
} else if (event->txe_button == TX_CHARACTER) {
|
|
|
|
|
/* We have a text command, go into text collection mode */
|
|
|
|
|
|
|
|
|
|
/* need to check for long command or macro, then assemble line */
|
|
|
|
|
ch = event->txe_ch;
|
|
|
|
|
TxFreeEvent(event);
|
|
|
|
|
if ((ch == (int)TX_LONG_CMD) || (ch == (int)TX_LONG_CMD2))
|
|
|
|
|
{
|
|
|
|
|
(void) TxGetLinePrompt(inputLine, TX_MAX_CMDLEN, TX_CMD_PROMPT);
|
|
|
|
|
if (inputLine[0] != '\0') MacroDefine(DBWclientID, (int)'.',
|
|
|
|
|
inputLine, NULL, FALSE);
|
2025-01-31 18:39:24 +01:00
|
|
|
TxParseString_internal(inputLine, queue, (TxInputEvent* ) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bool iMacro; /* Interactive macro */
|
|
|
|
|
char *macroDef;
|
|
|
|
|
|
|
|
|
|
macroDef = MacroRetrieve(DBWclientID, ch, &iMacro);
|
|
|
|
|
if (macroDef == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Cases macroDef may be NULL: */
|
|
|
|
|
/* 1) Return 2) Ctrl-C 3) Invalid macro */
|
|
|
|
|
|
|
|
|
|
if (ch == (int)'\n')
|
|
|
|
|
{
|
|
|
|
|
if (TxInteractive) TxPrintf("%c\n", TX_PROMPT);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* not a valid command */
|
|
|
|
|
char *vis = MacroName(ch);
|
|
|
|
|
TxError("Unknown macro or short command: '%s'\n", vis);
|
|
|
|
|
freeMagic(vis);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (iMacro)
|
|
|
|
|
{
|
|
|
|
|
(void) TxGetLineWPrompt(inputLine,
|
|
|
|
|
TX_MAX_CMDLEN, TX_CMD_PROMPT, macroDef);
|
|
|
|
|
if (inputLine[0] != '\0') MacroDefine(DBWclientID, (int)'.',
|
|
|
|
|
inputLine, NULL, FALSE);
|
2025-01-31 18:39:24 +01:00
|
|
|
TxParseString_internal(inputLine, queue, (TxInputEvent *) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-01-31 18:39:24 +01:00
|
|
|
TxParseString_internal(macroDef, queue, (TxInputEvent *) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
freeMagic(macroDef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if ((event->txe_button & TX_LEFT_BUTTON) ||
|
|
|
|
|
(event->txe_button & TX_MIDDLE_BUTTON) ||
|
|
|
|
|
(event->txe_button & TX_RIGHT_BUTTON)) {
|
|
|
|
|
/* We have a button command, but ignore double ups & downs. */
|
|
|
|
|
int oldButtons;
|
|
|
|
|
oldButtons = TxCurButtons;
|
|
|
|
|
if (event->txe_buttonAction == TX_BUTTON_UP)
|
|
|
|
|
TxCurButtons &= ~(event->txe_button);
|
|
|
|
|
else
|
|
|
|
|
TxCurButtons |= event->txe_button;
|
|
|
|
|
if (oldButtons == TxCurButtons) {
|
|
|
|
|
TxFreeEvent(event);
|
|
|
|
|
} else {
|
|
|
|
|
/* We have a valid button command. */
|
|
|
|
|
cmd = TxNewCommand();
|
|
|
|
|
cmd->tx_button = event->txe_button;
|
|
|
|
|
cmd->tx_buttonAction = event->txe_buttonAction;
|
|
|
|
|
cmd->tx_p = event->txe_p;
|
|
|
|
|
cmd->tx_wid = event->txe_wid;
|
|
|
|
|
cmd->tx_argc = 0;
|
|
|
|
|
cmd->tx_argv[0] = NULL;
|
|
|
|
|
DQPushRear(queue, (ClientData) cmd);
|
|
|
|
|
TxFreeEvent(event);
|
|
|
|
|
}
|
|
|
|
|
} else { /* TX_BYPASS */
|
|
|
|
|
/* Ignore this event */
|
|
|
|
|
TxFreeEvent(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* txGetFileCommand --
|
|
|
|
|
*
|
|
|
|
|
* Get a line from a file and stick the commands into the input queue.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Reads the file and puts a command into the queue.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
txGetFileCommand(
|
|
|
|
|
FILE *f, /* File to read. */
|
|
|
|
|
DQueue *queue) /* Queue to receive the new commands. */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
char inputLine[TX_MAX_CMDLEN];
|
|
|
|
|
char *linep;
|
|
|
|
|
char *current;
|
|
|
|
|
int spaceleft;
|
|
|
|
|
|
|
|
|
|
/* Generate a line by patching together successive lines ending
|
|
|
|
|
* in '\'.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
current = inputLine;
|
|
|
|
|
spaceleft = TX_MAX_CMDLEN - 1;
|
|
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
if (fgets(current, spaceleft, f) == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
while (*current != 0)
|
|
|
|
|
{
|
|
|
|
|
current++;
|
|
|
|
|
spaceleft--;
|
|
|
|
|
}
|
|
|
|
|
if ((*(current-1) != '\n') || (*(current-2) != '\\')) goto gotline;
|
|
|
|
|
current--;
|
|
|
|
|
spaceleft++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gotline: *current = 0;
|
|
|
|
|
|
|
|
|
|
/* If the line is empty, or contains a hash mark as
|
|
|
|
|
* the first non-blank character, then skip over it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
current = inputLine;
|
|
|
|
|
while (isspace(*current)) current++;
|
|
|
|
|
} while ((*current == 0) || (*current == '#'));
|
|
|
|
|
|
|
|
|
|
linep = inputLine;
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
/* Don't muck with Tcl's interpretation of semicolon as a */
|
|
|
|
|
/* comment character. Watch for single-colon entries for */
|
|
|
|
|
/* backwards-compatibility with the old format, but watch */
|
|
|
|
|
/* for the double-colon, which is a Tcl namespace marker. */
|
|
|
|
|
|
|
|
|
|
if ((inputLine[0] == ':') && (inputLine[1] != ':')) linep++;
|
|
|
|
|
#else
|
|
|
|
|
if ((inputLine[0] == ':') || (inputLine[1] == ';')) linep++;
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-01-31 18:39:24 +01:00
|
|
|
TxParseString_internal(linep, queue, (TxInputEvent *) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-08 23:34:00 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxRebuildCommand:
|
|
|
|
|
*
|
|
|
|
|
* Rebuild the arguments of a TxCommand structure. This assumes
|
|
|
|
|
* that a routine has rewritten the tx_argstring record with a
|
|
|
|
|
* modified command. Tokenize the tx_argstring and update the
|
|
|
|
|
* tx_argc count and tx_argv pointers.
|
|
|
|
|
*
|
|
|
|
|
* The purpose of this routine is to allow some command callbacks
|
|
|
|
|
* to change the command from one implying the use of pointer
|
|
|
|
|
* coordinates to an equivalent command that uses database units,
|
|
|
|
|
* for the purpose of logging the command.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxRebuildCommand(
|
|
|
|
|
TxCommand *cmd)
|
2023-07-08 23:34:00 +02:00
|
|
|
{
|
|
|
|
|
char *cptr, *tptr, c;
|
|
|
|
|
|
|
|
|
|
cmd->tx_argc = 0;
|
|
|
|
|
tptr = cptr = cmd->tx_argstring;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
c = *cptr;
|
|
|
|
|
if ((c == ' ') || (c == '\0'))
|
|
|
|
|
{
|
|
|
|
|
cmd->tx_argv[cmd->tx_argc] = tptr;
|
|
|
|
|
cmd->tx_argc++;
|
|
|
|
|
*cptr = '\0';
|
|
|
|
|
tptr = cptr + 1;
|
|
|
|
|
}
|
|
|
|
|
cptr++;
|
|
|
|
|
}
|
|
|
|
|
while (c != '\0');
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxTclDispatch:
|
|
|
|
|
*
|
|
|
|
|
* Command dispatcher which relies on the Tcl interpreter for most
|
|
|
|
|
* of the work. Replaces TxDispatch except for file input during
|
|
|
|
|
* startup.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-31 18:13:02 +01:00
|
|
|
TxTclDispatch(
|
|
|
|
|
ClientData clientData,
|
|
|
|
|
int argc,
|
|
|
|
|
char *argv[],
|
|
|
|
|
bool quiet)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int result;
|
|
|
|
|
int n, asize;
|
|
|
|
|
TxCommand *tclcmd;
|
|
|
|
|
unsigned char lastdrc;
|
|
|
|
|
|
|
|
|
|
if (argc > TX_MAXARGS)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: number of command arguments exceeds %d!\n", TX_MAXARGS);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SigIOReady = FALSE;
|
|
|
|
|
if (SigInterruptOnSigIO >= 0) SigInterruptOnSigIO = 1;
|
|
|
|
|
SigInterruptPending = FALSE;
|
|
|
|
|
|
|
|
|
|
tclcmd = TxNewCommand();
|
|
|
|
|
tclcmd->tx_argc = argc;
|
|
|
|
|
|
|
|
|
|
asize = 0;
|
|
|
|
|
for (n = 0; n < argc; n++)
|
|
|
|
|
{
|
|
|
|
|
if (asize + strlen(argv[n]) >= TX_MAX_CMDLEN)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: command length exceeds %d characters!\n", TX_MAX_CMDLEN);
|
|
|
|
|
TxFreeCommand(tclcmd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
strcpy(&tclcmd->tx_argstring[asize], argv[n]);
|
|
|
|
|
tclcmd->tx_argv[n] = &tclcmd->tx_argstring[asize];
|
|
|
|
|
asize += (1 + strlen(argv[n]));
|
|
|
|
|
}
|
|
|
|
|
tclcmd->tx_p = txCurrentPoint;
|
|
|
|
|
if (txHaveCurrentPoint)
|
|
|
|
|
tclcmd->tx_wid = txCurrentWindowID;
|
|
|
|
|
else
|
|
|
|
|
tclcmd->tx_wid = WIND_UNKNOWN_WINDOW;
|
|
|
|
|
|
|
|
|
|
/* Prevent DRCContinuous from running in the middle of a command */
|
|
|
|
|
/* (Note that Tcl_CancelIdleCall() does *not* work. . .) */
|
|
|
|
|
|
|
|
|
|
lastdrc = DRCBackGround;
|
|
|
|
|
if (DRCBackGround != DRC_SET_OFF) DRCBackGround = DRC_NOT_SET;
|
|
|
|
|
|
|
|
|
|
result = WindSendCommand((MagWindow *)clientData, tclcmd, quiet);
|
|
|
|
|
|
2023-06-29 03:31:24 +02:00
|
|
|
if (txLogFile != NULL) txLogCommand(tclcmd);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
TxFreeCommand(tclcmd);
|
|
|
|
|
|
|
|
|
|
// Don't update the command number on bypass commands, or else
|
|
|
|
|
// the selection mechanism gets broken by the background DRC.
|
|
|
|
|
|
|
|
|
|
if (argc > 0 && strcmp(argv[0], "*bypass")) TxCommandNumber++;
|
|
|
|
|
|
|
|
|
|
if (SigInterruptPending)
|
|
|
|
|
TxPrintf("[Interrupted]\n");
|
|
|
|
|
|
|
|
|
|
if (result == 0) WindUpdate();
|
|
|
|
|
|
|
|
|
|
SigInterruptPending = FALSE;
|
|
|
|
|
if (SigInterruptOnSigIO >= 0) SigInterruptOnSigIO = 0;
|
|
|
|
|
SigIOReady = FALSE;
|
|
|
|
|
|
|
|
|
|
/* Force a break of DRCContinuous() so we don't run DRC on an */
|
|
|
|
|
/* invalid database. */
|
|
|
|
|
|
|
|
|
|
/* Avoid breaking on "*bypass" commands. Technically, one could */
|
|
|
|
|
/* set up a command to *bypass that alters the database and crashes */
|
|
|
|
|
/* magic. This is a total hack. We should flag an invalid */
|
|
|
|
|
/* database instead. */
|
|
|
|
|
|
|
|
|
|
if (DRCBackGround == DRC_NOT_SET) DRCBackGround = lastdrc;
|
|
|
|
|
if (argc > 0 && strcmp(argv[0], "*bypass") && strcmp(argv[0], "windownames"))
|
|
|
|
|
DRCBreak();
|
|
|
|
|
|
|
|
|
|
/* Reinstate the idle call */
|
|
|
|
|
if (result == 0) Tcl_DoWhenIdle(DRCContinuous, (ClientData)NULL);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
#else /* !MAGIC_WRAPPER */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* TxDispatch:
|
|
|
|
|
*
|
|
|
|
|
* Read input from the interactive devices (keyboard or mouse) or from
|
|
|
|
|
* a file. Send a command off to WindSendCommand for processing, and
|
|
|
|
|
* then repeat.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Commands are read and executed, one per cycle of this procedure's loop.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
TxDispatch(
|
|
|
|
|
FILE *f) /* Read commands from this file instead of
|
2017-04-25 14:41:48 +02:00
|
|
|
* from the mouse or keyboard.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
bool inFile;
|
|
|
|
|
DQueue inputCommands; /* A queue of commands from our input
|
|
|
|
|
* sources.
|
2020-05-23 23:13:14 +02:00
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
DQInit(&inputCommands, 4);
|
|
|
|
|
|
|
|
|
|
inFile = (f != (FILE *) NULL);
|
|
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
if (!inFile)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
/* Update the screen info of the window package and all of
|
2017-04-25 14:41:48 +02:00
|
|
|
* its clients. If there's already input waiting, skip this...
|
|
|
|
|
* it can get done at the end, after all pending commands have
|
|
|
|
|
* been processed.
|
|
|
|
|
*/
|
|
|
|
|
if (SigInterruptOnSigIO >= 0) SigInterruptOnSigIO = 0;
|
|
|
|
|
SigInterruptPending = FALSE;
|
|
|
|
|
txGetInteractiveCommand(FALSE, &inputCommands);
|
|
|
|
|
if (DQIsEmpty(&inputCommands))
|
|
|
|
|
{
|
|
|
|
|
WindUpdate();
|
2020-05-23 23:13:14 +02:00
|
|
|
if (SigInterruptPending)
|
2017-04-25 14:41:48 +02:00
|
|
|
TxPrintf("[Redisplay Interrupted]\n");
|
|
|
|
|
txGetInteractiveCommand(FALSE, &inputCommands);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Give the DRC a chance to work. */
|
|
|
|
|
if (!SigGotSigWinch && DQIsEmpty(&inputCommands)) {
|
|
|
|
|
if (DRCHasWork) {
|
|
|
|
|
TxSetPrompt(']');
|
|
|
|
|
TxPrompt();
|
|
|
|
|
(void) GrEnableTablet();
|
|
|
|
|
GrFlush();
|
|
|
|
|
|
|
|
|
|
/* Call background DRC.
|
|
|
|
|
* It will return if it is not enabled, there is no work to
|
2020-05-23 23:13:14 +02:00
|
|
|
* be done, or it learns that the user has entered a
|
2017-04-25 14:41:48 +02:00
|
|
|
* command.
|
|
|
|
|
*/
|
|
|
|
|
SigIOReady = FALSE;
|
|
|
|
|
if (SigInterruptOnSigIO >= 0) SigInterruptOnSigIO = 1;
|
|
|
|
|
SigInterruptPending = FALSE;
|
|
|
|
|
DRCContinuous();
|
|
|
|
|
TxUnPrompt();
|
|
|
|
|
|
|
|
|
|
SigIOReady = FALSE;
|
|
|
|
|
if (SigInterruptOnSigIO >= 0) SigInterruptOnSigIO = 0;
|
|
|
|
|
SigInterruptPending = FALSE;
|
|
|
|
|
(void) GrDisableTablet();
|
|
|
|
|
WindUpdate();
|
|
|
|
|
if (SigInterruptPending)
|
|
|
|
|
TxPrintf("[DRC Redisplay Interrupted]\n");
|
|
|
|
|
}
|
|
|
|
|
TxSetPrompt(TX_PROMPT);
|
|
|
|
|
TxPrompt();
|
|
|
|
|
(void) GrEnableTablet();
|
|
|
|
|
GrFlush();
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
txGetInteractiveCommand(TRUE, &inputCommands);
|
|
|
|
|
|
|
|
|
|
TxUnPrompt();
|
|
|
|
|
(void) GrDisableTablet();
|
|
|
|
|
GrFlush();
|
|
|
|
|
SigInterruptPending = FALSE;
|
|
|
|
|
SigIOReady = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (feof(f)) goto done;
|
2020-05-23 23:13:14 +02:00
|
|
|
if (SigInterruptPending && inFile)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxPrintf("[Read-in of command file aborted]\n");
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
if (!SigGotSigWinch) txGetFileCommand(f, &inputCommands);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SigGotSigWinch) {
|
|
|
|
|
/* WindUpdate will take care of SigWinch and set it to FALSE */
|
|
|
|
|
WindUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now send off the commands in the queue. */
|
|
|
|
|
while (!DQIsEmpty(&inputCommands))
|
|
|
|
|
{
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
|
|
|
|
|
cmd = (TxCommand *) DQPopFront(&inputCommands);
|
|
|
|
|
if (TxDebug) TxPrintCommand(cmd);
|
|
|
|
|
if (cmd->tx_button == TX_EOF)
|
|
|
|
|
{
|
|
|
|
|
/* End of our input stream. */
|
|
|
|
|
TxError("EOF encountered on input stream -- Magic exiting.\n");
|
|
|
|
|
MainExit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/****
|
|
|
|
|
ASSERT(cmd->tx_p.p_x > 0 && cmd->tx_p.p_x < 10000, "TxDispatch");
|
|
|
|
|
ASSERT(cmd->tx_p.p_y > 0 && cmd->tx_p.p_y < 10000, "TxDispatch");
|
|
|
|
|
****/
|
|
|
|
|
|
|
|
|
|
/* force the point, if that was requested */
|
|
|
|
|
if (txHaveCurrentPoint) {
|
|
|
|
|
cmd->tx_p = txCurrentPoint;
|
|
|
|
|
cmd->tx_wid = txCurrentWindowID;
|
|
|
|
|
if (!inFile) txHaveCurrentPoint = FALSE;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/****
|
2020-05-23 23:13:14 +02:00
|
|
|
ASSERT(cmd->tx_argc >= 0 && cmd->tx_argc <= TX_MAXARGS,
|
2017-04-25 14:41:48 +02:00
|
|
|
"TxDispatch");
|
|
|
|
|
if (cmd->tx_argc != 0) {
|
|
|
|
|
ASSERT(cmd->tx_button == TX_CHARACTER, "TxDispatch");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ASSERT((cmd->tx_buttonAction == TX_BUTTON_DOWN) || \
|
|
|
|
|
(cmd->tx_buttonAction == TX_BUTTON_UP),
|
|
|
|
|
"TxDispatch");
|
|
|
|
|
ASSERT((cmd->tx_button == TX_LEFT_BUTTON) || \
|
|
|
|
|
(cmd->tx_button == TX_MIDDLE_BUTTON) || \
|
|
|
|
|
(cmd->tx_button == TX_RIGHT_BUTTON) || \
|
|
|
|
|
(cmd->tx_button == TX_CHARACTER),
|
|
|
|
|
"TxDispatch");
|
|
|
|
|
};
|
|
|
|
|
****/
|
|
|
|
|
if (!inFile && (txLogFile != NULL)) txLogCommand(cmd);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* Add lisp interpreter here. We trap the character
|
|
|
|
|
* commands and call the lisp interpreter that traps
|
|
|
|
|
* any lisp-like behavior and returns a stream of "cmd"
|
|
|
|
|
* functions which are then sent to the appropriate window
|
|
|
|
|
* using the usual WindSendCommand() call.
|
|
|
|
|
*
|
|
|
|
|
* (rajit@cs.caltech.edu)
|
|
|
|
|
*/
|
|
|
|
|
#ifdef SCHEME_INTERPRETER
|
|
|
|
|
if (cmd->tx_button == TX_CHARACTER)
|
|
|
|
|
{
|
|
|
|
|
lisp_cur_cmd = cmd;
|
|
|
|
|
LispEvaluate (cmd->tx_argc, cmd->tx_argv, inFile);
|
|
|
|
|
TxFreeCommand (cmd);
|
|
|
|
|
lisp_cur_cmd = NULL;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
#endif
|
|
|
|
|
(void) WindSendCommand((MagWindow *) NULL, cmd, FALSE);
|
|
|
|
|
TxFreeCommand(cmd);
|
|
|
|
|
TxCommandNumber++;
|
|
|
|
|
|
|
|
|
|
#ifdef SCHEME_INTERPRETER
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
/* ?? */
|
|
|
|
|
WindUpdate();
|
|
|
|
|
|
|
|
|
|
} /* while */
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
while (!DQIsEmpty(&inputCommands))
|
|
|
|
|
TxFreeCommand((TxCommand *) DQPopFront(&inputCommands));
|
|
|
|
|
DQFree(&inputCommands);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* !MAGIC_WRAPPER */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* txCommandsInit --
|
|
|
|
|
*
|
|
|
|
|
* Initialize the things in this file
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Variables are set up.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 18:13:02 +01:00
|
|
|
txCommandsInit(void)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
txZeroTime.tv_sec = 0;
|
|
|
|
|
txZeroTime.tv_usec = 0;
|
|
|
|
|
FD_ZERO(&txInputDescriptors);
|
txCommands.c txInputDevRec FD select() usage overhaul
There are numerous concerns with the original code from a read through.
The #define TX_MAX_OPEN_FILES 20 is badly named, the txCommand.c uses
fd_set to hold active FD for each device, and makes no attempt to bounds
check the 'fd' or 'fd_set' of any TxAdd1InputDevice() is in range.
The real name of this variable should be TX_MAX_INPUT_DEVICES as it
related to the fixed compile time slots available for devices, each input
device must have at least one active FD and it can be in the range
that fd_set allows, which is 0..1023 on linux.
So based on this being the original intention, due to the way the code is
constructed and API calls made available, the file has been reworked
to allow all these considerations at the same time.
Now the design should be FD clean for FDs in the range 0..1023 instead of
being artificially clamped to the first 20 FDs being valid input devices.
Renaming of variable 'i' to 'fd' when it relates to an fd number, so all
uses of FD_XXXX() should use fd, this helps clarify the different domain
the number relates to.
When 'i' is used it relates to the index into the txInputDevRec array.
A large part of the concerns relate to trying to mix the two numbering
domains interchangeably, just using a different name really clears up
understanding to the reader.
Other items that look like errors TxDelete1InputDevice() will
shuffle-remove entries with no active FD, but it is iterating an array
it is also modifying, so it looks like it would have incorrectly skipped
the next item that should be inspected just after a shuffle-removal
occurred.
The various iterators that works from 0..TX_MAX_OPEN_FILES incorrectly
limited the visibility of the routines to the first 20 FDs instead of
the full FD_SETSIZE the TxAddInputDevice API call allows to be
registered.
Passing in TxAdd1InputDevice with an fd above 19 would have resulted in
everything looking successful until select() was used, then the kernel
would likely not sleep/wait because the input fd_set would look blank due
to being clipped by nfds=TX_MAX_OPEN_FILES (instead of that plus 1).
The use of TX_MAX_OPEN_FILES in select instead of TX_MAX_OPEN_FILES+1 for
the 'nfds' field would have meant a device with fd=19 would not work as
the design intended.
The define definition has been removed from the module public header,
I assume it was there for use by another module, but the incorrect
select() usage has been corrected over there in a previous commit.
So now the define can be maintained near the array it relates to.
While the code might looks less 'efficient' as it is now sweeping all
1024 FDs when input devices during add/remove (hopefully there maybe some
compiler auto-vectorization/tzcnt use there). The main event loop is
slightly more 'efficient' (especially for the single device with input
fd=0 case) as it will only check the 1 FD each input event loop iteration,
not check all 20 FDs for data readyness everytime.
2025-02-24 09:49:07 +01:00
|
|
|
txInputDescriptors_nfds = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
DQInit(&txInputEvents, 4);
|
|
|
|
|
DQInit(&txFreeEvents, 4);
|
|
|
|
|
DQInit(&txFreeCommands, 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef SCHEME_INTERPRETER
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* txLispDispatch --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Send command to magic window.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns TRUE on success, FALSE on failure.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* whatever the command does.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
TxLispDispatch (argc,argv,trace, inFile)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
int trace;
|
|
|
|
|
int inFile;
|
|
|
|
|
{
|
|
|
|
|
TxCommand *cmd = lisp_cur_cmd;
|
|
|
|
|
int i,j,k;
|
|
|
|
|
bool ret;
|
|
|
|
|
Point tx_p;
|
|
|
|
|
int wid;
|
|
|
|
|
|
|
|
|
|
cmd->tx_argc = argc;
|
|
|
|
|
for (i=0,j=0; i < argc; i++) {
|
|
|
|
|
cmd->tx_argv[i] = cmd->tx_argstring+j;
|
|
|
|
|
k=0;
|
|
|
|
|
while (cmd->tx_argstring[j] = argv[i][k])
|
|
|
|
|
j++,k++;
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
tx_p = cmd->tx_p;
|
|
|
|
|
wid = cmd->tx_wid;
|
|
|
|
|
if (txHaveCurrentPoint) {
|
|
|
|
|
cmd->tx_p = txCurrentPoint;
|
|
|
|
|
cmd->tx_wid = txCurrentWindowID;
|
|
|
|
|
if (!inFile) txHaveCurrentPoint = FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (trace)
|
|
|
|
|
TxPrintCommand (cmd);
|
|
|
|
|
ret = WindSendCommand((MagWindow *) NULL, cmd, FALSE);
|
|
|
|
|
/* restore the original values */
|
|
|
|
|
cmd->tx_p = tx_p;
|
|
|
|
|
cmd->tx_wid = wid;
|
|
|
|
|
TxCommandNumber++;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* SCHEME_INTERPRETER */
|