Issue with detecting whether the shift key is pressed and released - c

Im developing a keyboard driver and it works however I'm also
implementing capital letters. Im trying to set a flag for when the shift key
is being pressed or not so the OS will know when to switch to a new scancode.
It can detect if the shift key is being pressed but sadly NOT when the shift
key is released.
Heres my code:
static void keyboard_callback(registers_t *regs) {
uint8_t scancode = port_byte_in(0x60);
// return scancode_to_shift[scancode];
// scancode_to_captial[scancode];
unsigned char c = 0;
if (scancode > SC_MAX) return;
// If shift is pressed.
if (scancode == 0x2A)
{
shift_hold = true;
print_string("Pressed!");
} if (scancode == 0xAA) {
shift_hold = false;
print_string("Worked!");
}
/*
if (scancode == BACKSPACE) {
if (backspace(key_buffer) == true) {
print_backspace();
}
} else if (scancode == ENTER) {
print_string("\n");
execute_command(key_buffer);
key_buffer[0] = '\0';
} else {
if (shift_hold == true)
{
char letter = scancode_to_shift[(int) scancode];
append(key_buffer, letter);
char str[2] = {letter, '\0'};
print_string(str);
}
else if (shift_hold == false)
{
char letter = scancode_to_char[(int) scancode];
append(key_buffer, letter);
char str[2] = {letter, '\0'};
print_string(str);
}
}
*/
}
`
Unless im wrong 0xAA should be the right code for referencing to the right shift key unreleasing.
I tried googling this issue and there is not a lot of information on this sadly..
And other source code for keyboard drivers seem a little too "complex" for me to implement.

Im trying to set a flag for when the shift key is being pressed or not so the OS will know when to switch to a new scancode.
There are 2 shift keys. You need 2 flags (maybe isLeftShiftDown and isRightShiftDown) that are ORed together (like "isAnyShiftDown = isLeftShiftDown | isRightShiftDown") to be able to handle sequences like "left shift pressed, right shift pressed, left shift released" properly.
There is also capslock state; which (for letters and not other keys) makes it more like "isCapital = (isLeftShiftDown | isRightShiftDown) ^ capsLockState". For other keys (e.g. "1" vs. "!") the capslock is ignored.
Then there's "control" and "alt" (and number lock) - e.g. "control+alt+shift+A" is not considered a printable character even though "shift+A" by itself would be.
I tried googling this issue and there is not a lot of information on this sadly..
Try https://wiki.osdev.org/PS/2_Keyboard#Driver_Model .
And other source code for keyboard drivers seem a little too "complex" for me to implement.
Unfortunately (like many things) there's a certain amount of required complexity needed to make it work properly.

Related

How can I convert a vk constant to text?

I'm trying to translate vk constants (possibly joined together) to their names (e.g., 81 (VK_CONTROL|VK_SHIFT|VK_A) = β€œControl+Shift+A.”
I tried using the GetKeyNameText function (both with vk and scancode constants), but it does not seem to work (the string is blank, it returns 0, and GetLastError returns 0 as well).
As far as I know, there is no API function that directly converts the combined virtual keys into text. For non-character keys (VK_CONTROL, VK_SHIFT, etc.), you need to manually concatenate strings.
For character keys, MapVirtualKey works fine for me.
I tested the code you provided:
UINT t = MapVirtualKey(VK_A, MAPVK_VK_TO_CHAR);
I can get its return value:
Filter out the control and shift with
char keybuff[64] = {0};
if (keys & VK_CONTROL) {
strcat(keybuff, "Control+");
keys &= ~VK_CONTROL; // turn bits OFF
}
if (keys & VK_SHIFT) {
strcat(keybuff, "Shift+");
keys &= ~VK_SHIFT;
}
And use MapVirtualKey to map the VK code to a character
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeya
So in the following case key becomes 65 which is the ascii value for capital 'A'. Cast it to a char and you have the letter. This works for all codes below ascii 127.
UINT key = MapVirtualKey(VK_A, MAPVK_VK_TO_CHAR);
char c = (char)key; // 'A'
I advise you not to use GetKeyNameText API at all since this an outdated shit that is broken for non-US layouts. Create and use some lookup table youself instead. :)
If you still decide to use it then proper call of GetKeyNameText is tricky:
// Get keyboard layout specific localized key name
std::string GetKeyNameTextWrapper(uint16_t scanCode)
{
wchar_t name[128];
int charCount = 0;
// GetKeyNameText is not working for these keys
// due to use of broken MAPVK_VK_TO_CHAR under the hood
// See https://stackoverflow.com/a/72464584/1795050
const uint16_t vkCode = LOWORD(::MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX));
if ((vkCode >= (uint16_t)'A') && (vkCode <= (uint16_t)'Z'))
{
const uint32_t flags = 1 << 2; // Do not change keyboard state of this thread
static uint8_t state[256] = { 0 };
// This call can produce multiple UTF-16 code points
// in case of ligatures or non-BMP Unicode chars that have hi and low surrogate
// See examples: https://kbdlayout.info/features/ligatures
charCount = ::ToUnicode(vkCode, scanCode, state, name, 128, flags);
// negative value is returned on dead key press
if (charCount < 0)
charCount = -charCount;
}
else
{
const LONG lParam = MAKELONG(0, ((scanCode & 0xff00) != 0 ? KF_EXTENDED : 0) | (scanCode & 0xff));
charCount = ::GetKeyNameTextW(lParam, name, 128);
}
return utf8::narrow(name, charCount);
}
Also note that GetKeyNameText will return different string according to string data that is sewn into keyboard layout dll. And that layout dlls may have bugs.

C xtest emitting key presses for every Unicode character

I wanted to make a program to simulate key presses. I think i am mostly done but i have done something wrong i guess because it is not doing what i expect it to do. I have made a small example program to illustrate the issue. The main problem is that if i want to generate capital letters it does not work with strings like 'zZ'. It is generating only small letters 'zz'. Although symbols like '! $ & _ >' etc. work fine (that require shift on my German keyboard layout) and even multi byte ones like 'πŸ’£' . What i am doing is this:
preamble:
So basically the main problem by emulating key presses is first the layout that changes from user to user and most importantly modifier keys. So if you go the naive route and get a keysym with XStringToKeysym() get a keycode from that keysym with XKeysymToKeycode() and fire that event its not working like most 'newcomers' would expect (like me). The problem here is, that multiple keysyms are mapped to the same keycode. Like the keysysm for 'a' and 'A' are mapped to the same keycode because they're on the same physikal button on your keyboard that is linked to that keycode. So if you go the route from above you end up with the same keycode although the keysyms are different but mapped to the same button/keycode. And there is usually no way around this because it is not clear how the 'A' came to existence in the first place. shift+a or caps+a or you have a fancy keyboard with an 'a' and 'A' button on it. The other problem is how do i emit key presses for buttons that are not even on the keyboard of that person running that application. Like what key is pressed on an english layout if i want to type a 'Γ„' (german umlaut). This does not work because XKeysymToKeycode() will not return a proper keycode for this because there is no keysym mapping for it with that layout.
my approach:
What i am tying to do to circumvent this is finding a keycode that is not being used. You have 255-8 keycodes at your disposal but a regular keyboard has only ~110 keys on it so there is usually some space left. I am trying to find one of those keycodes that are unmapped on the current layout and use it to assign my own keysyms on it. Then i get a keysym from my char i got by iterating over my string and pass it to XStringToKeysym() which gives me the appropriate keysym. In case of β€™πŸ’£β€™ that is in most cases not mapped to any keyboard layout i know of. So i map it to the unused keycode and press it with XTestFakeKeyEvent() and repeat that for every char in the string. This works great with all fancy glyph one can think of but it does not work with simple letters and i really don't know why :( in my debugging sessions keysyms and keycodes seem to be correct its just that XTestFakeKeyEvent() does not do the right things in that case. Its possible that i fucked something up at the keymapping part but i am not really sure whats the problem here and i hope someone has a good idea and can help me find a way to a working solution.
I am just using this unicode notation in the strings array because i don't want to deal with this in the example here. Just assume there is code producing this from an arbitrary input string.
be aware that the code below can ruin your keymapping in such a way that you're not able to type and use your keyboard anymore and need to restart your X-Server/PC ... i hope it does not in its current state (working fine here) just be aware if you fiddle with the code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <unistd.h>
//gcc -g enigo2.c -lXtst -lX11
int main(int argc, char *argv[])
{
Display *dpy;
dpy = XOpenDisplay(NULL);
//my test string already transformed into unicode
//ready to be consumed by XStringToKeysym
const char *strings[] = {
"U1f4a3",// πŸ’£
"U007A", //z
"U005A", //Z
"U002f", //'/'
"U005D", //]
"U003a", //:
"U002a", //*
"U0020", //' '
"U0079", //y
"U0059", //Y
"U0020", //' '
"U0031", //1
"U0021", //!
"U0020", //' '
"U0036", //6
"U0026", //&
"U0020", //' '
"U0034", //4
"U0024", //$
"U0020", //' '
"U002D", //-
"U005F", //_
"U0020", //' '
"U003C", //<
"U003E", //>
"U0063", //c
"U0043", //C
"U006f", //o
"U004f", //O
"U00e4", //Γ€
"U00c4", //Γ„
"U00fc", //ΓΌ
"U00dc", //Ü
};
KeySym *keysyms = NULL;
int keysyms_per_keycode = 0;
int scratch_keycode = 0; // Scratch space for temporary keycode bindings
int keycode_low, keycode_high;
//get the range of keycodes usually from 8 - 255
XDisplayKeycodes(dpy, &keycode_low, &keycode_high);
//get all the mapped keysyms available
keysyms = XGetKeyboardMapping(
dpy,
keycode_low,
keycode_high - keycode_low,
&keysyms_per_keycode);
//find unused keycode for unmapped keysyms so we can
//hook up our own keycode and map every keysym on it
//so we just need to 'click' our once unmapped keycode
int i;
for (i = keycode_low; i <= keycode_high; i++)
{
int j = 0;
int key_is_empty = 1;
for (j = 0; j < keysyms_per_keycode; j++)
{
int symindex = (i - keycode_low) * keysyms_per_keycode + j;
// test for debugging to looking at those value
// KeySym sym_at_index = keysyms[symindex];
// char *symname;
// symname = XKeysymToString(keysyms[symindex]);
if(keysyms[symindex] != 0) {
key_is_empty = 0;
} else {
break;
}
}
if(key_is_empty) {
scratch_keycode = i;
break;
}
}
XFree(keysyms);
XFlush(dpy);
usleep(200 * 1000);
int arraysize = 33;
for (int i = 0; i < arraysize; i++)
{
//find the keysym for the given unicode char
//map that keysym to our previous unmapped keycode
//click that keycode/'button' with our keysym on it
KeySym sym = XStringToKeysym(strings[i]);
KeySym keysym_list[] = { sym };
XChangeKeyboardMapping(dpy, scratch_keycode, 1, keysym_list, 1);
KeyCode code = scratch_keycode;
usleep(90 * 1000);
XTestFakeKeyEvent(dpy, code, True, 0);
XFlush(dpy);
usleep(90 * 1000);
XTestFakeKeyEvent(dpy, code, False, 0);
XFlush(dpy);
}
//revert scratch keycode
{
KeySym keysym_list[] = { 0 };
XChangeKeyboardMapping(dpy, scratch_keycode, 1, keysym_list, 1);
}
usleep(100 * 1000);
XCloseDisplay(dpy);
return 0;
}
When you send a single keysym for a given keycode to XChangeKeyboardMapping and it is a letter, it automatically fills correct upper and lower case equivalents for shift and capslock modifiers. That is, after
XChangeKeyboardMapping(dpy, scratch_keycode, 1, &keysym, 1);
the keycode map for scratch_keycode effectively changes (on my machine) to
tolower(keysym), toupper(keysym), tolower(keysym), toupper(keysym), tolower(keysym), toupper(keysym), 0, 0, 0, 0, ...
In order to inhibit this behaviour, send 2 identical keysyms per keycode:
KeySym keysym_list[2] = { sym, sym };
XChangeKeyboardMapping(dpy, scratch_keycode, 2, keysym_list, 1);
This will fill both shifted and unshifted positions with the same keysym.

Why does an empty printf allow me to continue reading data from the stdin?

CODE
while (1)
{
keycode = key_hook();
if (keycode == SPACE || keycode == BKSPACE)
{
render_again = 1;
}
if (keycode == ESC)
break;
if (render_again)
{
render_again = 0;
render(all);
}
dprintf(1, ""); //I have no idea why this prevents the program from freezing
}
int key_hook()
{
char buffer[4];
read(0, buffer, 4);
return (*(unsigned int *)buffer);
}
Alright, so this piece of code handles redrawing of text on screen. Some rows of text are underlined or highlighted using termcaps (tputs(tgetstr("us, NULL")......). Everything prints fine but after the first redraw of the text the while apparently freezes unless a dprintf/printf is present. The key_hook function just reads 4 bytes from the stdin and converts them to an int.
When I last did work here, my version of key_hook had a loop of single byte reads. This was broken by an alarm of 1 second and logic to whether the data so far was a key prefix.
The alarm interrupted the read, and stopped freeze

Issues with repeated key checking with getch()

I am having issues with repeating key checking using a function that utilizes getch().
Here is a code example:
static char g_keybuffer[256];
_Bool IsKeyDown(char c)
{
char ch;
if(kbhit())
ch = getch();
if(ch == -32 || ch == 224)
{
ch = getch();
}
g_keybuffer[ch] = 1;
if(g_keybuffer[c] == 1)
{
g_keybuffer[c] = 0;
return 1;
}
return 0;
}
/*
*
*/
int main(int argc, char** argv) {
while(1)
{
if(IsKeyDown('a'))
{
printf("Test\n");
}
if(IsKeyDown('a'))
{
printf("Hello\n");
}
else if(IsKeyDown('b'))
{
printf("World\n");
}
Sleep(100);
}
return (EXIT_SUCCESS);
}
I know why the problem occurs. When a key is pressed, kbhit is true once per loop, and sets ch to the character retrieved from the buffer. When IsKeyDown is used, if it is equal to the parameter, the key in the buffer g_keybuffer is set equal to zero to avoid having a key be "down" infinitely. The problem with this is if you want to check if the same key is down more than once, only the first instance of IsKeyDown will be ran, with the rest being invalid due to the g_keybuffer of the key now being 0.
Does anyone know how I can change IsKeyDown to give it the ability to check the same key multiple times per looping? I'm stuck.
Your problem is because you are setting g_keybuffer[c] to 0 after you get a hit for the key state. I'm guessing you have done this to avoid getting the same result twice - but that is just a workaround. The only way to do what you want to do properly is to choose a library that is actually made to capture the keyboard state.
Most graphics libraries have functions for capturing keyboard states. I don't know of any solutions thought that don't involve a little overhead if you are just writing a small program.

Can't Read (!##$...and Capital Letters) from console with ReadConsoleInput

have wrote this app which reads input from console.
for(; ; )
{
GetNumberOfConsoleInputEvents(stdinInput, &numEvents);
if (numEvents != 0) {
INPUT_RECORD eventBuffer;
ReadConsoleInput(stdinInput, &eventBuffer, 1, &numEventsRead);
if (eventBuffer.EventType == KEY_EVENT) {
if(eventBuffer.Event.KeyEvent.bKeyDown)
{
printf("%c",eventBuffer.Event.KeyEvent.uChar.AsciiChar);
dataBuffer[bufferLen++] = eventBuffer.Event.KeyEvent.uChar.AsciiChar;
dataBuffer[bufferLen] = '\0';
if ( dataBuffer[bufferLen] == 99 || eventBuffer.Event.KeyEvent.uChar.AsciiChar == '\r' ) {
printf("User Wrote: %s\n",dataBuffer);
memset(dataBuffer,0,sizeof(dataBuffer));
bufferLen = 0;
}
}
}
}
}
It puts the data on a buffer and then it prints out the buffer. The problem occurs when im using Shift or CapsLock to write Capital letters or ! # # $ % characters. Then it prints out NOTHING.
Ive tried something with the VK_LSHIFT code but didn't worked.
Also if try to write something in other language than English it prints out something like this β–’β”œβ•žβ–’β”œβ•žβ–’β”œβ”‚β–’β”œβ”‚ It cannot recognize the other language.
Can someone give me a hint on how to fix those problems ?
Thanks!
ReadConsoleInput returns events for each keystroke. For example, if you type SHIFT+A to get a capital A then you'll receive four key events: SHIFT down, A down, A up, SHIFT up.
The SHIFT key does not have a corresponding ASCII code so eventBuffer.Event.KeyEvent.uChar.AsciiChar is set to zero. This zero terminates the string you are building in dataBuffer so you don't see anything typed after the SHIFT key.
The simplest fix is to ignore any key event with an ASCII code of zero.
Additionally, if you want this to work well with foreign languages you might do better to use ReadConsoleInputW and eventBuffer.Event.KeyEvent.uChar.UnicodeChar. Better yet, compile it all as a Unicode app.

Resources