Converting scancodes to ASCII - c++

I'm implementing my own text editor in c++. It's going... ok. ;P
I need a way to turn a keycode (specifically Allegro, they call it scancodes) into an ASCII-char. I can do A-Z easy, and converting those to a-z is easy as well. What I do currently is use a function in Allegro that returns a name from a scancode (al_keycode_to_name), meaning if the key pressed is A-Z it returns "A" to "Z". That's easy peasy, but I can't simply read special characters like ",", ";" etc. That's where I'm having a hard time.
Is there a way to do this automatically? Maybe a library that does this? The real trick is taking different layouts into consideration.
Here's what I have so far, in case anyone's interested. The class InputState is basically a copy of the Allegro inputstate, with added functionality (keyDown, keyUp, keyPress for example):
void AllegroInput::TextInput(const InputState &inputState, int &currentCharacter, int &currentRow, std::string &textString)
{
static int keyTimer = 0;
static const int KEY_TIMER_LIMIT = 15;
for (int i = 0; i < 255; i++)
{
if (inputState.key[i].keyDown)
{
keyTimer++;
}
if (inputState.key[i].keyPress)
{
keyTimer = 0;
}
if ((inputState.key[i].keyPress) || ((inputState.key[i].keyDown) && (keyTimer >= KEY_TIMER_LIMIT)))
{
std::string ASCII = al_keycode_to_name(i);
if ((ASCII.c_str()[0] >= 32) && (ASCII.c_str()[0] <= 126) && (ASCII.length() == 1))
{
textString = textString.substr(0, currentCharacter) + ASCII + textString.substr(currentCharacter, textString.length());
currentCharacter++;
}
else
{
switch(i)
{
case ALLEGRO_KEY_DELETE:
if (currentCharacter >= 0)
{
textString.erase(currentCharacter, 1);
}
break;
case ALLEGRO_KEY_BACKSPACE:
if (currentCharacter > 0)
{
currentCharacter--;
textString.erase(currentCharacter, 1);
}
break;
case ALLEGRO_KEY_RIGHT:
if (currentCharacter < textString.length())
{
currentCharacter++;
}
break;
case ALLEGRO_KEY_LEFT:
if (currentCharacter > 0)
{
currentCharacter--;
}
break;
case ALLEGRO_KEY_SPACE:
if (currentCharacter > 0)
{
textString = textString.substr(0, currentCharacter) + " " + textString.substr(currentCharacter, textString.length());
currentCharacter++;
}
break;
}
}
}
}
}

You should be using the ALLEGRO_EVENT_KEY_CHAR event with the event.keyboard.unichar value to read text input. ALLEGRO_EVENT_KEY_DOWN and ALLEGRO_EVENT_KEY_UP correspond to physical keys being pressed. There is not a 1:1 correspondence between them and printable characters.
Say a dead key is being used to convert the two keys e' to é. You'd get two key down events for e and ' (and neither are useful for capturing the proper input), but one key char event with é. Or inversely, maybe somebody mapped F4 to a macro that unleashes an entire paragraph of text. In that case, you'd have multiple chars for a single key down.
Or a simple test: if you hold down a key for five seconds, you will get one ALLEGRO_EVENT_KEY_DOWN but multiple ALLEGRO_EVENT_KEY_CHAR as the OS' keyboard driver sends repeat events.
You can use ALLEGRO_USTR to easily store these unicode strings.
ALLEGRO_USTR *input = al_ustr_new("");
// in the event loop
al_ustr_append_chr(input, event.keyboard.unichar);
There's also ways to delete characters if backspace is pressed, etc. You can use the ustr data types with the font add-on directly via al_draw_ustr(font, color, x, y, flags, input), or you can use al_cstr(input) to get a read-only pointer to a UTF-8 string.

Related

Sorting in alphabetical with å ä ö

