home *** CD-ROM | disk | FTP | other *** search
- /* Altabber, n. He who performs an `Alt-Tab' action.
- **
- ** Version 1.5 (1.4.99) © Mfc.
- **
- ** Compile with DICE:
- ** dcc -mi -mRR -pi Altabber.c NewReadArgs.c
- **
- ** The "fixed-keys" version of Altabber (without argument/tooltypes parsing)
- ** can be obtained with:
- ** dcc -mi -mRR -DFIXEDKEYS -pi Altabber.c
- **
- ** To enable debugging output, simply add "-DDEBUG"
- **
- */
-
-
- #include "Altabber.h"
- #ifndef FIXEDKEYS
- #include <NewReadArgs.h>
- #endif
-
- /// Defines
-
- /* Messages we will receive from the Cx filter: */
- #define WINTAB 1 /* Cycle thru windows */
- #define SCRTAB 2 /* Cycle thru screens */
- #define UPSTROKE 8 /* Select this item */
-
- /* Max string lengths: */
- #define MAX_TITLE_LEN 80
- #define MAX_HOTKEY_LEN 40
-
- /* Some macros: */
- #define CURRENTLY_ACTIVE_SCR IntuitionBase->ActiveScreen
- #define CURRENTLY_ACTIVE_WIN IntuitionBase->ActiveWindow
- #define FIRST_AVAILABLE_SCR IntuitionBase->FirstScreen
- #define FIRST_AVAILABLE_WIN IntuitionBase->ActiveScreen->FirstWindow
-
- /* Pretty obvious :-) */
- #define JAM1 0
- #define JAM2 1
-
- /* This is a clearly impossible hotkey */
- #define DUMMY_KEY "Ctrl LAmiga RAmiga NumPad *"
-
- /* A couple of largely used macros: */
- #define xalloc(s) AllocVec(s, 0)
- #define xfree(a) FreeVec(a)
-
- #ifdef DEBUG
- #define D(x) x
- #else
- #define D(x)
- #endif
-
- #define TEMPLATE "WQ=WIN_QUAL/K,WK=WIN_KEY/K," \
- "SQ=SCR_QUAL/K,SK=SCR_KEY/K," \
- "CX_PRIORITY/N/K," \
- "COLOR1/N/K,COLOR2/N/K,"\
- "AA=AUTOACTIVATE/S" /* Not yet fully functional! */
- ///
-
- /// Globals
-
- /* The options (either CLI parameters or icon ToolTypes) */
- /* are stored in the Opt struct. Some defaults are given here: */
- const int def_pri = -5;
- const int def_color1 = 1;
- const int def_color2 = 2;
-
- struct {
- STRPTR win_qual, win_key;
- STRPTR scr_qual, scr_key;
- int *pri;
- int *color1, *color2;
- int aa; /* Not yet fully functional! */
- } Opt = {
- "LAmiga", "Tab",
- "LAmiga", "Shift Tab",
- &def_pri,
- &def_color1, &def_color2,
- 0 /* Not yet fully functional! */
- };
-
- /* These are extern since we let the compiler care for */
- /* opening library, replying WB, etc. */
- extern struct IntuitionBase *IntuitionBase;
- extern struct WBStartup *_WBMsg;
-
- /* Has our window been opened? */
- int Win_open = 0;
- int Scr_open = 0;
-
- /* Each item (either scr or win) is stored in a struct */
- /* like this. Multiple records are then linked together */
- /* in a single chain. */
-
- struct record {
- void *id;
- unsigned char title[MAX_TITLE_LEN];
- int len;
- int width;
- struct record *next;
- };
-
- struct record *First_item; /* Head of the chain */
- struct record *Special_item; /* Some magic stuff usad by the OMM */
- struct record *Displayed_item; /* The currently displayed item */
- struct record *Active_item; /* The item which was active when the user */
- /* pressed the hotkey the first time */
-
- /* Gfx Globals: all the global variable which are related to gfx */
- /* (window, rastport, fonts, etc.) are placed in a single global struct */
- struct {
- struct Window *win;
- struct RastPort *rp;
- struct TextFont *font;
- int font_height;
- int font_baseline;
- int max_width;
- int top_corner, left_corner;
- } GG = {NULL};
-
- /* OMM (Olivier's Memory Method) */
- void *Last_active_win = NULL;
- void *Last_active_scr = NULL;
-
- struct MsgPort *mp; /* the port where we will receive all the msgs */
- ULONG Sigbits; /* signals we will wait for */
-
- /* Every Cx must have an Object called a broker: */
- CxObj *Broker;
-
- /* The special filters used to know when the user releases the qualifier key. */
- /* They can't be "normal" hotkeys as they imply "upstroke", which the Cx */
- /* interface doen't recognize. */
- /* magic class code cmask qual qmask synonyms*/
- IX scr_ix = { IX_VERSION, IECLASS_RAWKEY, 0, 0xffff, 0, 0, 0 };
- IX win_ix = { IX_VERSION, IECLASS_RAWKEY, 0, 0xffff, 0, 0, 0 };
-
-
-
- ///
-
- /// Prototypes
-
- int open_window(void);
- void close_window(void);
- void display_next(void);
-
- int create_scrlist(void);
- int create_winlist(void);
- void raise_scr(void);
- void raise_win(void);
- void dispose_list(void);
-
- void handle(void);
-
- int copy_title(STRPTR, STRPTR);
- int qualcode(STRPTR);
-
- CxObj *AttachFilter(STRPTR, ULONG);
- int ShowError(STRPTR);
- ///
-
- #define BROKERVERSION "Altabber 1.5 ©1999 Mfc."
- static const STRPTR version = "$VER: Altabber 1.5 (1.4.99)";
-
- /// main()
- /* ¯¯¯¯¯¯
- */
-
- #ifdef _DCC
- __stkargs
- #endif
- void _main()
- {
- int error = 0;
-
- #ifndef FIXEDKEYS
- /* Read the arguments/tooltypes */
- struct NewRDArgs nrda = {
- TEMPLATE,
- NULL,
- NULL,
- (LONG *) &Opt,
- -1,
- TRUE,
- /* other fields = NULL */
- };
-
- error = NewReadArgs(_WBMsg, &nrda);
- #endif
-
- if (error == 0)
- {
- unsigned char win_hotkey[MAX_HOTKEY_LEN];
- unsigned char scr_hotkey[MAX_HOTKEY_LEN];
-
- #ifdef DEBUG
- Printf("win_qual=%s\n", Opt.win_qual);
- Printf("win_key=%s\n", Opt.win_key);
- Printf("scr_qual=%s\n", Opt.scr_qual);
- Printf("scr_key=%s\n", Opt.scr_key);
- Printf("pri=%ld\n", *Opt.pri);
- Printf("color1,2=%ld,%ld\n", *Opt.color1, *Opt.color2);
- Printf("aa=%ld\n", Opt.aa);
- #endif
-
- /* Try to interpret the options and to build an adequate */
- /* IX filter for every qualifier */
- win_ix.ix_Code = IECODE_UP_PREFIX | qualcode(Opt.win_qual);
- scr_ix.ix_Code = IECODE_UP_PREFIX | qualcode(Opt.scr_qual);
-
- /* Build the win hotkey as the union of the "_QUAL" option */
- /* and the "_KEY" option */
- strcpy(win_hotkey, Opt.win_qual);
- strcat(win_hotkey, " ");
- strcat(win_hotkey, Opt.win_key);
-
- /* Build the scr hotkey as the union of the "_QUAL" option */
- /* and the "_KEY" option */
- strcpy(scr_hotkey, Opt.scr_qual);
- strcat(scr_hotkey, " ");
- strcat(scr_hotkey, Opt.scr_key);
-
- #ifdef DEBUG
- Printf("win_hotkey=|%s|\n", win_hotkey);
- Printf("scr_hotkey=|%s|\n", scr_hotkey);
- Printf("win_ix.ix_Code=%lx\n", win_ix.ix_Code);
- Printf("scr_ix.ix_Code=%lx\n", scr_ix.ix_Code);
- #endif
-
- if (mp=CreateMsgPort())
- {
- /* Create the broker */
- struct NewBroker nb = {
- NB_VERSION,
- "Altabber",
- BROKERVERSION,
- "Windoze-like Alt-Tab function",
- NBU_UNIQUE | NBU_NOTIFY,
- 0, *Opt.pri, 0, 0
- };
-
- nb.nb_Port = mp;
- Sigbits = SIGBREAKF_CTRL_C | (1L << mp->mp_SigBit);
-
- if (Broker=CxBroker(&nb, &error))
- {
- /* Now we start to attach things to the broker. */
- /* First of all the two filters which intercept */
- /* the Amiga+Tab and Amiga+Shift+Tab strokes */
- if (AttachFilter(win_hotkey, WINTAB) &&
- AttachFilter(scr_hotkey, SCRTAB))
- {
- /* Add the special filter which intercept */
- /* the release of the Amiga key. To do this, */
- /* first build a normal filetr with a dummy hotkey, */
- /* then replace the dummy hotkey with the actual IX */
- CxObj *x = AttachFilter(DUMMY_KEY, UPSTROKE);
- if (x)
- {
- SetFilterIX(x, &win_ix);
-
- /* Do the same with the scr upstroke filter, */
- /* but only if it's different from the win one */
- if (scr_ix.ix_Code != win_ix.ix_Code)
- {
- x = AttachFilter(DUMMY_KEY, UPSTROKE);
- if (x)
- SetFilterIX(x, &scr_ix);
- }
- if (x)
- {
- /* If everithing's ok, activate the broker */
- ActivateCxObj(Broker, 1);
-
- handle();
- }
- else
- error = ShowError("couldn't create 2nd upstroke filter.");
- }
- else
- error = ShowError("Couldn't create upstroke filter.");
- }
- else
- error = ShowError("couldn't create filters.");
-
- /* Delete the broker (all attached objects get freed as well) */
- DeleteCxObjAll(Broker);
-
- /* Empty the msg queue by replying to all pending msgs */
- {
- struct Message *msg;
- while (msg=GetMsg(mp))
- ReplyMsg(msg);
- }
- }
- else
- {
- if (error == CBERR_DUP)
- /* Not a real error: the prg was started twice */
- /* so the second instance tells the first one to quit */
- error = 0;
- else
- error = ShowError("couldn't create broker.");
- }
-
- DeleteMsgPort(mp);
- }
- else
- error = ShowError("couldn't create message port.");
-
- #ifndef FIXEDKEYS
- NewFreeArgs(&nrda);
- #endif
- }
- else
- error = ShowError("invalid arguments.");
-
- _exit(error);
-
-
- #ifdef _DCC
- /* Dummy reference (never executed) which forces Dice to import */
- /* the WBMsg handling routines. */
- void _waitwbmsg(void);
- _waitwbmsg();
- #endif
- }
- ///
-
- /// AttachFilter()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- ** This routine adds a filter to an existing broker.
- ** Each filter is triggered by the event ginven in the "key" string.
- ** The filter in turn has attached a Translator (which "eats" the event
- ** so that it doesn't propagate to lower-priority Cx's) an a Sender
- ** (which sends a "event" msg to the msg port "mp").
- **
- ** It returns the address of the created filter.
- */
-
- CxObj *AttachFilter(STRPTR key, ULONG event)
- {
- CxObj *filter, *sender, *translator;
-
- if (filter=CxFilter(key))
- {
- AttachCxObj(Broker, filter);
-
- if (sender=CxSender(mp, event))
- {
- AttachCxObj(filter, sender);
-
- if (translator=CxTranslate(NULL))
- {
- AttachCxObj(filter, translator);
-
- if (!CxObjError(filter))
- return filter;
- }
- }
- }
-
- return NULL;
- }
- ///
-
- /// handle()
- /* ¯¯¯¯¯¯¯¯
- ** This is the kernel of the entire program. It basically consists
- ** of a classic Wait-GetMsg-ReplyMsg loop where the appropriate
- ** routines are called according to the type of message received.
- */
-
- void handle(void)
- {
- int error = 0;
-
- /* Main loop: the routines inside will set error to 1 */
- /* if the main loop is to be quitted. */
-
- while (!error)
- {
- CxMsg *msg;
- ULONG signal = Wait(Sigbits);
-
- /* We received a signal: either one or more msgs have arrived */
- /* or someone has sent us a CTRL-C */
-
- while (msg = (CxMsg*) GetMsg(mp))
- {
- /* We received a msg, so keep its type & ID */
- /* and then reply to it */
- ULONG id = CxMsgID(msg);
- ULONG type = CxMsgType(msg);
- ReplyMsg((struct Message *) msg);
-
- /* Take the appropriate action */
- /* Note that this part could be more optimized */
- switch (type)
- {
- case CXM_IEVENT: /* One of the hotkeys... */
-
- switch(id)
- {
- case SCRTAB:
- if (Win_open)
- {
- close_window();
- dispose_list();
- }
-
- if (!Scr_open)
- {
- if (create_scrlist())
- Scr_open = open_window();
- }
-
- if (Scr_open)
- {
- display_next();
- }
- break;
-
- case WINTAB:
- if (Scr_open)
- {
- close_window();
- dispose_list();
- }
-
- if (!Win_open)
- {
- if (create_winlist())
- Win_open = open_window();
- }
-
- if (Win_open)
- {
- display_next();
- }
- break;
-
- case UPSTROKE:
- /* If alright, raise the selected item */
- /* then close the window */
- if (Scr_open)
- {
- raise_scr();
- close_window();
- dispose_list();
- }
- if (Win_open)
- {
- raise_win();
- close_window();
- dispose_list();
- }
- break;
- }
-
- break;
-
- case CXM_COMMAND: /* Standard CX stuff... */
-
- switch (id)
- {
- case CXCMD_DISABLE:
- ActivateCxObj(Broker, 0);
- break;
-
- case CXCMD_ENABLE:
- ActivateCxObj(Broker, 1);
- break;
-
- case CXCMD_KILL:
- case CXCMD_UNIQUE:
- /* Quit... */
- error = 1;
- break;
- }
- break;
- }
- }
-
- /* If we received a CTRL-C, quit */
- if (signal & SIGBREAKF_CTRL_C)
- error = 1;
- }
-
- /* Before quitting, close open windows and free allocated resources */
- if (Win_open || Scr_open)
- {
- close_window();
- dispose_list();
- }
- }
- ///
-
- /// create_winlist()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- ** Examine the list of open windows and build a chain of "items"
- ** or "records". The first win in the chain must be the Special One
- ** according to the OMM (Olivier's Memory Method). The last win
- ** should be the currently active one.
- ** Windows whithout printable title are skipped.
- */
-
- int create_winlist(void)
- {
- Active_item = First_item = Special_item = Displayed_item = NULL;
-
- /* Examine all the wins, starting with the currently active one */
- struct Window *aw = CURRENTLY_ACTIVE_WIN;
- struct Window *w = aw;
-
- do {
- /* Has printable title? */
- STRPTR title = w->Title;
- if ( title && (*title != '\0') )
- {
- /* Yes: allocate space for a new record and fill it */
- struct record *item = xalloc(sizeof(struct record));
- if (item == NULL)
- break;
-
- item->len = copy_title(item->title, title);
- item->id = w;
-
- /* Is this win the Special One according to OMM? */
- if (w == Last_active_win && w != aw)
- {
- /* Yes: keep it apart */
- Special_item = item;
- }
- else
- {
- /* No: add it to the head of the chain */
- item->next = First_item;
- First_item = item;
- }
-
- /* Is this win the currently active one? */
- if (w == aw)
- Active_item = item;
- }
-
- /* Examine the next win. If we reach the end of the list */
- /* restart from the first win of this screen */
-
- w = w->NextWindow;
- if (w == NULL)
- w = FIRST_AVAILABLE_WIN;
-
- /* Stop when we cycled thru all available wins and we came back to */
- /* the firstly examined one, which happens to be the currently active one */
- } while (w != aw);
-
- /* If a Special Win was found, add it to the head of the chain, so that */
- /* it will appear first (making Olivier happy) */
- if (Special_item)
- {
- Special_item->next = First_item;
- First_item = Special_item;
- }
-
- /* Now, if we found only one win and that win is already selected */
- /* (the currently active one) then Altabber is obviously of no use, */
- /* so don't even open the window: discard the chain and return an error */
- if (First_item == Active_item)
- {
- dispose_list();
- return 0; /* means: couldn't correctly initiate the wins chain */
- }
-
- return 1; /* Ok */
- }
- ///
-
- /// create_scrlist()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- ** Examine the list of open screens and build a chain of "items"
- ** or "records". The first scr in the chain must be the Special One
- ** according to the OMM (Olivier's Memory Method). The last scr
- ** should be the currently active one.
- **
- ** Please see the comments in the create_winlist() function.
- */
-
- int create_scrlist(void)
- {
- Active_item = First_item = Special_item = Displayed_item = NULL;
-
- struct Screen *as = CURRENTLY_ACTIVE_SCR;
- struct Screen *s = as;
-
- do {
- STRPTR title = s->Title; /* Or DefaultTitle ?!? */
-
- /* Screen are never skipped, regardless of their title */
- //if ( title && (*title != '\0') )
- {
- struct record *item = xalloc(sizeof(struct record));
- if (item == NULL)
- break;
-
- /* If the scr has no printable title, a default one */
- /* is provided */
- if ( title && (*title != '\0') )
- item->len = copy_title(item->title, title);
- else
- item->len = copy_title(item->title, "<no title>");
-
- item->id = s;
-
- if (s == Last_active_scr && s != as)
- {
- Special_item = item;
- }
- else
- {
- item->next = First_item;
- First_item = item;
- }
-
- if (s == as)
- Active_item = item;
- }
-
- s = s->NextScreen;
- if (s == NULL)
- s = FIRST_AVAILABLE_SCR;
-
- } while (s != as);
-
- if (Special_item)
- {
- Special_item->next = First_item;
- First_item = Special_item;
- }
-
- if (First_item == Active_item)
- {
- dispose_list();
- return 0;
- }
-
- return 1; /* Ok */
-
- }
- ///
-
- /// open_window()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯¯
- */
-
- int open_window(void)
- {
- struct Screen *as = IntuitionBase->ActiveScreen;
- struct RastPort *tmp_rp = &as->RastPort;
-
- /* Get the screen font's height */
- GG.font_height = as->Font->ta_YSize;
-
- /* Try to determine the max window width required */
- /* by the titles in the chain */
- GG.max_width = 0;
- struct record *item = First_item;
- while (item)
- {
- int width = TextLength(tmp_rp, item->title, item->len);
- item->width = width;
- if (width > GG.max_width)
- GG.max_width = width;
-
- item = item->next;
- }
-
- /* Leave a margin of twice the font's height on both sides */
- /* and above and below the print-area */
- int winw = GG.max_width + 4 * GG.font_height;
- int winh = 5 * GG.font_height;
-
- /* Open the window on the currently active screen */
- /* with the calculated size and without any title */
- struct Window *win = OpenWindowTags(NULL,
- WA_Left, (as->Width - winw)/2,
- WA_Top, (as->Height - winh)/2,
- WA_Width, winw,
- WA_Height, winh,
- WA_CustomScreen,(ULONG) as,
- TAG_END, 0
- );
-
- if (win == NULL)
- return 0;
-
- /* Fill in the Global Gfx variables */
- GG.win = win;
- GG.rp = win->RPort;
- /* These are the coordinates of the print-area */
- /* inside the window */
- GG.left_corner = 2 * GG.font_height;
- GG.top_corner = 2 * GG.font_height;
-
- /* Set the same font as the screen's one */
- GG.font = OpenFont(as->Font);
- if (GG.font)
- {
- SetFont(GG.rp, GG.font);
- }
-
- GG.font_baseline = GG.font->tf_Baseline;
-
- /* Be sure we are writing in JAM1 mode */
- SetABPenDrMd(GG.rp, 1, 0, JAM1);
-
- return 1; /* Ok */
- }
- ///
-
- /// display_next()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- */
-
- void display_next(void)
- {
- /* Select the next item */
- if (Displayed_item)
- Displayed_item = Displayed_item->next;
-
- /* If at the end of the chain (or if called for the first time) */
- /* restart from the head of the chain */
- if (Displayed_item == NULL)
- Displayed_item = First_item;
-
- /* Clear the print-area */
- EraseRect(GG.rp,
- GG.left_corner,
- GG.top_corner,
- GG.left_corner + GG.max_width,
- GG.top_corner + GG.font_height
- );
-
- /* Center the title in the print-area */
- Move(GG.rp,
- GG.left_corner + (GG.max_width - Displayed_item->width)/2,
- GG.top_corner + GG.font_baseline
- );
-
- /* Use color2 (white) if we are showing the currently active win/scr, */
- /* color1 (black) otherwise */
- if (Displayed_item == Active_item)
- SetAPen(GG.rp, *Opt.color2);
- else
- SetAPen(GG.rp, *Opt.color1);
-
- Text(GG.rp, Displayed_item->title, Displayed_item->len);
- }
- ///
-
- /// close_window()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- ** Close the window and the font (if opened),
- ** then free all the items of the chain.
- */
-
- void close_window(void)
- {
- if (GG.font)
- {
- CloseFont(GG.font);
- GG.font = NULL;
- }
-
- if (GG.win)
- {
- CloseWindow(GG.win);
- GG.win = NULL;
- }
-
- Scr_open = Win_open = 0;
- }
- ///
-
- /// dispose_list()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- ** This routine frees all items of the chain.
- */
-
- void dispose_list(void)
- {
- struct record *item = First_item;
- while(item)
- {
- struct record *next = item->next;
- xfree(item);
- item = next;
- }
- }
- ///
-
- /// raise_win()
- /* ¯¯¯¯¯¯¯¯¯¯¯
- ** Bring the selected win to the foreground, but only if it's not
- ** the currently active one.
- */
-
- void raise_win(void)
- {
- if (Displayed_item && Displayed_item != Active_item)
- {
- Last_active_win = CURRENTLY_ACTIVE_WIN; /* OMM! */
- WindowToFront(Displayed_item->id);
- ActivateWindow(Displayed_item->id);
- }
- }
- ///
-
- /// raise_scr()
- /* ¯¯¯¯¯¯¯¯¯¯¯
- ** Bring the selected scr to the foreground, but only if it's not
- ** the currently active one. If the AUTOACTIVE flag is on, then
- ** activate the first window of the newly raised screen.
- */
-
- void raise_scr(void)
- {
- if (Displayed_item && Displayed_item != Active_item)
- {
- Last_active_scr = CURRENTLY_ACTIVE_SCR; /* OMM! */
- ScreenToFront(Displayed_item->id);
- if (Opt.aa)
- /* Not yet fully functional! */
- ActivateWindow(FIRST_AVAILABLE_WIN);
- }
- }
- ///
-
- /// copy_title()
- /* ¯¯¯¯¯¯¯¯¯¯¯¯
- ** Same as strcpy(), but ignores multiple spaces and copies
- ** only up to MAX_TITLE_LEN-1 chars (including trailing '\0').
- **
- ** Returns the number of chars copied, same as strlen(dst).
- */
-
- int copy_title(STRPTR dst, STRPTR src)
- {
- int i = 0;
-
- while ( *src && i < MAX_TITLE_LEN-1 )
- {
- if (*src == ' ')
- {
- while (*src == ' ')
- ++src;
- if (i)
- dst[i++] = ' ';
- }
- else
- {
- dst[i++] = *(src++);
- }
- }
-
- dst[i] = '\0';
-
- return i;
- }
- ///
-
- /// ShowError()
- /* ¯¯¯¯¯¯¯¯¯¯¯
- ** Prints an error message in a requester.
- */
-
- int ShowError(STRPTR err)
- {
- struct EasyStruct errmsg =
- {
- sizeof(struct EasyStruct),
- 0,
- "Altabber",
- "An error has occurred:\n%s\nQuitting.",
- "Ok"
- };
-
- EasyRequest(NULL, &errmsg, NULL, err);
-
- return 10;
- }
- ///
-
- /// qualcode()
- /* ¯¯¯¯¯¯¯¯¯¯
- ** Every "qualifier" key has a raw code associated, as any other
- ** "normal" key. Unfortunately there's no way to access to these keys
- ** as normal keys using the standard "input expression" method.
- ** So to get the correct raw code we must work it out manually
- */
-
- int qualcode(STRPTR key)
- {
- if (!stricmp(key, "ctrl")) return 0x63;
- if (!stricmp(key, "control")) return 0x63;
- if (!stricmp(key, "lalt")) return 0x64;
- if (!stricmp(key, "ralt")) return 0x65;
- if (!stricmp(key, "lamiga")) return 0x66;
- if (!stricmp(key, "ramiga")) return 0x67;
- if (!stricmp(key, "lcommand")) return 0x66;
- if (!stricmp(key, "rcommand")) return 0x67;
- if (!stricmp(key, "lshift")) return 0x60;
- if (!stricmp(key, "rshift")) return 0x61;
- if (!stricmp(key, "caps")) return 0x62;
- if (!stricmp(key, "capslock")) return 0x62;
-
- return 0;
- }
- ///
-
-
-