getch() and mixing regular keys with arrow keys - c++

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.

Related

Code ignoring if statements - C++ [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I've been working on a password generator for my College coursework, and one of the parts to this involves creating 'Complex' passwords, which are passwords which are nothing more than strings of random characters, and the user should be able to specify what types of characters are used. However, the set of if statements which control if a function is used don't activate based on the values within uppertrue numbertrue and lowertrue, they all act as if the statement returns true, and so the function is always run.
#include
#include
#include
#include
int upper(), lower(), number(), symbol(); //initializing functions to be used to generate the ascii code
int clength = 15;
int pass[30];
int uppertrue = 0, numbertrue = 1, symboltrue = 0;
int main()
{
srand (time(NULL)); //seed random generator
int i = 0; //counter
int which = 0;
do
{
which = rand() % 4 + 1; //randomly decides which type of character will be shown - probablity is unweighted for complex module
if (which == 1)
{
pass[i] = lower(); //inserts the code returned by the function into the array
i++;
}
else if ((uppertrue == 1) && (which == 2))
{
pass[i] = upper();
i++;
}
else if (numbertrue == 1 && which == 3)
{
pass[i] = number();
i++;
}
else if (symboltrue == 1 && which == 4)
{
pass[i] = symbol();
i++;
}
}while (i!=(clength+1)); //terminates loop when the array is complete
std::string strpass;
int x=0;
do
{
char tempchar;
tempchar = pass[x];
std::cout << tempchar;
x++;
}while (x!=15);
return 0;
}
int upper() //creates random number between the range of ascii characters that results in caps
{
return rand() % 65 + 26;
}
int number() //same as upper but for numbers
{
return rand() % 48 + 9;
}
int lower() //same as upper but for lower case
{
return rand() % 122 + 26;
}
int symbol() //same as upper but for symbols (currently only supporting a few characters
{
return rand() % 63 + 6;
}
if anyone can point me in the correct direction it would be much appreciated, it seems like it's a logical error but I can't see anything logically wrong with it. Is it perhaps to do with some sort of quirk with C++? (bearing in mind I was taught C and this is the first thing I've done in C++)
Many thanks
(A comment said to remove the part where i'd usually enter the values for uppertrue etc so i've hardcoded the values to show the problem instead)
Your problem is here:
int lower() // same as upper but for lower case
{
return rand() % 122 + 26;
}
It will produce random number in range 26 ... 147. Which is something completely different than range for lower case characters. You want this:
return rand() % ('z' - 'a' + 1) + 'a';
You should fix the other functions in similar manner.
Note to those who worry about their code being able to run on, for example, mainframes using EBCDIC character encoding: This assumes that a..z have continuous character codes.
The specific problem is that you have bugs in the functions that return, at random, the various characters.
The C++ standard is intentionally vague as to the numeric values that it associates with characters. The precise mapping is down to the implementation and the scheme is called the encoding.
Whilst ASCII encoding is common, it's by no means universal and so in order to achieve portability it's best not to make assumptions about your platform unless you really need to.
So, you really ought to recast lower on the lines:
char lower
{
const char* s = "abcdefghijklmnopqrstuvwxyz";
return s[rand() % 26];
}
which is truly portable. I've also taken the liberty of changing your function return type.
You ought to do similar for upper. Your symbols function will drop out similarly.
I'd be tempted to adopt the same approach for number too but here the C++ standard does say something about the digits: the encoding must arrange the characters 0 to 9 to be in a contiguous block and in that order, so the statement
return rand() % ('9' - '0' + 1) + '0';
is portable. As a final remark, you could use static char[] s = "abc...z"; and (sizeof(s) - 1) in place of the hardcoded 26. This is a quite advanced technique and not obvious to a beginner but do research it as your programming skills develop.

How to walk along UTF-16 codepoints?

I have the following definition of varying ranges which correspond to codepoints and surrogate pairs:
https://en.wikipedia.org/wiki/UTF-16#Description
My code is based on ConvertUTF.c from the Clang implementation.
I'm currently struggling with wrapping my head around how to do this.
The code which is most relevant from LLVM's implementation that I'm trying to understand is:
unsigned short bytesToWrite = 0;
const u32char_t byteMask = 0xBF;
const u32char_t byteMark = 0x80;
u8char_t* target = *targetStart;
utf_result result = kConversionOk;
const u16char_t* source = *sourceStart;
while (source < sourceEnd) {
u32char_t ch;
const u16char_t* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
u32char_t ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == kStrictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = kSourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = kSourceExhausted;
break;
}
} else if (flags == kStrictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = kSourceIllegal;
break;
}
}
...
Specifically they say in the comments:
If we have a surrogate pair, convert to UTF32 first.
and then:
If it's a low surrogate, convert to UTF32.
I'm getting lost along the lines of "if we have.." and "if it's.." and my response being while reading the comments: "what do we have?" and "what is it?"
I believe ch and ch2 is the first char16 and the next char16 (if one exists), checking to see if the second is part of a surrogate pair, and then walking along each char16 (or do you walk along pairs of chars?) until the end.
I'm getting lost along the lines of how they are using UNI_SUR_HIGH_START, UNI_SUR_HIGH_END, UNI_SUR_LOW_START, UNI_SUR_LOW_END, and their use of halfShift and halfBase.
Wikipedia also notes:
There was an attempt to rename "high" and "low" surrogates to "leading" and "trailing" due to their numerical values not matching their names. This appears to have been abandoned in recent Unicode standards.
Making note of "leading" and "trailing" in any responses may help clarify things as well.
ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END checks if ch is in the range where high surrogates are, that is, [D800-DBFF]. That's it. Then the same is done for checking if ch2 is in the range where low surrogates are, meaning [DC00-DFFF].
halfShift and halfBase are just used as prescribed by the UTF-16 decoding algorithm, which turns a pair of surrogates into the scalar value they represent. There's nothing special being done here; it's the textbook implementation of that algorithm, without any tricks.

