home *** CD-ROM | disk | FTP | other *** search
- /*
-
- 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!
- */