ncurses non-blocking read pushes cursor to bottom of window - c++

My game's main loop relies on a non-blocking read from getnstr. It checks whether the string to which it has read has non-zero length before proceeding with the rest of the loop (I couldn't find the convention for getting this behavior if one exists).
The problem is that it has the effect of forcing the input cursor down the bottom of the window, as if I had spammed Enter or something.
char command[5];
timeout(0);
while (getnstr(command, 4) && gameActive) {
if (strlen(command) == 0) { continue; }
...
}

Agreeing that it seems surprising, but SVr4 curses (which ncurses does match in this detail) always moves to the next row after completing the (attempt to) read characters.
You can see the corresponding code for (Open)Solaris at Illumos's Github in lines 191-207:
/*
* The following code is equivalent to waddch(win, '\n')
* except that it does not do a wclrtoeol.
*/
if (doecho) {
SP->fl_echoit = TRUE;
win->_curx = 0;
if (win->_cury + 1 > win->_bmarg)
(void) wscrl(win, 1);
else
win->_cury++;
win->_sync = savsync;
win->_immed = savimmed;
win->_leave = savleave;
(void) wrefresh(win);
}
that is, the "win->_cury++;" (or the scrolling operation).

Related

c++ - GetAsyncKeyState makes my program crash when I open it

So I'm making a game that runs on the console. I started with some functions to make drawing to the screen easier, and that worked fine. Then when I tried to add input (using the function GetAsyncKeyState), the program crashed as soon as I started the program. It said: "Text Game.exe has stopped working" Here's how I handled the code:
if(GetAsyncKeyState('A' && 0x8000)) {
x -= 1;
}
if(GetAsyncKeyState('D' && 0x8000)) {
x += 1;
}
if(GetAsyncKeyState('W' && 0x8000)) {
y += 1;
}
if(GetAsyncKeyState('S' && 0x8000)) {
y += 1;
}
If it helps, I got this method by reading this:
How to check if a Key is pressed
EDIT: So I ran it in debug mode, and it said it crashed when I ran a function I made called "refreshScreen();". I don't know why though. Here's the code:
void refreshScreen() {
system("CLS");
for ( int i = 0; i < screenHeight; i++ ) {
for ( int j = 0; j < screenWidth; j++ ) {
cout << screen[i][j];
}
cout << endl;
}
}
It's meant to clear the console, then print all of the contents of array "screen". "Screen", by the way, is the buffer that I write to.
If you are trying to use the GetAsyncKeyState as it's described in answers by the link you provided, you are doing it wrong.
Documentation says the following:
If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState.
So what is done in the answer by the link you provided:
if (GetAsyncKeyState('W') & 0x8000)
{ /*key is down*/ }
In the if statement there bitwise "and" operation is performed on return value of GetAsyncKeyState function and 0x8000 constant - which equals to 0x8000 the most significant bit is set or equals to 0 when it is not set.
What is your code doing:
if(GetAsyncKeyState('A' && 0x8000)) // ...
logical "and" operation between 'A' and 0x8000 constant - gives true which is casted to 1 and passed to GetAsyncKeyState as an argument.
[EDIT]: As it was mentioned in comments, 1 corresponds to left mouse button. So all if conditions will be true in case left mouse button is down and will be false otherwise. Probably, crash appears in different part of your program after unexpected change of x and y values. You should debug your program to localize the crash.

Function to recognize the end of a page won't split the text correctly

