/* * Copyright (c) 2009-2023 Julien Nadeau Carriere * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Interface to keyboards. */ #include #include #if defined(AG_WIDGETS) && defined(AG_DEBUG) #include #include #include #endif /* #define DEBUG_KEYBOARD */ /* * Agar internal keysyms */ static const struct { AG_KeySym ks; AG_KeyCategory cat; const char *_Nonnull name; } agKeyNameTbl[] = { { AG_KEY_NONE, AG_KCAT_NONE, "None" }, { AG_KEY_BACKSPACE, AG_KCAT_CONTROL, "BackSpace" }, { AG_KEY_TAB, AG_KCAT_SPACING, "Tab" }, { AG_KEY_CLEAR, AG_KCAT_CONTROL, "Clear" }, { AG_KEY_RETURN, AG_KCAT_RETURN, "Return" }, { AG_KEY_PAUSE, AG_KCAT_CONTROL, "Pause" }, { AG_KEY_ESCAPE, AG_KCAT_CONTROL, "Escape" }, { AG_KEY_SPACE, AG_KCAT_SPACING, "Space" }, { AG_KEY_EXCLAIM, AG_KCAT_PRINT, "!" }, { AG_KEY_QUOTEDBL, AG_KCAT_PRINT, "\"" }, { AG_KEY_HASH, AG_KCAT_PRINT, "#" }, { AG_KEY_DOLLAR, AG_KCAT_PRINT, "$" }, { AG_KEY_PERCENT, AG_KCAT_PRINT, "%" }, { AG_KEY_AMPERSAND, AG_KCAT_PRINT, "&" }, { AG_KEY_QUOTE, AG_KCAT_PRINT, "'" }, { AG_KEY_LEFTPAREN, AG_KCAT_PRINT, "(" }, { AG_KEY_RIGHTPAREN, AG_KCAT_PRINT, ")" }, { AG_KEY_ASTERISK, AG_KCAT_PRINT, "*" }, { AG_KEY_PLUS, AG_KCAT_PRINT, "+" }, { AG_KEY_COMMA, AG_KCAT_PRINT, "," }, { AG_KEY_MINUS, AG_KCAT_PRINT, "-" }, { AG_KEY_PERIOD, AG_KCAT_PRINT, "." }, { AG_KEY_SLASH, AG_KCAT_PRINT, "/" }, { AG_KEY_0, AG_KCAT_PRINT, "0" }, { AG_KEY_1, AG_KCAT_PRINT, "1" }, { AG_KEY_2, AG_KCAT_PRINT, "2" }, { AG_KEY_3, AG_KCAT_PRINT, "3" }, { AG_KEY_4, AG_KCAT_PRINT, "4" }, { AG_KEY_5, AG_KCAT_PRINT, "5" }, { AG_KEY_6, AG_KCAT_PRINT, "6" }, { AG_KEY_7, AG_KCAT_PRINT, "7" }, { AG_KEY_8, AG_KCAT_PRINT, "8" }, { AG_KEY_9, AG_KCAT_PRINT, "9" }, { AG_KEY_COLON, AG_KCAT_PRINT, ":" }, { AG_KEY_SEMICOLON, AG_KCAT_PRINT, ";" }, { AG_KEY_LESS, AG_KCAT_PRINT, "<" }, { AG_KEY_EQUALS, AG_KCAT_PRINT, "=" }, { AG_KEY_GREATER, AG_KCAT_PRINT, ">" }, { AG_KEY_QUESTION, AG_KCAT_PRINT, "?" }, { AG_KEY_AT, AG_KCAT_PRINT, "@" }, { AG_KEY_LEFTBRACKET, AG_KCAT_PRINT, "[" }, { AG_KEY_BACKSLASH, AG_KCAT_PRINT, "\\" }, { AG_KEY_RIGHTBRACKET, AG_KCAT_PRINT, "]" }, { AG_KEY_CARET, AG_KCAT_PRINT, "^" }, { AG_KEY_UNDERSCORE, AG_KCAT_PRINT, "_" }, { AG_KEY_BACKQUOTE, AG_KCAT_PRINT, "`" }, { AG_KEY_A, AG_KCAT_ALPHA, "A" }, { AG_KEY_B, AG_KCAT_ALPHA, "B" }, { AG_KEY_C, AG_KCAT_ALPHA, "C" }, { AG_KEY_D, AG_KCAT_ALPHA, "D" }, { AG_KEY_E, AG_KCAT_ALPHA, "E" }, { AG_KEY_F, AG_KCAT_ALPHA, "F" }, { AG_KEY_G, AG_KCAT_ALPHA, "G" }, { AG_KEY_H, AG_KCAT_ALPHA, "H" }, { AG_KEY_I, AG_KCAT_ALPHA, "I" }, { AG_KEY_J, AG_KCAT_ALPHA, "J" }, { AG_KEY_K, AG_KCAT_ALPHA, "K" }, { AG_KEY_L, AG_KCAT_ALPHA, "L" }, { AG_KEY_M, AG_KCAT_ALPHA, "M" }, { AG_KEY_N, AG_KCAT_ALPHA, "N" }, { AG_KEY_O, AG_KCAT_ALPHA, "O" }, { AG_KEY_P, AG_KCAT_ALPHA, "P" }, { AG_KEY_Q, AG_KCAT_ALPHA, "Q" }, { AG_KEY_R, AG_KCAT_ALPHA, "R" }, { AG_KEY_S, AG_KCAT_ALPHA, "S" }, { AG_KEY_T, AG_KCAT_ALPHA, "T" }, { AG_KEY_U, AG_KCAT_ALPHA, "U" }, { AG_KEY_V, AG_KCAT_ALPHA, "V" }, { AG_KEY_W, AG_KCAT_ALPHA, "W" }, { AG_KEY_X, AG_KCAT_ALPHA, "X" }, { AG_KEY_Y, AG_KCAT_ALPHA, "Y" }, { AG_KEY_Z, AG_KCAT_ALPHA, "Z" }, { AG_KEY_DELETE, AG_KCAT_CONTROL, "Del" }, { AG_KEY_KP0, AG_KCAT_NUMBER, "Kp0" }, { AG_KEY_KP1, AG_KCAT_NUMBER, "Kp1" }, { AG_KEY_KP2, AG_KCAT_NUMBER, "Kp2" }, { AG_KEY_KP3, AG_KCAT_NUMBER, "Kp3" }, { AG_KEY_KP4, AG_KCAT_NUMBER, "Kp4" }, { AG_KEY_KP5, AG_KCAT_NUMBER, "Kp5" }, { AG_KEY_KP6, AG_KCAT_NUMBER, "Kp6" }, { AG_KEY_KP7, AG_KCAT_NUMBER, "Kp7" }, { AG_KEY_KP8, AG_KCAT_NUMBER, "Kp8" }, { AG_KEY_KP9, AG_KCAT_NUMBER, "Kp9" }, { AG_KEY_KP_PERIOD, AG_KCAT_NUMBER, "KpPeriod" }, { AG_KEY_KP_DIVIDE, AG_KCAT_NUMBER, "KpDiv" }, { AG_KEY_KP_MULTIPLY, AG_KCAT_NUMBER, "KpMult" }, { AG_KEY_KP_MINUS, AG_KCAT_NUMBER, "KpMinus" }, { AG_KEY_KP_PLUS, AG_KCAT_NUMBER, "KpPlus" }, { AG_KEY_KP_ENTER, AG_KCAT_NUMBER, "KpEnter" }, { AG_KEY_KP_EQUALS, AG_KCAT_NUMBER, "KpEquals" }, { AG_KEY_UP, AG_KCAT_DIR, "Up" }, { AG_KEY_DOWN, AG_KCAT_DIR, "Down" }, { AG_KEY_RIGHT, AG_KCAT_DIR, "Right" }, { AG_KEY_LEFT, AG_KCAT_DIR, "Left" }, { AG_KEY_INSERT, AG_KCAT_CONTROL, "Insert" }, { AG_KEY_HOME, AG_KCAT_CONTROL, "Home" }, { AG_KEY_END, AG_KCAT_CONTROL, "End" }, { AG_KEY_PAGEUP, AG_KCAT_CONTROL, "PageUp" }, { AG_KEY_PAGEDOWN, AG_KCAT_CONTROL, "PageDown" }, { AG_KEY_F1, AG_KCAT_FUNCTION, "F1" }, { AG_KEY_F2, AG_KCAT_FUNCTION, "F2" }, { AG_KEY_F3, AG_KCAT_FUNCTION, "F3" }, { AG_KEY_F4, AG_KCAT_FUNCTION, "F4" }, { AG_KEY_F5, AG_KCAT_FUNCTION, "F5" }, { AG_KEY_F6, AG_KCAT_FUNCTION, "F6" }, { AG_KEY_F7, AG_KCAT_FUNCTION, "F7" }, { AG_KEY_F8, AG_KCAT_FUNCTION, "F8" }, { AG_KEY_F9, AG_KCAT_FUNCTION, "F9" }, { AG_KEY_F10, AG_KCAT_FUNCTION, "F10" }, { AG_KEY_F11, AG_KCAT_FUNCTION, "F11" }, { AG_KEY_F12, AG_KCAT_FUNCTION, "F12" }, { AG_KEY_F13, AG_KCAT_FUNCTION, "F13" }, { AG_KEY_F14, AG_KCAT_FUNCTION, "F14" }, { AG_KEY_F15, AG_KCAT_FUNCTION, "F15" }, { AG_KEY_F16, AG_KCAT_FUNCTION, "F16" }, { AG_KEY_F17, AG_KCAT_FUNCTION, "F17" }, { AG_KEY_F18, AG_KCAT_FUNCTION, "F18" }, { AG_KEY_F19, AG_KCAT_FUNCTION, "F19" }, { AG_KEY_F20, AG_KCAT_FUNCTION, "F20" }, { AG_KEY_F21, AG_KCAT_FUNCTION, "F21" }, { AG_KEY_F22, AG_KCAT_FUNCTION, "F22" }, { AG_KEY_F23, AG_KCAT_FUNCTION, "F23" }, { AG_KEY_F24, AG_KCAT_FUNCTION, "F24" }, { AG_KEY_F25, AG_KCAT_FUNCTION, "F25" }, { AG_KEY_F26, AG_KCAT_FUNCTION, "F26" }, { AG_KEY_F27, AG_KCAT_FUNCTION, "F27" }, { AG_KEY_F28, AG_KCAT_FUNCTION, "F28" }, { AG_KEY_F29, AG_KCAT_FUNCTION, "F29" }, { AG_KEY_F30, AG_KCAT_FUNCTION, "F30" }, { AG_KEY_F31, AG_KCAT_FUNCTION, "F31" }, { AG_KEY_F32, AG_KCAT_FUNCTION, "F32" }, { AG_KEY_F33, AG_KCAT_FUNCTION, "F33" }, { AG_KEY_F34, AG_KCAT_FUNCTION, "F34" }, { AG_KEY_F35, AG_KCAT_FUNCTION, "F35" }, { AG_KEY_NUMLOCK, AG_KCAT_LOCK, "NumLock" }, { AG_KEY_CAPSLOCK, AG_KCAT_LOCK, "CapsLock" }, { AG_KEY_SCROLLOCK, AG_KCAT_LOCK, "ScrollLock" }, { AG_KEY_RSHIFT, AG_KCAT_MODIFIER, "R-Shift" }, { AG_KEY_LSHIFT, AG_KCAT_MODIFIER, "L-Shift" }, { AG_KEY_RCTRL, AG_KCAT_MODIFIER, "R-Ctrl" }, { AG_KEY_LCTRL, AG_KCAT_MODIFIER, "L-Ctrl" }, { AG_KEY_RALT, AG_KCAT_MODIFIER, "R-Alt" }, { AG_KEY_LALT, AG_KCAT_MODIFIER, "L-Alt" }, { AG_KEY_RMETA, AG_KCAT_MODIFIER, "R-Meta" }, { AG_KEY_LMETA, AG_KCAT_MODIFIER, "L-Meta" }, { AG_KEY_LSUPER, AG_KCAT_MODIFIER, "L-Super" }, { AG_KEY_RSUPER, AG_KCAT_MODIFIER, "R-Super" }, { AG_KEY_MODE, AG_KCAT_MODIFIER, "Mode" }, { AG_KEY_COMPOSE, AG_KCAT_MODIFIER, "Compose" }, { AG_KEY_HELP, AG_KCAT_FUNCTION, "Help" }, { AG_KEY_PRINT, AG_KCAT_FUNCTION, "Print" }, { AG_KEY_SYSREQ, AG_KCAT_FUNCTION, "SysReq" }, { AG_KEY_BREAK, AG_KCAT_FUNCTION, "Break" }, { AG_KEY_MENU, AG_KCAT_FUNCTION, "Menu" }, { AG_KEY_POWER, AG_KCAT_FUNCTION, "Power" }, { AG_KEY_EURO, AG_KCAT_PRINT, "Euro" }, { AG_KEY_UNDO, AG_KCAT_FUNCTION, "Undo" }, { AG_KEY_GRAVE, AG_KCAT_PRINT, "Grave" }, { AG_KEY_KP_CLEAR, AG_KCAT_CONTROL, "KpClear" }, { AG_KEY_COMMAND, AG_KCAT_FUNCTION, "Command" }, { AG_KEY_FUNCTION, AG_KCAT_FUNCTION, "Function" }, { AG_KEY_VOLUME_UP, AG_KCAT_FUNCTION, "VolumeUp" }, { AG_KEY_VOLUME_DOWN, AG_KCAT_FUNCTION, "VolumeDown" }, { AG_KEY_VOLUME_MUTE, AG_KCAT_FUNCTION, "VolumeMute" }, { AG_KEY_BEGIN, AG_KCAT_FUNCTION, "Begin" }, { AG_KEY_RESET, AG_KCAT_FUNCTION, "Reset" }, { AG_KEY_STOP, AG_KCAT_FUNCTION, "Stop" }, { AG_KEY_USER, AG_KCAT_FUNCTION, "User" }, { AG_KEY_SYSTEM, AG_KCAT_FUNCTION, "System" }, { AG_KEY_PRINT_SCREEN, AG_KCAT_FUNCTION, "PrintScreen" }, { AG_KEY_CLEAR_LINE, AG_KCAT_FUNCTION, "ClearLine" }, { AG_KEY_CLEAR_DISPLAY, AG_KCAT_FUNCTION, "ClearDisplay"}, { AG_KEY_INSERT_LINE, AG_KCAT_FUNCTION, "InsertLine" }, { AG_KEY_DELETE_LINE, AG_KCAT_FUNCTION, "DeleteLine" }, { AG_KEY_INSERT_CHAR, AG_KCAT_FUNCTION, "InsertChar" }, { AG_KEY_DELETE_CHAR, AG_KCAT_FUNCTION, "DeleteChar" }, { AG_KEY_PREV, AG_KCAT_FUNCTION, "Prev" }, { AG_KEY_NEXT, AG_KCAT_FUNCTION, "Next" }, { AG_KEY_SELECT, AG_KCAT_FUNCTION, "Select" }, { AG_KEY_EXECUTE, AG_KCAT_FUNCTION, "Execute" }, { AG_KEY_REDO, AG_KCAT_FUNCTION, "Redo" }, { AG_KEY_FIND, AG_KCAT_FUNCTION, "Find" }, { AG_KEY_MODE_SWITCH, AG_KCAT_FUNCTION, "ModeSwitch" }, { AG_KEY_NON_US_BACKSLASH, AG_KCAT_PRINT, "NonUsBackslash" }, { AG_KEY_APPLICATION, AG_KCAT_FUNCTION, "Application" }, { AG_KEY_AGAIN, AG_KCAT_FUNCTION, "Again" }, { AG_KEY_CUT, AG_KCAT_FUNCTION, "Cut" }, { AG_KEY_PASTE, AG_KCAT_FUNCTION, "Paste" }, { AG_KEY_KP_COMMA, AG_KCAT_NUMBER, "KpComma" }, { AG_KEY_KP_EQUALS_AS_400, AG_KCAT_NUMBER, "KpEqualsAs400" }, { AG_KEY_INTERNATIONAL_1, AG_KCAT_FUNCTION, "International1" }, { AG_KEY_INTERNATIONAL_2, AG_KCAT_FUNCTION, "International2" }, { AG_KEY_INTERNATIONAL_3, AG_KCAT_FUNCTION, "International3" }, { AG_KEY_INTERNATIONAL_4, AG_KCAT_FUNCTION, "International4" }, { AG_KEY_INTERNATIONAL_5, AG_KCAT_FUNCTION, "International5" }, { AG_KEY_INTERNATIONAL_6, AG_KCAT_FUNCTION, "International6" }, { AG_KEY_INTERNATIONAL_7, AG_KCAT_FUNCTION, "International7" }, { AG_KEY_INTERNATIONAL_8, AG_KCAT_FUNCTION, "International8" }, { AG_KEY_INTERNATIONAL_9, AG_KCAT_FUNCTION, "International9" }, { AG_KEY_LANGUAGE_1, AG_KCAT_FUNCTION, "Language1" }, { AG_KEY_LANGUAGE_2, AG_KCAT_FUNCTION, "Language2" }, { AG_KEY_LANGUAGE_3, AG_KCAT_FUNCTION, "Language3" }, { AG_KEY_LANGUAGE_4, AG_KCAT_FUNCTION, "Language4" }, { AG_KEY_LANGUAGE_5, AG_KCAT_FUNCTION, "Language5" }, { AG_KEY_LANGUAGE_6, AG_KCAT_FUNCTION, "Language6" }, { AG_KEY_LANGUAGE_7, AG_KCAT_FUNCTION, "Language7" }, { AG_KEY_LANGUAGE_8, AG_KCAT_FUNCTION, "Language8" }, { AG_KEY_LANGUAGE_9, AG_KCAT_FUNCTION, "Language9" }, { AG_KEY_ALT_ERASE, AG_KCAT_CONTROL, "AltErase" }, { AG_KEY_CANCEL, AG_KCAT_FUNCTION, "Cancel" }, { AG_KEY_PRIOR, AG_KCAT_FUNCTION, "Prior" }, { AG_KEY_RETURN2, AG_KCAT_FUNCTION, "Return2" }, { AG_KEY_SEPARATOR, AG_KCAT_FUNCTION, "Separator" }, { AG_KEY_OUT, AG_KCAT_FUNCTION, "Out" }, { AG_KEY_OPER, AG_KCAT_FUNCTION, "Oper" }, { AG_KEY_CLEAR_AGAIN, AG_KCAT_FUNCTION, "ClearAgain" }, { AG_KEY_CRSEL, AG_KCAT_FUNCTION, "CrSel" }, { AG_KEY_EXSEL, AG_KCAT_FUNCTION, "ExSel" }, { AG_KEY_KP_00, AG_KCAT_NUMBER, "Kp00" }, { AG_KEY_KP_000, AG_KCAT_NUMBER, "Kp000" }, { AG_KEY_THOUSANDS_SEPARATOR, AG_KCAT_PRINT, "ThousandsSep"}, { AG_KEY_DECIMALS_SEPARATOR, AG_KCAT_PRINT, "DecimalsSep" }, { AG_KEY_CURRENCY_UNIT, AG_KCAT_PRINT, "CurrencyUnit"}, { AG_KEY_KP_LEFT_PAREN, AG_KCAT_PRINT, "KpLeftParen" }, { AG_KEY_KP_RIGHT_PAREN, AG_KCAT_PRINT, "KpRightParen"}, { AG_KEY_KP_LEFT_BRACE, AG_KCAT_PRINT, "KpLeftBrace" }, { AG_KEY_KP_RIGHT_BRACE, AG_KCAT_PRINT, "KpRightBrace"}, { AG_KEY_KP_TAB, AG_KCAT_SPACING, "KpTab" }, { AG_KEY_KP_BACKSPACE, AG_KCAT_CONTROL, "KpBackspace" }, { AG_KEY_KP_A, AG_KCAT_NUMBER, "KpA" }, { AG_KEY_KP_B, AG_KCAT_NUMBER, "KpB" }, { AG_KEY_KP_C, AG_KCAT_NUMBER, "KpC" }, { AG_KEY_KP_D, AG_KCAT_NUMBER, "KpD" }, { AG_KEY_KP_E, AG_KCAT_NUMBER, "KpE" }, { AG_KEY_KP_F, AG_KCAT_NUMBER, "KpF" }, { AG_KEY_KP_XOR, AG_KCAT_FUNCTION, "KpXOR" }, { AG_KEY_KP_POWER, AG_KCAT_FUNCTION, "KpPower" }, { AG_KEY_KP_PERCENT, AG_KCAT_FUNCTION, "KpPercent" }, { AG_KEY_KP_LESS, AG_KCAT_FUNCTION, "KpLess" }, { AG_KEY_KP_GREATER, AG_KCAT_FUNCTION, "KpGreater" }, { AG_KEY_KP_AMPERSAND, AG_KCAT_PRINT, "KpAmpersand" }, { AG_KEY_KP_DBL_AMPERSAND, AG_KCAT_PRINT, "KpDblAmpersand"}, { AG_KEY_KP_VERTICAL_BAR, AG_KCAT_PRINT, "KpVerticalBar"}, { AG_KEY_KP_COLON, AG_KCAT_PRINT, "KpColon" }, { AG_KEY_KP_HASH, AG_KCAT_PRINT, "KpHash" }, { AG_KEY_KP_SPACE, AG_KCAT_SPACING, "KpSpace" }, { AG_KEY_KP_AT, AG_KCAT_PRINT, "KpAt" }, { AG_KEY_KP_EXCLAM, AG_KCAT_PRINT, "KpExclam" }, { AG_KEY_KP_MEM_STORE, AG_KCAT_FUNCTION, "KpMemStore" }, { AG_KEY_KP_MEM_RECALL, AG_KCAT_FUNCTION, "KpMemRecall" }, { AG_KEY_KP_MEM_CLEAR, AG_KCAT_FUNCTION, "KpMemClear" }, { AG_KEY_KP_MEM_ADD, AG_KCAT_FUNCTION, "KpMemAdd" }, { AG_KEY_KP_MEM_SUBTRACT, AG_KCAT_FUNCTION, "KpMemSubtract"}, { AG_KEY_KP_MEM_MULTIPLY, AG_KCAT_FUNCTION, "KpMemMultiply"}, { AG_KEY_KP_MEM_DIVIDE, AG_KCAT_FUNCTION, "KpMemDivide" }, { AG_KEY_KP_PLUS_MINUS, AG_KCAT_PRINT, "KpPlusMinus" }, { AG_KEY_KP_CLEAR_ENTRY, AG_KCAT_FUNCTION, "KpClearEntry"}, { AG_KEY_KP_BINARY, AG_KCAT_FUNCTION, "KpBinary" }, { AG_KEY_KP_OCTAL, AG_KCAT_FUNCTION, "KpOct" }, { AG_KEY_KP_DECIMAL, AG_KCAT_FUNCTION, "KpDec" }, { AG_KEY_KP_HEXADECIMAL, AG_KCAT_FUNCTION, "KpHex" }, { AG_KEY_LGUI, AG_KCAT_FUNCTION, "LeftGUI" }, { AG_KEY_RGUI, AG_KCAT_FUNCTION, "RightGUI" }, { AG_KEY_AUDIO_NEXT, AG_KCAT_FUNCTION, "AudioNext" }, { AG_KEY_AUDIO_PREV, AG_KCAT_FUNCTION, "AudioPrev" }, { AG_KEY_AUDIO_STOP, AG_KCAT_FUNCTION, "AudioStop" }, { AG_KEY_AUDIO_PLAY, AG_KCAT_FUNCTION, "AudioPlay" }, { AG_KEY_AUDIO_MUTE, AG_KCAT_FUNCTION, "AudioMute" }, { AG_KEY_MEDIA_SELECT, AG_KCAT_FUNCTION, "MediaSelect" }, { AG_KEY_WWW, AG_KCAT_FUNCTION, "WWW" }, { AG_KEY_MAIL, AG_KCAT_FUNCTION, "Mail" }, { AG_KEY_CALCULATOR, AG_KCAT_FUNCTION, "Calculator" }, { AG_KEY_COMPUTER, AG_KCAT_FUNCTION, "Computer" }, { AG_KEY_AC_SEARCH, AG_KCAT_FUNCTION, "AcSearch" }, { AG_KEY_AC_HOME, AG_KCAT_FUNCTION, "AcHome" }, { AG_KEY_AC_BACK, AG_KCAT_FUNCTION, "AcBack" }, { AG_KEY_AC_FORWARD, AG_KCAT_FUNCTION, "AcForward" }, { AG_KEY_AC_STOP, AG_KCAT_FUNCTION, "AcStop" }, { AG_KEY_AC_REFRESH, AG_KCAT_FUNCTION, "AcRefresh" }, { AG_KEY_AC_BOOKMARKS, AG_KCAT_FUNCTION, "AcBookmarks" }, { AG_KEY_AUDIO_REWIND, AG_KCAT_FUNCTION, "AudioRewind" }, { AG_KEY_AUDIO_FASTFORWARD, AG_KCAT_FUNCTION, "AudioFastFwd"}, }; static const Uint agKeyNameTblSize = sizeof(agKeyNameTbl) / sizeof(agKeyNameTbl[0]); AG_Keyboard * AG_KeyboardNew(void *drv, const char *desc) { AG_Keyboard *kbd; AG_OBJECT_ISA(drv, "AG_Driver:*"); if ((kbd = TryMalloc(sizeof(AG_Keyboard))) == NULL) { return (NULL); } AG_ObjectInit(kbd, &agKeyboardClass); AGINPUTDEVICE(kbd)->drv = drv; if ((AGINPUTDEVICE(kbd)->desc = TryStrdup(desc)) == NULL) { goto fail; } AGDRIVER(drv)->kbd = kbd; AG_ObjectAttach(&agInputDevices, kbd); return (kbd); fail: AG_ObjectDestroy(kbd); return (NULL); } static void Init(void *_Nonnull obj) { AG_Keyboard *kbd = obj; OBJECT(kbd)->flags |= AG_OBJECT_NAME_ONATTACH; kbd->keyCount = AG_KEY_LAST; kbd->keyState = Malloc(kbd->keyCount*sizeof(int)); memset(kbd->keyState, 0, kbd->keyCount*sizeof(int)); kbd->modState = AG_KEYMOD_NONE; } static void Destroy(void *_Nonnull obj) { AG_Keyboard *kbd = obj; free(kbd->keyState); } /* * Update Agar's internal keyboard state following a key press/release * event. Drivers are only required to update this state for keys in the * AG_KEY_ASCII_START to AG_KEY_ASCII_END range, as well as modifier keys. */ void AG_KeyboardUpdate(AG_Keyboard *kbd, AG_KeyboardAction action, AG_KeySym ks) { Uint ms = kbd->modState; if (ks >= kbd->keyCount) /* Ignore bad keysym */ return; /* Update the keyboard state. */ switch (action) { case AG_KEY_PRESSED: switch (ks) { case AG_KEY_NONE: return; case AG_KEY_NUMLOCK: ms ^= AG_KEYMOD_NUMLOCK; if ((ms & AG_KEYMOD_NUMLOCK) == 0) { action = AG_KEY_RELEASED; } break; case AG_KEY_CAPSLOCK: ms ^= AG_KEYMOD_CAPSLOCK; if ((ms & AG_KEYMOD_CAPSLOCK) == 0) { action = AG_KEY_RELEASED; } break; case AG_KEY_LCTRL: ms |= AG_KEYMOD_LCTRL; break; case AG_KEY_RCTRL: ms |= AG_KEYMOD_RCTRL; break; case AG_KEY_LSHIFT: ms |= AG_KEYMOD_LSHIFT; break; case AG_KEY_RSHIFT: ms |= AG_KEYMOD_RSHIFT; break; case AG_KEY_LALT: ms |= AG_KEYMOD_LALT; break; case AG_KEY_RALT: ms |= AG_KEYMOD_RALT; break; case AG_KEY_LMETA: ms |= AG_KEYMOD_LMETA; break; case AG_KEY_RMETA: ms |= AG_KEYMOD_RMETA; break; case AG_KEY_MODE: ms |= AG_KEYMOD_MODE; break; default: break; } break; case AG_KEY_RELEASED: switch (ks) { case AG_KEY_NONE: case AG_KEY_NUMLOCK: case AG_KEY_CAPSLOCK: return; case AG_KEY_LCTRL: ms &= ~AG_KEYMOD_LCTRL; break; case AG_KEY_RCTRL: ms &= ~AG_KEYMOD_RCTRL; break; case AG_KEY_LSHIFT: ms &= ~AG_KEYMOD_LSHIFT; break; case AG_KEY_RSHIFT: ms &= ~AG_KEYMOD_RSHIFT; break; case AG_KEY_LALT: ms &= ~AG_KEYMOD_LALT; break; case AG_KEY_RALT: ms &= ~AG_KEYMOD_RALT; break; case AG_KEY_LMETA: ms &= ~AG_KEYMOD_LMETA; break; case AG_KEY_RMETA: ms &= ~AG_KEYMOD_RMETA; break; case AG_KEY_MODE: ms &= ~AG_KEYMOD_MODE; break; default: break; } break; default: return; } kbd->modState = ms; kbd->keyState[ks] = (int)action; } /* Post a key-up event to widgets with the UNFOCUSED_KEYUP flag set. */ static void PostUnfocusedKeyUp(AG_Widget *_Nonnull wid, AG_KeySym ks, AG_KeyMod kmod, AG_Char ch) { AG_Widget *cwid; AG_ObjectLock(wid); if (wid->flags & AG_WIDGET_UNFOCUSED_KEYUP) { if (WIDGET_OPS(wid)->key_up != NULL) { WIDGET_OPS(wid)->key_up(wid, ks, kmod, ch); } else { #ifdef AG_UNICODE AG_PostEvent(wid, "key-up", "%i(key),%i(mod),%lu(ch)", (int)ks, (int)kmod, (Ulong)ch); #else AG_PostEvent(wid, "key-up", "%i(key),%i(mod),%u(ch)", (int)ks, (int)kmod, (Uint)ch); #endif } } OBJECT_FOREACH_CHILD(cwid, wid, ag_widget) PostUnfocusedKeyUp(cwid, ks, kmod, ch); AG_ObjectUnlock(wid); } /* Post a key-down event to widgets with the UNFOCUSED_KEYDOWN flag set. */ static void PostUnfocusedKeyDown(AG_Widget *_Nonnull wid, AG_KeySym ks, AG_KeyMod kmod, AG_Char ch) { AG_Widget *cwid; AG_ObjectLock(wid); if (wid->flags & AG_WIDGET_UNFOCUSED_KEYDOWN) { if (WIDGET_OPS(wid)->key_down != NULL) { WIDGET_OPS(wid)->key_down(wid, ks, kmod, ch); } else { #ifdef AG_UNICODE AG_PostEvent(wid, "key-down", "%i(key),%i(mod),%lu(ch)", (int)ks, (int)kmod, (Ulong)ch); #else AG_PostEvent(wid, "key-down", "%i(key),%i(mod),%u(ch)", (int)ks, (int)kmod, (Uint)ch); #endif } } OBJECT_FOREACH_CHILD(cwid, wid, ag_widget) PostUnfocusedKeyDown(cwid, ks, kmod, ch); AG_ObjectUnlock(wid); } /* * Process a key press or key release action, sending key-down/key-up * events to the appropriate Agar widget(s). This function is usually * invoked from the ProcessEvent() routine of a driver. * * Unicode characters without related keysym (or vice-versa) are allowed. * The ks argument can be AG_KEY_NONE, or the ch argument can be 0. * * The agDrivers VFS must be locked. */ int AG_ProcessKey(AG_Keyboard *kbd, AG_Window *win, AG_KeyboardAction action, AG_KeySym ks, AG_Char ch) { AG_Driver *drv = AGINPUTDEVICE(kbd)->drv; AG_Widget *wFoc; const AG_KeyMod modState = kbd->modState; int tabCycle; #ifdef DEBUG_KEYBOARD Debug(kbd, "ProcessKey(%s,%s,%s,0x%x)\n", OBJECT(win)->name, (action == AG_KEY_PRESSED) ? "Down" : "Up", AG_LookupKeyName(ks), ch); #endif switch (action) { case AG_KEY_RELEASED: PostUnfocusedKeyUp(WIDGET(win), ks, modState, ch); break; case AG_KEY_PRESSED: if (AG_ExecGlobalKeys(ks, modState)) { return (1); /* Break out of Window loop */ } PostUnfocusedKeyDown(WIDGET(win), ks, modState, ch); break; } tabCycle = 1; if (AG_WindowIsFocused(win) && /* Focused widget? */ (wFoc = AG_WidgetFindFocused(win)) != NULL) { AG_ObjectLock(wFoc); if (ks != AG_KEY_TAB || (wFoc->flags & AG_WIDGET_CATCH_TAB)) { if (wFoc->flags & AG_WIDGET_CATCH_TAB) tabCycle = 0; if (action == AG_KEY_RELEASED) { /* Key Released */ if (WIDGET_OPS(wFoc)->key_up != NULL) { WIDGET_OPS(wFoc)->key_up(wFoc, ks, modState, ch); } else { #ifdef AG_UNICODE AG_PostEvent(wFoc, "key-up", "%i(key),%i(mod),%lu(ch)", (int)ks, (int)modState, (Ulong)ch); #else AG_PostEvent(wFoc, "key-up", "%i(key),%i(mod),%u(ch)", (int)ks, (int)modState, (Uint)ch); #endif } } else { /* Key Pressed */ if (WIDGET_OPS(wFoc)->key_down != NULL) { WIDGET_OPS(wFoc)->key_down(wFoc, ks, modState, ch); } else { #ifdef AG_UNICODE AG_PostEvent(wFoc, "key-down", "%i(key),%i(mod),%lu(ch)", (int)ks, (int)modState, (Ulong)ch); #else AG_PostEvent(wFoc, "key-down", "%i(key),%i(mod),%u(ch)", (int)ks, (int)modState, (Uint)ch); #endif } } if (AGDRIVER_SINGLE(drv)) { /* * Once this key is released, ensure the key-up * event will be delivered to this window (and * not the newly focused one). Prevents issues * where key-down event handlers cause changes * in window focus. */ AGDRIVERSW(drv)->winLastKeydown = win; } } AG_ObjectUnlock(wFoc); } if (AGDRIVER_MULTIPLE(drv) && tabCycle && /* Focus move */ (ks == AG_KEY_TAB && action == AG_KEY_RELEASED)) { AG_WindowCycleFocus(win, (modState & AG_KEYMOD_SHIFT) ? 1 : 0); /* Break out of Window loop */ return (1); } return (0); } /* Return a string describing a given Agar keysym. */ const char * AG_LookupKeyName(AG_KeySym ks) { int i; for (i = 0; i < agKeyNameTblSize; i++) { if (agKeyNameTbl[i].ks == ks) { return (agKeyNameTbl[i].name); } } return (NULL); } /* Return a string describing a given Agar keymod. */ char * AG_LookupKeyMod(AG_KeyMod kmod) { char buf[32]; int nMods=0; if (kmod & AG_KEYMOD_CTRL_SHIFT) { Strlcpy(buf, "Ctrl-Shift", sizeof(buf)); nMods++; } else if (kmod & AG_KEYMOD_CTRL_ALT) { Strlcpy(buf, "Ctrl-Alt", sizeof(buf)); nMods++; } else { if (kmod & AG_KEYMOD_CTRL) { if ((kmod & AG_KEYMOD_LCTRL) && (kmod & AG_KEYMOD_RCTRL)==0) { Strlcpy(buf, "LCtrl", sizeof(buf)); } else if ((kmod & AG_KEYMOD_LCTRL)==0 && (kmod & AG_KEYMOD_RCTRL)) { Strlcpy(buf, "RCtrl", sizeof(buf)); } else { Strlcpy(buf, "Ctrl", sizeof(buf)); } nMods++; } else if (kmod & AG_KEYMOD_META) { if ((kmod & AG_KEYMOD_LMETA) && (kmod & AG_KEYMOD_RMETA)==0) { Strlcpy(buf, "LMeta", sizeof(buf)); } else if ((kmod & AG_KEYMOD_LMETA)==0 && (kmod & AG_KEYMOD_RMETA)) { Strlcpy(buf, "RMeta", sizeof(buf)); } else { Strlcpy(buf, "Meta", sizeof(buf)); } nMods++; } else { buf[0] = '\0'; } if ((kmod & AG_KEYMOD_ALT) && ((kmod & AG_KEYMOD_CTRL_ALT) == 0)) { if (nMods > 0) { Strlcat(buf, "-", sizeof(buf)); nMods = 0; } if ((kmod & AG_KEYMOD_LALT) && (kmod & AG_KEYMOD_RALT)==0) { Strlcat(buf, "LAlt", sizeof(buf)); } else if ((kmod & AG_KEYMOD_LALT)==0 && (kmod & AG_KEYMOD_RALT)) { Strlcat(buf, "RAlt", sizeof(buf)); } else { Strlcat(buf, "Alt", sizeof(buf)); } nMods++; } if (kmod & AG_KEYMOD_SHIFT) { if (nMods > 0) { Strlcat(buf, "-", sizeof(buf)); nMods--; } if ((kmod & AG_KEYMOD_LSHIFT) && (kmod & AG_KEYMOD_RSHIFT)==0) { Strlcat(buf, "LShift", sizeof(buf)); } else if ((kmod & AG_KEYMOD_LSHIFT)==0 && (kmod & AG_KEYMOD_RSHIFT)) { Strlcat(buf, "RShift", sizeof(buf)); } else { Strlcat(buf, "Shift", sizeof(buf)); } nMods++; } } #if 0 if (kmod & AG_KEYMOD_MODE) { if (nMods > 0) { Strlcat(buf, "-", sizeof(buf)); } Strlcat(buf, "Mode", sizeof(buf)); } else if (kmod & AG_KEYMOD_NUMLOCK) { if (nMods > 0) { Strlcat(buf, "-", sizeof(buf)); } Strlcat(buf, "NumLock", sizeof(buf)); } else if (kmod & AG_KEYMOD_CAPSLOCK) { if (nMods > 0) { Strlcat(buf, "-", sizeof(buf)); } Strlcat(buf, "CapsLock", sizeof(buf)); } #endif return AG_Strdup(buf); } /* Lookup an Agar keysym by name. */ AG_KeySym AG_LookupKeySym(const char *name) { int i; for (i = 0; i < agKeyNameTblSize; i++) { if (strcmp(agKeyNameTbl[i].name,name) == 0) { return (agKeyNameTbl[i].ks); } } return (AG_KEY_NONE); } /* Compare unsided modifier state against a string of flags. */ int AG_CompareKeyMods(Uint modState, const char *flags) { const char *c; for (c = &flags[0]; *c != '\0'; c++) { switch (*c) { case 'C': if (modState & AG_KEYMOD_LCTRL || modState & AG_KEYMOD_RCTRL) { return (1); } break; case 'A': if (modState & AG_KEYMOD_LALT || modState & AG_KEYMOD_RALT) { return (1); } break; case 'S': if (modState & AG_KEYMOD_LSHIFT || modState & AG_KEYMOD_RSHIFT) { return (1); } break; case 'M': if (modState & AG_KEYMOD_LMETA || modState & AG_KEYMOD_RMETA) { return (1); } break; } } return (0); } #if defined(AG_WIDGETS) && defined(AG_DEBUG) static void PollKeyboardState(AG_Event *_Nonnull event) { AG_Tlist *tl = AG_TLIST_SELF(); AG_Keyboard *kbd = AG_PTR(1); AG_TlistItem *it; Uint i; if (!AG_OBJECT_VALID(kbd) || !AG_KEYBOARD_ISA(kbd)) return; AG_TlistBegin(tl); for (i = 0; i < kbd->keyCount; i++) { const char *keyName; if (kbd->keyState[i] == 0) { continue; } if ((keyName = AG_LookupKeyName(i)) != NULL) { it = AG_TlistAddS(tl, NULL, keyName); } else { it = AG_TlistAdd(tl, NULL, _("Unknown Key %d"), i); } it->p1 = &kbd->keyState[i]; } AG_TlistEnd(tl); } static void * Edit(void *obj) { AG_Keyboard *kbd = obj; AG_Box *box; AG_Label *lbl; AG_Tlist *tl; box = AG_BoxNewVert(NULL, AG_BOX_EXPAND); lbl = AG_LabelNewS(box, 0, AGINPUTDEVICE(kbd)->desc); AG_SetFontFamily(lbl, "league-spartan"); AG_SetFontSize(lbl, "130%"); lbl = AG_LabelNewPolledMT(box, AG_LABEL_HFILL, &OBJECT(kbd)->lock, _("Key Count: %u\n" "Modifier State: 0x%x"), &kbd->keyCount, &kbd->modState); AG_RedrawOnTick(lbl, 120); tl = AG_TlistNewPolled(box, AG_TLIST_EXPAND, PollKeyboardState,"%p", kbd); AG_SetFontWeight(tl, "bold"); AG_SetFontSize(tl, "120%"); AG_TlistSizeHint(tl, "", 10); AG_TlistSetRefresh(tl, 120); return (box); } #endif /* AG_WIDGETS and AG_DEBUG */ AG_ObjectClass agKeyboardClass = { "Agar(InputDevice:Keyboard)", sizeof(AG_Keyboard), { 1,0, AGC_KEYBOARD, 0xE020 }, Init, NULL, /* reset */ Destroy, NULL, /* load */ NULL, /* save */ #if defined(AG_WIDGETS) && defined(AG_DEBUG) Edit #else NULL /* edit */ #endif };