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.
Related
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.
I'm trying to translate the given vkCode and scanCode to actual characters, based on the keyboard. Example of what I'm trying to achieve is translating AltGr+V to '#', Shift+1 to '!' etc., but based on the current keyboard layout. I've managed to get it working briefly, but after an unknown change it's no longer working. Here is what I currently have:
unsigned char btKeys[256] = {0};
GetKeyboardState(btKeys);
HKL keyboardLayout = GetKeyboardLayout(0);
wchar_t szBuffer[2] = {0};
if (ToUnicodeEx(vkCode, scanCode, btKeys, szBuffer, 2, 0, keyboardLayout)) {
if (iswcntrl(szBuffer[0])) {
sendControl(szBuffer[0]);
} else {
sendCharacter(szBuffer[0]);
}
}
But for some reason I only get back the same character as was sent in via the vkCode (for example pressing Shift+1 yields only '1')
How can I get ToUnicodeEx to return the correct value?
I'd like to read button mappings from a text file that contains data like this:
DPAD_LEFT = 105
DPAD_RIGHT = 106
DPAD_UP = 103
DPAD_DOWN = 108
The right part is actually the evdev keycode (as defined in <linux/input.h>).
This is quite hard to read, so I'd like to be able have files like this:
DPAD_LEFT = KEY_LEFT
DPAD_RIGHT = KEY_RIGHT
DPAD_UP = KEY_UP
DPAD_DOWN = KEY_DOWN
But I'm currently not able to convert them back:
char[256] keyname;
some_method_to_read(&keyname, "DPAD_LEFT");
//keyname now contains "KEY_LEFT"
How do I get the corresponding keycode (e.g. 105)? Is there a standard way to do this?
EDIT: The only way I can think of right now is by duplicating all the keycodes in my source and putting them in an array or map, like the evtest utility does. But there are a lot of keycodes and this seems quite a bit of overkill to me. Also, this might get out-of-sync with the keycodes defined in <input/linux.h> at some point.
std::map<string, int> keynames;
#define MAP_KEYCODE(keycode) keynames[#keycode] = keycode
MAP_KEYCODE(KEY_LEFT);
MAP_KEYCODE(KEY_RIGHT);
MAP_KEYCODE(KEY_UP);
MAP_KEYCODE(KEY_DOWN);
// [...]
Have your program read the name-to-code mapping from a configuration file(s), say /usr/share/yourprogram/keycodes and/or $HOME/.yourprogram/keycodes.
Document that anyone can regenerate that file from their /usr/include/linux/input.h -- and regenerate the initial file yourself -- using for example
awk '$2 ~ /^KEY_/ { code[$2] = $3 }
END {
for (name in code)
if (code[name] ~ /^KEY_/)
code[name] = code[code[name]];
for (name in code)
if (code[name] !~ /KEY_/)
printf "%-24s %s\n", name, code[name]
}' /usr/include/linux/input.h | sort
You might have to add KEY_CNT youself (it's value is one more than KEY_MAX), as the above script does not do math, only direct aliases.
To describing the name-to-code mappings, I'd use
struct keycode {
struct keycode *next;
unsigned int code;
unsigned int hash;
unsigned char namelen;
char name[];
};
where the hash is a simple hash, say djb2,
unsigned int djb2(const char *const str, const size_t len)
{
unsigned int result = 5831U;
size_t i;
for (i = 0; i < len; i++)
result = result * 33U ^ (unsigned int)str[i];
return result;
}
Of currently defined key codes, only KEY_CUT and KEY_F15 map to the same djb2 hash, 1857856141. (If you used 31U instead of 33U, the current set would have no collisions, but that's no proof against future collisions. Better have one collision already, so you can test it is handled correctly.)
The function that reads the configuration file could just return the codes by prepending new ones to a singly-linked list, perhaps
int read_keycodes(const char *const filename,
struct keycode **list);
If you prepend to the list, you should later on ignore redefinitions of the same name. This way, if you read the system-wide configuration first, then the user-specific one, the user-specific one can override the system-wide ones.
After all keycode mappings are read, you construct a hash table, something like
struct keytab {
unsigned int size; /* index = hash % size */
struct keycode **slot;
};
(When building the hash table, discard keycodes whose exact names are already in the keytab. This way later definitions override earlier ones.)
This way you only need to calculate the hash of the name you want to look up, then probe the linked list in your keytab structure. Compare hashes first, then lengths; if both match, finally do a strcmp(). This way the lookup will be very fast, and relatively simple to implement, too. With current key codes, you'll only do the slow strcmp() twice for KEY_F15 and KEY_CUT; for all others, a single strcmp() will suffice.
Questions?
I found a way to do this properly: By using libevdev's libevdev_event_code_from_name function.
unsigned int event_type = EV_KEY;
const char* name = "BTN_A";
int code = libevdev_event_code_from_name(event_type, name);
if(code < 0)
{
puts("There was an error!");
}
I'm using Nanopb to try and send protobuf messages from a VxWorks based National Instruments Compact RIO (9025). My cross compilation works great, and I can even send a complete message with data types that don't require extra encoding. What's getting me is the callbacks. My code is cross compiled and called from LabVIEW and the callback based structure of Nanopb seems to break (error out, crash, target reboots, whatever) on the target machine. If I run it without any callbacks it works great.
Here is the code in question:
bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
char *str = "Woo hoo!";
if (!pb_encode_tag_for_field(stream, field))
return false;
return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}
extern "C" uint16_t getPacket(uint8_t* packet)
{
uint8_t buffer[256];
uint16_t packetSize;
ExampleMsg msg = {};
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
msg.name.funcs.encode = &encode_string;
msg.value = 17;
msg.number = 18;
pb_encode(&stream, ExampleMsg_fields, &msg);
packetSize = stream.bytes_written;
memcpy(packet, buffer, 256);
return packetSize;
}
And here's the proto file:
syntax = "proto2"
message ExampleMsg {
required int32 value = 1;
required int32 number = 2;
required string name = 3;
}
I have tried making the callback an extern "C" as well and it didn't change anything. I've also tried adding a nanopb options file with a max length and either didn't understand it correctly or it didn't work either.
If I remove the string from the proto message and remove the callback, it works great. It seems like the callback structure is not going to work in this LabVIEW -> C library environment. Is there another way I can encode the message without the callback structure? Or somehow embed the callback into the getPacket() function?
Updated code:
extern "C" uint16_t getPacket(uint8_t* packet)
{
uint8_t buffer[256];
for (unsigned int i = 0; i < 256; ++i)
buffer[i] = 0;
uint16_t packetSize;
ExampleMsg msg = {};
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
msg.name.funcs.encode = &encode_string;
msg.value = 17;
msg.number = 18;
char name[] = "Woo hoo!";
strncpy(msg.name, name, strlen(name));
pb_encode(&stream, ExampleMsg_fields, &msg);
packetSize = stream.bytes_written;
memcpy(packet, buffer, sizeof(buffer));
return packetSize;
}
Updated proto file:
syntax = "proto2"
import "nanopb.proto";
message ExampleMsg {
required int32 value = 1;
required int32 number = 2;
required string name = 3 [(nanopb).max_size = 40];
}
You can avoid callbacks by giving a maximum size for the string field using the option (nanopb).max_size = 123 in the .proto file. Then nanopb can generate a simple char array in the structure (relevant part of documentation).
Regarding why callbacks don't work: just a guess, but try adding extern "C" also to the callback function. I assume you are using C++ there, so perhaps on that platform the C and C++ calling conventions differ and that causes the crash.
Does the VxWorks serial console give any more information about the crash? I don't remember if it does that for functions called from LabView, so running some test code directly from the VxWorks shell may be worth a try also.
Perhaps the first hurdle is how the code handles strings.
LabVIEW's native string representation is not null-terminated like C, but you can configure LabVIEW to use a different representation or update your code to handle LabVIEW's native format.
LabVIEW stores a string in a special format in which the first four bytes of the array of characters form a 32-bit signed integer that stores how many characters appear in the string. Thus, a string with n characters requires n + 4 bytes to store in memory.
LabVIEW Help: Using Arrays and Strings in the Call Library Function Node
http://zone.ni.com/reference/en-XX/help/371361L-01/lvexcodeconcepts/array_and_string_options/
My background is not in C (it's in Real Studio - similar to VB) and I'm really struggling to split a comma-delimited string since I'm not used to low-level string handling.
I'm sending strings to an Arduino over serial. These strings are commands in a certain format. For instance:
#20,2000,5!
#10,423,0!
'#' is the header indicating a new command and '!' is the terminating footer marking the end of a command. The first integer after '#' is the command id and the remaining integers are data (the number of integers passed as data may be anywhere from 0 - 10 integers).
I've written a sketch that gets the command (stripped of the '#' and '!') and calls a function called handleCommand() when there is a command to handle. The problem is, I really don't know how to split this command up to handle it!
Here's the sketch code:
String command; // a string to hold the incoming command
boolean commandReceived = false; // whether the command has been received in full
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// main loop
handleCommand();
}
void serialEvent(){
while (Serial.available()) {
// all we do is construct the incoming command to be handled in the main loop
// get the incoming byte from the serial stream
char incomingByte = (char)Serial.read();
if (incomingByte == '!')
{
// marks the end of a command
commandReceived = true;
return;
}
else if (incomingByte == '#')
{
// marks the start of a new command
command = "";
commandReceived = false;
return;
}
else
{
command += incomingByte;
return;
}
}
}
void handleCommand() {
if (!commandReceived) return; // no command to handle
// variables to hold the command id and the command data
int id;
int data[9];
// NOT SURE WHAT TO DO HERE!!
// flag that we've handled the command
commandReceived = false;
}
Say my PC sends the Arduino the string "#20,2000,5!". My sketch ends up with a String variable (called command) that contains "20,2000,5" and the commandRecieved boolean variable is set to True so the handleCommand() function is called.
What I would like to do in the (currently useless) handleCommand() function is assign 20 to a variable called id and 2000 and 5 to an array of integers called data, i.e: data[0] = 2000, data[1] = 5, etc.
I've read about strtok() and atoi() but frankly I just can't get my head around them and the concept of pointers. I'm sure my Arduino sketch could be optimised too.
Since you're using the Arduino core String type, strtok and other string.h functions aren't appropriate. Note that you can change your code to use standard C null-terminated strings instead, but using Arduino String will let you do this without using pointers.
The String type gives you indexOf and substring.
Assuming a String with the # and ! stripped off, finding your command and arguments would look something like this:
// given: String command
int data[MAX_ARGS];
int numArgs = 0;
int beginIdx = 0;
int idx = command.indexOf(",");
String arg;
char charBuffer[16];
while (idx != -1)
{
arg = command.substring(beginIdx, idx);
arg.toCharArray(charBuffer, 16);
// add error handling for atoi:
data[numArgs++] = atoi(charBuffer);
beginIdx = idx + 1;
idx = command.indexOf(",", beginIdx);
}
data[numArgs++] = command.substring(beginIdx);
This will give you your entire command in the data array, including the command number at data[0], while you've specified that only the args should be in data. But the necessary changes are minor.
seems to work, could be buggy:
#include<stdio.h>
#include <string.h>
int main(){
char string[]="20,2000,5";
int a,b,c;
sscanf(string,"%i,%i,%i",&a,&b,&c);
printf("%i %i %i\n",a,b,c);
a=b=c=0;
a=atoi(strtok(string,","));
b=atoi(strtok(0,","));
c=atoi(strtok(0,","));
printf("%i %i %i\n",a,b,c);
return 0;
}