I'm trying to write a simple quest log program in c++ and SFML - something like quest logs in RPG games. I'm stuck at the state/function responsible for displaying all the entries in a single quest. I want it to work like an e-book reader, meaning when opening a quest, the pages (and std::vector) are filled with all the entries and only 2 neighboring ones are displayed at any giving time. First i had to put a function that would recognize when the horizontal end of the page is reached and this works just fine
Result of horizontal breaking:
but when i added a function to recognize when an entry hits the vertical end of the page, this is what happens
Result of horizontal + vertical breaking:
Here's the code for the function filling the pages
if (!mPagesFilled)
{
//make sure container of pages is not empty
if(mPages.empty())
mPages.push_back(Page());
for (auto it : mViewedQuest->questEntries) //main loop - iterate through entries belonging to a single, selected quest
{
//if there is less than 100px left, add a new Page() to a vector mPages
if (mActiveTheme->pageLayout[mLeftRight].height - mPages.back().pageSize.y < 100)
{
mPages.push_back(Page());
if (mLeftRight == 0)
mLeftRight = 1;
else
mLeftRight = 0;
}
//create and initialize the temporary PageEntry buffer object
PageEntry entry;
entry.header.setFont(mActiveTheme->themeFont);
entry.header.setPosition(mActiveTheme->pageLayout[mLeftRight].left, mActiveTheme->pageLayout[mLeftRight].top + mPages.back().pageSize.y);
entry.header.setString(it.getEntryDate());
entry.header.setColor(sf::Color(50, 50, 50, 255));
entry.entryHeight = entry.header.getLocalBounds().height + 20 ; //update entry height
//set up the body of entry
entry.body.setFont(mActiveTheme->themeFont);
entry.body.setColor(sf::Color::Black);
entry.body.setPosition(mActiveTheme->pageLayout[mLeftRight].left, mActiveTheme->pageLayout[mLeftRight].top + entry.entryHeight + mPages.back().pageSize.y);
//clear the stream and the buffer string and put the iterated entry content into the stream
ss.clear();
mBuffer.clear();
ss << it.entryText;
while (ss) //loop for as long as there is something to be read from the stream
{
//check if we have reached the end of stream and break the loop if we did to avoid the garbage char at the end
mBufferChar = ss.peek();
if (mBufferChar == EOF)
continue; // i know technically it should be break; but since it's EOF than it doesn't really matter i think
//append the buffer string with the next char from the stream and update the body
mBuffer += ss.get();
entry.body.setString(mBuffer);
//check if we have hit the right side of the page and add newline if we did
if (entry.body.getLocalBounds().width > mActiveTheme->pageLayout[mLeftRight].width)
{
size_t it = mBuffer.find_last_of(" "); //check where was the last space in the string
mBuffer.insert(it, "\n"); //put a newline there
entry.body.setString(mBuffer); //update body string
//after putting the newline update the height by one line height (represented by header height, always one line) and update pagesize
entry.entryHeight += entry.header.getLocalBounds().height;
mPages.back().pageSize.y += entry.entryHeight;
//check if entry has reached the end of page
if (mActiveTheme->pageLayout[mLeftRight].height - mPages.back().pageSize.y < 100)
{
//if yes mark entry as unfinished, push it into the page, and reset the height of the buffer entry
entry.isFinished = false;
mPages.back().page.push_back(entry);
entry.entryHeight = 0;
//push a new page and toggle the pagemark
mPages.push_back(Page());
if (mLeftRight == 0)
mLeftRight = 1;
else
mLeftRight = 0;
//move the entry to the other page, clear the string buffer and the body string
entry.body.setPosition(mActiveTheme->pageLayout[mLeftRight].left, mActiveTheme->pageLayout[mLeftRight].top + entry.entryHeight + mPages.back().pageSize.y);
mBuffer.clear();
entry.body.setString("");
entry.header.setString("");
}
} //once entry passes these to if statements go on with the next stream chars until once again right side of the page is reached
}
//all stream chars read, mark entry as finished and push it to the page
entry.isFinished = true;
mPages.back().page.push_back(entry);
}
//all entries parsed, mark filled flag true
mPagesFilled = true;
}
else
{ }
I suspect i had to mess something because now even after commenting out the part for vertical breaking the result is very similar to the 2nd picture, but can't seem to find the error. Quick breakdown:
mActiveTheme->pageLayout[mLeftRight] - this is a vector of sf::IntRect
objects that specify the position of pages in a selected theme and
mLeftRight is an int variable keeping track of whether at the moment a
left or right page is being used
Entry object is a struct of a date and std::string and mViewedQuest->questEntries is a vector of Entry objects
mPages is a vector of PageEntry objects, these in turn are structs containing the following
PageEntry()
{
};
sf::Text header; //header containing the date of an Entry
sf::Text body; //entry text
int entryHeight; //stores the current height of the object
bool isFinished; //indicator if an entry is split among pages or not
ss is a stringstream
pageSize in mPages.back().pageSize is an sf::Vector2i storing the height of all PageEntry objects contained withing a single page

Getting stuck in an infinite loop

