home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_2.iso
/
files
/
731.lha
/
AKeySwap_v1.1
/
AKeySwap.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-02-07
|
12KB
|
373 lines
/*
AKeySwap.c
Version 1.1 by John Fieber
05 Feb 1993
v1.1 05 Feb 1993 fixed a tiny "sanity check" bug
v1.0 31 Jan 1993 first polished version
v0.9 16 Dec 1992 first quick-and-dirty version
1. What is AKeySwap
The two `Amiga' keys to the left and right of the spacebar serve
different functions in the Amiga system. The right key is used for
menu shortcuts while the left is reserved for system use, such as
flipping through screens.
The menu shortcut key is the most frequently used by most people but
some find it awkward to use. AKeySwap swaps the the left and right
Amiga key functions so you can use the left Amiga key as the menu
shortcut key. Correspondingly, the right Amiga key becomes the
system key.
2. Installation and Use
Because AKeySwap is a commodity, you need at least version 2.04 of
the operating system.
Using AKeySwap is trivial. Just double click on the icon. For
`permanent' installation, drop it in your WBStartup drawer. Be sure
that the DONOTWAIT tooltype is present. Alternately, you may run it
from a shell or your User-Startup. It runs as a background task so
there is no need to "RUN >NIL: <NIL" it.
For optimal results, AKeySwap should be one of the very first
programs in the input event chain and thus the default CX_PRIORITY is
100. If you need to adjust this, you may put "CX_PRIORITY=<value>" in
the icon tooltypes or specify it on the command line.
As with all commodities, you can activate, deactivate or remove
AKeySwap by using the Exchange program supplied by Commodore. You
can also remove AKeySwap by running it a second time or sending a
Control-C break to its process.
3. Credits and Comments
This program was written at the suggestion of David Salamon.
Please feel free to distribute and modify this under the following
conditions:
· The source code, documentation and executable must be distributed
together.
· Any changes to the source, documentation or executable must be
documented with what was change and who performed the change.
· Do make an attempt to contact me about the change. See below.
Send comments suggestions to:
jfieber@sophia.smith.edu
or for postal users:
John Fieber
14 Conz
Northampton MA 01060-3861
USA
*/
/*
Here is the soruce code. This was written to be compiled with the
SAS/C 6.x compiler and linked with the cback.o startup module.
The defaults in the SCOPTIONS file call for a gst which is not
included in the archive. Just supply the MAKEGST=AKeySwap.gst option
to rebuild it.
Some basic "who we are" #defines and a version string to be found by
the `Version' shell command.
*/
#define PROGNAME "AKeySwap"
#define PROGVERSION "1.1"
#define PROGDATE "(5.2.93)"
static char *VersionTag = "\0$VER: " PROGNAME " " PROGVERSION " " PROGDATE;
/*
Here are the include files for the system interface.
*/
#include <stdlib.h>
#include <exec/exec.h>
#include <dos/dos.h>
#include <libraries/commodities.h>
#include <workbench/icon.h>
#include <devices/inputevent.h>
/*
To accompany the previous includes, Commodore provides us with
C function prototypes for all the system funtions. SAS throws in
#pragmas for the functions and declares the library base pointers so
that the startup code will automagically open the necessary
libraries.
*/
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/commodities.h>
#include <proto/icon.h>
#include <clib/alib_protos.h>
/*
While we are at it, how about prototypes for functions defined in
this module...all two of them.
*/
void __saveds swap_keys(CxMsg *cxmessage, CxObj *cxobject);
void process_events(void);
/*
The SAS/C cback.o startup module uses this information to detach this
program from the shell if you wish to start from one. Since this
process doesn't actually do much of anything, we can save some memory
on the stack.
*/
long __stack = 1024L; /* How much stack we need */
char *__procname = PROGNAME; /* What the process should be called */
long __BackGroundIO = 0L; /* Do we want to allow for some output */
/*
Some more SAS/C specific stuff. The startup code will automagically
open any libraries whose bases are declared (NOT defined!). This
variable indicates the minimum library version that we need.
*/
long __oslibversion = 37;
/*
The commodities system works by having applications (such as this)
register with the system by using a broker. When the input device
gets an input event, it passes it off to the commodities system which
in turn passes it to all the active brokers. To register our broker,
we need a NewBroker structure.
The NBU_UNIQUE flag means that only one broker with our name is
allowed in the system. The commodities system will refuse to create
another one, thus the call to CxBroker will fail. The NBU_NOTIFY
command informs that if the above happens, the commodities system
should notify the existing broker of the event.
*/
struct NewBroker newbroker = {
NB_VERSION,
PROGNAME,
PROGNAME " " PROGVERSION,
"Swaps left and right Amiga keys",
NBU_UNIQUE | NBU_NOTIFY,
0,
0,
NULL,
0
};
/*
Pointers to our commodities opjects.
*/
CxObj *broker;
CxObj *custom;
/*
Another thing we need is a message port through which the commodities
system can communicate with our task.
*/
struct MsgPort *broker_message_port; /* where we get message from */
/*
User supplied parameters should always be subjected to some sort of
sanity check. In this case, we want to make sure the CX_PRIORITY
option is reasonable. These macros will ensure that.
*/
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) <= (b) ? (a) : (b))
#define range_check(user_value, a, b) max(min(user_value, max(a,b)), min(a,b))
/*
And this is the default, maximum and minimum values for the
CX_PRIORITY parameter.
*/
#define DEFAULT_CX_PRIORITY 100
#define MAX_CX_PRIORITY 127
#define MIN_CX_PRIORITY -128
/*
And here is the main function. Basically we set up our commodity and
then go to sleep while waiting for messages from the commodities
system.
*/
main(int argc, char *argv[])
{
UBYTE **user_parameters;
CxMsg *msg; /* a scratch message pointer */
int error = 20; /* reset to 0 if everything goes okay */
/* Get the user's parameters */
user_parameters = ArgArrayInit(argc, argv);
/* Try to create the broker. Start by creating a message port */
if (broker_message_port = CreateMsgPort()) {
newbroker.nb_Port = broker_message_port;
/* Set the broker priority */
newbroker.nb_Pri = (BYTE) ArgInt(user_parameters, "CX_PRIORITY", DEFAULT_CX_PRIORITY);
newbroker.nb_Pri = range_check(newbroker.nb_Pri, MAX_CX_PRIORITY, MIN_CX_PRIORITY);
if (broker = CxBroker(&newbroker, NULL)) {
if (custom = CxCustom(swap_keys, 0L)) {
AttachCxObj(broker, custom);
ActivateCxObj(broker, TRUE);
process_events();
error = 0;
}
/* Clean the broker and all attached objects */
DeleteCxObjAll(broker);
}
/* Empty the message port and delete it */
while(msg = (CxMsg *) GetMsg(broker_message_port))
ReplyMsg((struct Message *) msg);
DeleteMsgPort(broker_message_port);
}
ArgArrayDone();
exit(error);
}
/*
Processing events is trivial. The only thing we have to deal with is
messages telling us to go away and messages telling us to either
activate or de-activate. The action of this commodity happens
entirely in its objects which run under a different task. This task
just sets everything up and the goes into hibernation.
*/
void process_events(void)
{
ULONG signal_received; /* what signals did we get? */
BOOL finished = FALSE; /* should we quit? */
CxMsg *message; /* a commodities message */
ULONG message_type; /* what sort of a message is it? */
ULONG message_id; /* what sort is it really? */
while (!finished) {
/* Wait for a signal... */
signal_received = Wait(SIGBREAKF_CTRL_C | 1L << broker_message_port->mp_SigBit);
while((message = (CxMsg *) GetMsg(broker_message_port)) && (!finished)) {
message_type = CxMsgType(message);
message_id = CxMsgID(message);
ReplyMsg((struct Message *) message);
if (message_type == CXM_COMMAND) {
switch(message_id) {
case CXCMD_DISABLE: /* disable ourself */
ActivateCxObj(broker, 0L);
break;
case CXCMD_ENABLE: /* enable ourself */
ActivateCxObj(broker, 1L);
break;
case CXCMD_KILL: /* vanish! (poof!) */
case CXCMD_UNIQUE:
finished = TRUE;
break;
}
}
}
/* Check for a break signal */
if (signal_received & SIGBREAKF_CTRL_C)
finished = TRUE;
}
}
/*
Up till now, most everything has been "stage-setting". This is the
function where the program actually does it's work. The commodities
system calls this function when our custom object receives a
commodities message. Do note that this function runs under the
input.device task. As such, we must be very conservative in our
stack usage, and most importantly, realize that we are running in a
*task*, not a process and thus we cannot use anything from the
dos.library. For the purposes of this program neither the stack nor
the task limitation are issues.
Anyway, on to the point. This function has to do two main things
when it receives a commodities message. The first must be done for
any keystroke and that is to look at the qualfier field of the input
event contained in the message and swap the states of the two Amiga
keys. The ie_Qualifier field is a bit field and the positions left
and right qualifier keys, including shift and alternate, are adjacent
so flipping them can be easily done with bitshifts.
The second task is that when one of the Amiga keys goes either up or
down, a message is generated in which the code field of the input
event is for the Amiga key that was hit. We have to change these too.
*/
/* The raw key codes. */
#define RAW_LAMIGA 102
#define RAW_RAMIGA 103
/* And the bitmasks for the ie_Qualifier field. */
#define LEFT_QUALIFIER_MASK (IEQUALIFIER_LCOMMAND)
#define RIGHT_QUALIFIER_MASK (IEQUALIFIER_RCOMMAND)
/* Note the __saveds. This is required as this function is called
from another task. */
void __saveds swap_keys(CxMsg *cxmessage, CxObj *cxobject)
{
register struct InputEvent *ie;
register UWORD left_qualifiers;
register UWORD right_qualifiers;
UWORD upstroke;
ie = (struct InputEvent *) CxMsgData(cxmessage);
while(ie) {
left_qualifiers = ie->ie_Qualifier & LEFT_QUALIFIER_MASK;
right_qualifiers = ie->ie_Qualifier & RIGHT_QUALIFIER_MASK;
/* If either qualifier is present, do the flip */
if (left_qualifiers || right_qualifiers) {
/* Flip the Amiga keys in the ie_Qualifier field */
ie->ie_Qualifier &= ~(LEFT_QUALIFIER_MASK | RIGHT_QUALIFIER_MASK);
ie->ie_Qualifier |= (left_qualifiers << 1) | (right_qualifiers >> 1);
/* Adjust the ie_Code field if necessary */
if (ie->ie_Class == IECLASS_RAWKEY) {
/* save and clear IECODE_UP_PREFIX from ie_Code */
upstroke = ie->ie_Code & IECODE_UP_PREFIX;
ie->ie_Code &= ~IECODE_UP_PREFIX;
if (ie->ie_Code == RAW_LAMIGA)
ie->ie_Code = RAW_RAMIGA;
else if (ie->ie_Code == RAW_RAMIGA)
ie->ie_Code = RAW_LAMIGA;
/* restore upstroke */
ie->ie_Code |= upstroke;
}
} /* end of flipping code */
ie = ie->ie_NextEvent;
}
}
/*
And that is it!
*/