I have an algoritm sorting words in alphabetical by the letters value, this all works fine until I include å ä ö as they return a int value ranging from -103 to -124. Becuse of this the order of the words are like this ä å ö a for example, when it should be a å ä ö. So how do I make it sort it correctly with å ä ö last?
Edit: Im not allowed to use fancy functions, that is why this code is so bare boned, also using using namespace std
My code:
pali is a vector of type string that I use to store the words
void SortPal() {
int antal = pali.size();
string tempO;
bool byte = false;
for (int i = 0; i < antal - 1; i++) { //går igenom alla ord i vectorn
if (int(pali[i][0]) > int(pali[i + 1][0])) {
tempO = pali[i];
pali[i] = pali[i + 1];
pali[i + 1] = tempO;
i = -1;
}
else if (int(pali[i][0]) == int(pali[i + 1][0])) { //Om första bokstaven är samma kollar den följande
int minsta = pali[i].size();
if (minsta > pali[i + 1].size()) {
minsta = pali[i + 1].size();
}
for (int a = 1; a < minsta-1; a++){
if (int(pali[i][a]) > int(pali[i + 1][a])) { //byter om någon av bokstäverna efter den första är mindre än bokstäverna i andra ordet
tempO = pali[i];
pali[i] = pali[i + 1];
pali[i + 1] = tempO;
i = -1;
byte = true;
break;
}
}
if (byte == false && pali[i].size() > pali[i + 1].size()) { // byter om pali i+1 är mindre än pali i
tempO = pali[i];
pali[i] = pali[i + 1];
pali[i + 1] = tempO;
i = -1;
}
}
}
}
Generally speaking, there's no relationship between the alphabetical order of letters in any given language and numerical codes assigned to said letters in any given character set. In order to compare strings according to the alphabetical order of a given language (or more generally the collation order of the current locale), C has a special function called strcoll.
In order to use it, you need to set up your locale accordingly. Unfortunately, locale names are not standard in C. If you are on Windows, the linked example is unlikely to work.
This is what you should be using in real software. It matters little for you assignment since you are not supposed to use fancy library functions. You need to implement a function similar to strcoll yourself, and it should only work for your language.
In a language where each character has its own place in the alphabet, this function is simple: write a function that takes a character and returns its place in the alphabet (e.g. for 'a' return 1, for 'b' return 2, ..., for 'å' return 27, for 'ä' return 28...) Compare the strings according to numbers returned by this function. This may or may not take into account letter case depending on what exact sort order you want.
If you don't want to write a big switch, you can use the fact that letters that are in ASCII are already ordered as you want, you only need to fix the order of three additional letters. So you can write something like this:
int collation_order(int ch) {
switch (ch) {
case 'Å': return 'Z'+1;
case 'å': return 'z'+1;
case 'Ä': return 'Z'+2;
case 'ä': return 'z'+2;
case 'Ö': return 'Z'+3;
case 'ö': return 'z'+3;
default : return ch;
}
}
int my_strcoll (char* p, char* q)
{
int pp, qq;
while (*p && (pp=collation_order(*p)) == (qq = collation_order(*q))) {
p++; q++;
}
return pp - qq;
}
Of course this means that non-alphabetic that come after Z/z in the ASCII table will get sorted incorrectly. If you want to sort those after Ö/ö, you need to extend collation_order accordingly. Try doing this without resorting to a case for each individual character.
Another way to write collation_order is to use character codes (cast to unsigned char) as indices in an array of 256 integer elements.
Also please note that old 8-bit encodings are old and should not be used for serious new development. For more information, read this.
Since your options are constrained and you can also constrain your input to a foreseeable universe, I'd suggest you to use a simple parser function to fit non-ASCII characters inside the places you know they should:
int parse_letter( int source )
{
switch( source )
{
case 'å':
case 'ä': return 'a';
case 'ö': return 'o':
// as many cases as needed...
default: return source;
}
}

How to input a multi-digit integer into an Arduino using a 4x4 keypad?