I had my program running smoothly, and then after commenting it and adding some final touches, it stopped working on me. The function that I am having problems with is using several objects/functions defined elsewhere, so I am just wondering if someone can affirm that my logic is correct and that the infinite loop is not a product of a syntax error. Thanks for your time, here is the problem I'm having:
If the cashier started a new order and wants to close his order, T is typed in. However, when trying to exit an order and loop back to the start of while(moreCustomers), nothing is happening. I am trying to exit the while(moreItems) loop by setting moreItems = false;, but after doing that, it gets stuck in the while(moreItems) loop and does not go back to while(moreCustomers). Does the syntax make sense, and should I be able to break the loop by setting moreItems = false;?
bool moreCustomers = true;
while (moreCustomers)
{
// get input to start new order or close register
drawInstruct("Enter N to start a new order or E to\n close the register.");
char* setFmt = "#"; // the input must be a letter
char input[7]; // char array that stores input from cashier
s.GetStr(xLeftCoord + 1, yTopCoord + 1, input, 1, setFmt, true);
for(int x = 1; x < 10; x++) // clear the input field
{
s.ClearScreenPos(x, 1);
}
if (input[0] == 'N') // if a new order is requested
{
bool moreItems = true;
while (moreItems)
{
getInput(input);
if(input[1]) // if input is not a single char
{
if (input[0] == 'M') // get the desired number of multiples for the current item and update the tape and display area accordingly
{
custTape.handleMultiples(atoi(input)); // adds multiples to tape
curVal = isUPC->price * (atoi(input)); // updates the current item price
drawDisplayArea(curVal); // updates the display area
}
else // invalid number of multiples, prompt for new multiple
{
drawInstruct("Invalid command. Please try again.");
s.Delay();
}
}
else if (input[0] == 'T') // close the order
{
drawInstruct("Order cancelled.");
s.Delay();
moreItems = false; // customer order is complete, exit loop
}
else // invalid command, get new input from the cashier
{
drawInstruct("Invalid command. Please try again.");
s.Delay();
}
}
}
else if (input[0] == 'E') // close the register
{
moreCustomers = false; // no more customers, exit the program
}
else // invalid command, get new input from the cashier
{
drawInstruct("Invalid Command. Please try again.");
s.Delay();
}
}
I can't exit else if(input[0] == 'T'), and any commands I enter in after moreItems = false; work correctly.
I'd set a breakpoint on the first moreItems = false; line to see if it is ever being hit. My guess is that it is not. You've tagged the question with Visual Studio, so if that is what you're using see this link for how to set a breakpoint:
http://msdn.microsoft.com/en-us/library/vstudio/k80ex6de%28v=vs.100%29.aspx
Basically a breakpoint causes your program to stop at that line. Also try setting a breakpoint on this line:
if (input[0] == 'N')
Run the program, press a key, and wait for the breakpoint to be hit. Then use the "Step Over" option on the Debug menu. This runs your program line by line, each time you press "Step Over" (F10 does this too, much quicker). Keep stepping to see what path of execution occurs through your code. You may also be able to hover over variables to see their values.
Theres loads on the net about debugging with visual studio, but if you master the above you'll be well away

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.

Am I using the getch() function from the ncurses library incorrectly?

I am writing a Pacman game in c++ using the ncurses library, but I am not able to move the Pacman properly. I have used getch() to move it it up, down, left and right, but it only moves right and does not move anywhere else when I press any other key.
This is a code snippet for moving up. I have written similar code with some conditions altered accordingly for moving left, right and down.
int ch = getch();
if (ch == KEY_RIGHT)
{
int i,row,column;
//getting position of cursor by getyx function
for (i=column; i<=last_column; i+=2)
{
//time interval of 1 sec
mvprintw(row,b,"<"); //print < in given (b,row) coordinates
//time interval of 1 sec
mvprintw(row,(b+1),"O"); //print "O" next to "<"
int h = getch(); //to give the option for pressing another key
if (h != KEY_RIGHT) //break current loop if another key is pressed
{
break;
}
}
}
if (condition)
{
//code to move left
}
Am I using getch() wrong, or is there something else I have to do?
Many of the "special" keys on a keyboard -- Up, Down, Left, Right, Home, End, Function keys, etc. actually return two scan codes from the keyboard controller back to the CPU. The "standard" keys all return one. So if you want to check for special keys, you'll need to call getch() twice.
For example up arrow is first 224, then 72.
261 is consistent with KEY_RIGHT (octal 0405 in curses.h). That tells us at least that keypad was used to allow getch to read special keys.
The fragment shown doesn't give clues to how it was incorporated into the rest of the program. However, the use of getch in a loop is likely a source of confusion, since on exiting the loop the value is discarded. If you expect to do something different (from KEY_RIGHT), you could use ungetch to save the (otherwise discarded) value within the loop, e.g.,
if (h != KEY_RIGHT) //break current loop if another key is pressed
{
ungetch(h); //added
break;
}
Doing that will allow the next call to getch to return the key which exits the loop.