Why can't I read all Ctrl + 'letters'

I've made a program that allows me to read all the stand-alone function keys (that I thought to test, at least) on my keyboard. I have it designed so that I can refer to any single key input as a single value. It handles Return, F1-F12, delete, backspace, arrows etc
I just thought to test modifications of input. I already made sure shift works, but now I decided to test Ctrl and Alt.
Question 1
Why does Alt not modify any of the input key codes?
Question 2
Why can I not capture certain Ctrl + combinations?
Eg. Ctrl + s; Ctrl + 1-9;
Ctrl + 2 works, but I think it might be due to having my keyboard set as UK.
This is the code I am using.
Please note, I am not necessarily asking how to capture these key combinations (unless it is a simple modification or two). I only want to know why I am unable to.
#include <iostream>
#include <conio.h>
#include <cwchar>
union wide_char
{
short Result;
char C[2];
};
int main()
{
wchar_t R;
int N;
wide_char user_input;
//Loops forever, this is only a proof of concept program proving this is possible to incorporate into a larger program
while(true)
{
user_input.C[0] = 0;
user_input.C[1] = 0;
//Loop twice, or until code causes the loop to exit
//Two times are neccessary for function keys unfortunately
for(int i = 0; i < 2; ++i)
{
//While there isn't a key pressed, loop doing nothing
while(!kbhit()){}
//Grab the next key from the buffer
//Since the loop is done, there must be at least one
user_input.C[i] = getch();
switch(user_input.C[i])
{
case 0:
case -32:
//The key pressed is a Function key because it matches one of these two cases
//This means getch() must be called twice
//Break switch, run the for loop again ++i
break;
default:
//The character obtained from getch() is from a regular key
//Or this is the second char from getch() because the for loop is on run #2
//Either way we need a wide char (16 bits / 2 Bytes)
if(user_input.C[1] != 0)
//Function keys {Arrows, F1-12, Esc}
//We now combine the bits of both chars obtained
//They must be combined Second+First else the F1-12 will be duplicate
//This is because on F1-12 getch() returns 0 thus won't affect the combination
R = user_input.Result;
else
//Regular key press
R = user_input.C[0];
//Display our unique results from each key press
N = R;
std::cout << R << " R = N " << N << std::endl;
if( R == 'a' )
std::cout << "a = " << N << std::endl;
//Manually break for loop
i = 3;
break;
}
}
//We need to reset the array in this situation
//Else regular key presses will be affected by the last function key press
}
}
This is very specific to your environment. You're using conio which is specific to DOS / Windows.
Most of the Ctrl + alpha key values are bound to characters 1 - 26, and certain others are bound to other values under 31, to map to ASCII control characters. But some, like Ctrl + S have special meaning (Ctrl + S is XOFF in ASCII), and so might get 'eaten' by your environment.
Fundamentally, the issue you're facing is the fact that getch approximates an old-school serial terminal interface. They only expose keyboard events at a "least common denominator" level, as opposed to a lower level that would allow you to distinguish modifier keys, etc. and give you a better way to deal with special keys such as function keys.
(As you've noticed, function keys, have special multi-byte sequences. Again, this is due to emulating old-school serial terminals, where the keyboard might be at the other end of a remote link.)
To get a lower-level (and therefore more direct and flexible interface) you need to use a more platform-specific library, or a richer library such as SDL. Either would give you a lower level view of the inputs from the keyboard.

Converting scancodes to ASCII

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.

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.