I am trying to make a combination lock using an Arduino, a keypad and a Servo but I have come across an obstacle.
I can't find a way to store a 4 digit value in a variable. since keypad.getKey only allows to store one digit.
After some browsing on the internet I came upon a solution for my problem on a forum but the answer didn't include a code sample, and I couldn't find anything else about in on the internet.
The answer said to either use a time limit for the user to input the number or a terminating character (which would be the better option according to them).
I would like to know more bout these terminating characters and how to implement them, or if anybody could suggest a better solution that would be much appreciated as well.
Thank you in advance,
To store 4 digit values, the easiest and naive way to do it is probably to use an array of size 4. Assuming keypad.getKey returns an int, you could do something like this: int input[4] = {0};.
You will need a cursor variable to know into which slot of the array you need to write when the next key is pressed so you can do some kind of loop like this:
int input[4] = {0};
for (unsigned cursor = 0; cursor < 4; ++cursor) {
input[cursor] = keypad.getKey();
}
If you want to use a terminating character (lets say your keyboard have 0-9 and A-F keys, we could say the F is the terminating key), the code changes for something like:
bool checkPassword() {
static const int expected[4] = {4,8,6,7}; // our password
int input[4] = {0};
// Get the next 4 key presses
for (unsigned cursor = 0; cursor < 4; ++cursor) {
int key = keypad.getKey();
// if F is pressed too early, then it fails
if (key == 15) {
return false;
}
// store the keypress value in our input array
input[cursor] = key;
}
// If the key pressed here isn't F (terminating key), it fails
if (keypad.getKey() != 15)
return false;
// Check if input equals expected
for (unsigned i = 0; i < 4; ++i) {
// If it doesn't, it fails
if (expected[i] != input[i]) {
return false;
}
}
// If we manage to get here the password is right :)
return true;
}
Now you can use the checkPassword function in your main function like this:
int main() {
while (true) {
if (checkPassword())
//unlock the thing
}
return 0;
}
NB: Using a timer sounds possible too (and can be combined with the terminating character option, they are not exclusive). The way to do this is to set a timer to the duration of your choice and when it ends you reset the cursor variable to 0.
(I never programmed on arduino and don't know about its keypad library but the logic is here, its up to you now)
In comment OP says a single number is wanted. The typical algorithm is that for each digit entered you multiply an accumulator by 10 and add the digit entered. This assumes that the key entry is ASCII, hence subtracting '0' from it to get a digit 0..9 instead of '0'..'9'.
#define MAXVAL 9999
int value = 0; // the number accumulator
int keyval; // the key press
int isnum; // set if a digit was entered
do {
keyval = getkey(); // input the key
isnum = (keyval >= '0' && keyval <= '9'); // is it a digit?
if(isnum) { // if so...
value = value * 10 + keyval - '0'; // accumulate the input number
}
} while(isnum && value <= MAXVAL); // until not a digit
If you have a backspace key, you simply divide the accumulator value by 10.

getch() and mixing regular keys with arrow keys

I can't get the text to display on console and neither is it saved properly. I got the arrow keys, enter,backspace and escpe working though.
also another /small/ error I don't really get is when I press esc and it exits from the void I get directed to this piece of code
#endif /* defined (_M_IX86) || defined (_M_X64) */
__fastfail(FAST_FAIL_STACK_COOKIE_CHECK_FAILURE);
inside of gc_report.c, which I don't find nuch infor,ation about (or atleast related to my problem).
#define KEY_UP 72
#define KEY_DOWN 80
#define KEY_LEFT 75
#define KEY_RIGHT 77
#define KEY_ENTER 13
#define KEY_BACKSPACE 8
#define KEY_ESCAPE 27
void texteditor(int x, int y,int kolommen,char textarr[20][20],int rijen=20)
{
int index = 0, indey = 0, keuze,lol = 20;
do{
gotoxy(index + x, indey + y);
keuze = 0;
keuze = _getch();
if (keuze == 0 || keuze == 0xE0 || keuze == 224)
{
keuze = _getch();
gotoxy(index + x, indey + y);
switch (keuze)
{
case KEY_UP:indey--;
break;
case KEY_LEFT: index--;
break;
case KEY_DOWN:indey++;
break;
case KEY_RIGHT: index++;
break;
}
indey = (indey <= 0) ? 0 : (indey > kolommen) ? kolommen : indey;
index = (index <= 0) ? 0 : (index > rijen) ? rijen : index;
}
if (keuze == 32 || (keuze >= 46 && keuze <= 57) || (keuze <= 64 && keuze >= 126))
{
textarr[index][indey] = (char)keuze;
std::cout << textarr[index][indey];
index++;
index = (index <= 0) ? 0 : (index > rijen) ? rijen : index;
}
if (keuze == KEY_BACKSPACE)
{
index = index--;
gotoxy(index + x, indey + y);
std::cout << " ";
index = (index <= 0) ? 0 : (index > rijen) ? rijen : index;
}
if (keuze == KEY_ENTER)
{
index = 0;
indey++;
indey =(indey >= kolommen) ? kolommen : indey++;
}
} while (keuze != KEY_ESCAPE);}
I also searched a bit on the values behind the arrow keys, in which I found 37(left arrow),38(up arrow),39(right arrow),40(down arrow) as ASCII value, then what's the difference from mine?
The ASCII charset, as the name implies, describes a character set and not the keys on the keyboard. For that reason there are no values for arrow keys in ASCII.
Instead the _getch() function you are using is returning either 224 (E0h) or 0 to indicate that what's following is a key scan code. The values you are using for these are correct and you can look them up here.
The other values you found (37-40) are called virtual keys and are much more common. e.g they are returned in Javascript for event.keyCodeand also available in the WinApi but they are not applicable to your _getch() example.
Now that this is sorted out, let's look at your example:
You can remove the E0 as it equals 224.
The textarr array you are using, has the dimensions 20x20. That means writing beyond textarr[19][19] is undefined behaviour. You are comparing the array index for being bigger than 20 before you use rijen as substitute index. You should instead have rijen be 19. That way you don't have the out of bound access.
I would place kolommen next to rijen. It makes no sense to have the array between them + it hinders you from setting a default value for it.
You are also ignoring a lot of printable characters. You should structure your code to have an else condition at the end, where you then check with isprint() whether it's printable and then print it.
And now the main part: if (... || (keuze <= 64 && keuze >= 126)). Do you see the problem? I guess you meant (keuze >= 64 && keuze <= 126). The way it's now you are ignoring all the ASCII letters.
Maybe you would like to clear screen before starting running texteditor(). system("cls") is the standard Windows way of doing this.
Another thing, nesting ternary expressions is ugly. The unneeded parenthesis you used for the condition would be much better around the second ternary expression. Also the windows console defaults to 80x25, which you might want to use instead of 20x20.
And finally, conio.h is a non-standard header and you'd be better off using the functionality present in <windows.h> instead. Or get away from the suboptimal terminal handling in Windows altogether and use a library like ncurses together with a proper terminal.
The codes are based on ansi.sys (which requires some add-ons to run on modern Windows). You did not identify which add-on you are using. Perhaps you are referring to some source as the Daniweb article how to detect arrow keys??.
Questions regarding arrow-keys and getch (for practical purposes the same as _getch -- see Difference between getch() and _getch()) have been answered before:
getch and arrow codes
How to scan arrow keys from keyboard?
The codes used for left/right/up/down are based on the final character of the escape sequences recognized by ansi.sys, e.g., (according to ncurses):
kcub1=\0K,
kcud1=\0P,
kcuf1=\0M,
kcuu1=\0H,
But the \0 may be other characters such as \033[, \340, depending on the configuration. The latter may be the Windows Extended Prefix Code as noted in Scan Codes on CPlusPlus forum.

Inexistent double decrement?

I was writing a little game, where there is an hidden word, and the user must guess, char to char, what word is.
While coding this I got stucked in something that I don't understeand where and how it happens.
while(true)
{
if(Hue == 0)
Try -= 1;
if(Hue == 1)
Hue = 0;
GotoXY(0, 3);
printf("Inserisci una lettera maiuscola\n>");
GotoXY(1, 4);
scanf("%c", &Key);
GotoXY(0, 4);
printf(" ");
GotoXY(0, 6);
printf("Numero di tentativi rimasti: %d ", Try);
for(unsigned short Iterator = 1; Iterator < Length - 1; ++Iterator)
if(Key == UserString[Iterator])
{
for(unsigned short SecIterator = Iterator; SecIterator < Length - 1; ++SecIterator)
{
if(Key == UserString[SecIterator])
{
GotoXY(SecIterator, 1);
printf("%c", Key);
}
}
Hue = 1;
break;
}
}
Hue is a simple control variable to check if the key was in the word.
If it's still 0 then the key wasn't in the word, so the Try decrements it self and so on.
But what happen is that Hue, either is 0 or 1 causes the decrement of Try, and the thing even more stange is that Try decrement twice when is 0, evenly in the code isn't written nothing like that.
Thanks for the help.
It seems the confusion is mostly due to the double decrement: well, you are reading chars and most likely you hit return making two chars available: the entered character and the '\n' from the return. Since apparently neither character matches you get two decrements.
Just for a bit of explanation: when using the formatted input using std::cin >> Key leading whitespace is skipped. When using scanf("%c", &c) each character is extracted. I think you can have scanf() skip leading spaces using
if (1 == scanf(" %c", &c)) {
// process the input
}
Note the extra space in front of the '%c'. To debug issues like this it is generally a good idea to print what was read. ...and, of course, you always need to verify that the read was actually successful.

C++ and GetAsyncKeyState() function

As it gives only Upper case letters, any idea how to get lower case??
If the user simultaneously pessed SHIFT+K or CAPSLOCK is on,etc, I want to get lower cases..
is it possible in this way or another??
Thanks,
Suppose "c" is the variable you put into GetAsyncKeyState().
You may use the following method to detect whether you should print upper case letter or lower case letter.
string out = "";
bool isCapsLock() { // Check if CapsLock is toggled
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) // If the low-order bit is 1, the key is toggled
return true;
else
return false;
}
bool isShift() { // Check if shift is pressed
if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) // If the high-order bit is 1, the key is down; otherwise, it is up.
return true;
else
return false;
}
if (c >= 65 && c <= 90) { // A-Z
if (!(isShift() ^ isCapsLock())) { // Check if the letter should be lower case
c += 32; // in ascii table A=65, a=97. 97-65 = 32
}
out = c;
As you rightly point out, it represents a key and not upper or lower-case. Therefore, perhaps another call to ::GetASyncKeyState(VK_SHIFT) can help you to determine if the shift-key is down and then you will be able to modify the result of your subsequent call appropriately.