So, I have a C++ class that represents rectangles, I use the WriteConsoleOutputCharacter function that outputs faster than cout or printf(), I've already made a program that prints the rectangle but I'm having trouble clearing the rectangle out.
From my understanding from msdn this function can print unicode characters or 8-bit characters from the console's current code page. Anyway, when I wanted to print backspace in order to clear the rectangle it didn't work and it prints something else(◘). When I tried to print backspace through it's hex code (0x008) it printed the symbol again.
The code is quite simple:
const char clr[] ="\b";//Thar the array I'm printing
void rect_m::clr_ar()
{
Ex = Vx + Lx;//These variables are the rectangle's sizes
Ey = Vy + Ly;
HANDLE mout = GetStdHandle(STD_OUTPUT_HANDLE);
//The loops cover the rectangle area
for (SHORT i = Vy; i < Ey; i++)
{
for (SHORT j = Vx; j < Ex; j++)
{
WriteConsoleOutputCharacter(mout, clr, strlen(clr), { j,i }, &dwWritten);
}
}
}
Well, all I want is a way to print backspace with the WriteConsoleOutputCharacter function to clear the text out (and not by printing spaces over it). I know that's a very basic mistake and that there is a better way. So,can someone tell me please what's wrong with my code?
for clear rectangle area we can use ScrollConsoleScreenBufferW for fill selected rectangle with blank characters. note that blank characters is equal to empty space, which we can view in test, if call ReadConsoleOutputCharacter at begin on yet empty console:
COORD xy{};
ULONG n;
WCHAR c;
ReadConsoleOutputCharacterW(hConsoleOutput, &c, 1, xy, &n);
//c == ' ';
so full code can look like:
BOOL cls(const SMALL_RECT* lpScrollRectangle = 0)
{
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hConsoleOutput, &csbi))
{
CHAR_INFO fi = { ' ', csbi.wAttributes };
if (!lpScrollRectangle)
{
csbi.srWindow.Left = 0;
csbi.srWindow.Top = 0;
csbi.srWindow.Right = csbi.dwSize.X - 1;
csbi.srWindow.Bottom = csbi.dwSize.Y - 1;
lpScrollRectangle = &csbi.srWindow;
}
return ScrollConsoleScreenBufferW(hConsoleOutput, lpScrollRectangle, 0, csbi.dwSize, &fi);
}
return FALSE;
}
Related
I'm working on a console game. It uses a screen buffer to refresh the console window after updating the map.
Here's the main while loop.
while (true) {
//player.doStuff(_kbhit());
//map.update();
WriteConsoleOutputCharacter(
console.getScreenBuffer(),
(LPWSTR)map.getScreen(),
map.getWidth() * map.getHeight(),
{ 0, 0 }, console.getBytesWritten()
);
Sleep(1000 / 30);
}
Before this loop, I'm getting the layout of the map from a .txt file:
class Map {
int width, height;
wchar_t* screen;
public:
wchar_t* getScreen() {
return screen;
}
void setScreen(std::string layoutFile, std::string levelDataFile) {
std::ifstream levelData(levelDataFile);
levelData >> width >> height;
screen = new wchar_t[(width + 1) * height];
levelData.close();
std::wifstream layout(layoutFile);
std::wstring line;
for (int j = 0; j < height; j++) {
std::getline<wchar_t>(layout, line);
for(int i = 0; i < width; i++) {
screen[j * width + i] = line.at(i);
}
screen[width * (j + 1)] = L'\n';
}
layout.close();
}
};
map.setScreen("demo.txt", "demo_data.txt");
The problem is that the printed map displays as one string without any line breaks, like this:
00000__00000
When I expected it to look like this instead:
0000
0__0
0000
I tried adding L'\n', L'\r\n' after every line written, but it doesn't work.
In short
There are two independent problems here:
The first one is that your newline characters get overwritten.
The second, once you've corrected the first, is that the windows console API does not handle newlines
More details
The problem with the overwriting
I assume that the width does not include the trailing newline at the end of each line, since your allocation for the screen is:
new wchar_t[(width + 1) * height]; // real width is width + 1 for '\n'
Now looking at your logic, the last '\n' that you add to the line, is set at:
screen[ width * (j + 1) ] // same as screen[ j * width + width ]
This seems fine according to your indexing scheme, since you copy the layout characters to:
screen[ j * width + i ]` // where `i` is between `0` and `width-1`
so the newline would be at screen[ j * width + width ].
Unfortunately, with your indexing formula, the first character of the next line overwrites the same place: replacing j with j+1 and i with 0 gives:
screen[ (j + 1) * width + 0 ]
which is
screen[ (j + 1) * width ] // same location as screen [ width * (j+1)]
The solution for having trailing newlines on very line
Correct your indexing scheme, taking into account that the real width of the line is width+1.
So the indexing formula becomes:
for(int i = 0; i < width; i++) {
screen[j * (width+1) + i] = line.at(i);
}
and of course the trailing newline:
screen[j * (width+1) + width] = L'\n'; // self-explaining
screen[(j+1) * (width+1) -1] = L'\n'; // or alternative
The problem with the console API
The WriteConsoleOutputCharacter() provides no real support for newlines and control characters. These are diplayed as a question mark.
The documentation refers to a possibility to get those control characters handled depending on the console mode, but I've tried with Windows 10, and even with the variant WriteConsoleOutputCharacterA() (to be sure to exclude issues with wide characters), it simply does not work.
You have two solutions to make this work, but the two require some rework:
display the output line by line and control the cursor position accordingly
use the WriteConsoleOutput() which allows you to specify the target rectangle (heigth and width) and write the characters in a rectangle without need for a newline. Unfortunately, the array shall then be of CHAR_INFO instead of simple characters.
Example for the second way:
std::string w = "SATORAREPOTENETOPERAROTAS";
SMALL_RECT sr{ 2, 2, 6, 6 };
CHAR_INFO t[25];
for (int i = 0; i < 25; i++) { t[i].Char.AsciiChar = w[i]; t[i].Attributes = BACKGROUND_GREEN; }
WriteConsoleOutputA(
hOut,
t,
{ 5,5 },
{ 0,0 },
&sr
);
I followed Custom edit control win32 second answer to create custom edit control, but the problem is that, I render every letter separate, so I can have different text colors. And for every render call, I have to calculate text offset to start rendering from top of the screen (0,0), so i don't have to render whole text. And if I have a 200kB file and scroll to bottom of the file (and do some editing there), there is just too much lag, since I have to go trough all that text and find all '\n' (indicates line number) for offset.
Render function:
int Screen_X = 0, Screen_Y = 0;
size_t Text_YOffset = Calc_TextYPos(m_Screen_YOff); //Screen pos(0,0) to text
size_t Text_Size = m_Text.size();
COLORREF Text_ColorOld = 0;
for (size_t pos = Text_YOffset; pos < Text_Size; pos++) {
if (m_Text.at(pos) == L'\n') {
Screen_Y++; Screen_X = 0;
continue;
}
if (Screen_X < m_Screen_XOff) { Screen_X++; continue; }
if (m_Screen_MaxX < Screen_X) continue;
if (m_Screen_MaxY < Screen_Y) break;
if (m_Text_Color.at(pos) != Text_ColorOld) {
Text_ColorOld = m_Text_Color.at(pos);
if (SetTextColor(hDC, Text_ColorOld) == CLR_INVALID) {
MB_ERR("'SetTextColor' Failed!");
PostQuitMessage(-1);
}
}
CHECK_ERR(TextOut(hDC, (Screen_X - m_Screen_XOff) * m_Char_Width, Screen_Y * m_Char_Height, &m_Text.at(pos), 1), ERR_MSG_TEXT_OUT);
Screen_X++;
}
Calc_TextYPos:
size_t Edit_Control::Calc_TextYPos(int Screen_y) {
if (Screen_y == 0) return 0;
size_t Offset = 0;
size_t Text_Size = m_Text.size();
for (size_t pos = 0; pos < Text_Size; pos++) {
if (m_Text.at(pos) == L'\n' && Screen_y != 0) {
Screen_y--;
Offset = pos + 1;
}
if (Screen_y == 0) return Offset;
}
return Offset;
}
Am I taking the wrong path here and should use "different algorithm" for rendering text (in different colors), or if not, how can I optimize this code? I like this approach (for caret) since, selecting text is really easy.
I also came across What is the fastest way to draw formatted text in the Win32 API? but, it doesn't answer my question. It only mentions about rendering function (ExtTextOut), but I don't need that. I need a fast way of calculating line offset(s) on big strings.
I'm trying to make a simple console game, and I am making a sort of console graphics engine that draws a screen with a map and some text. For some reason the engine writes strange characters to the console, rather than what is meant to be written.
This engine takes two 2d vectors that describe the characters and colors to be used in the console. I am using WriteConsole() to write to the console, and using SetConsoleTextAttribute() to change the color of the text as I draw. For some reason, when I try to print text, it writes some really weird characters that have no relation to the characters that are meant to be printed. Colors work just fine though. My characters are stored as TCHARs and my colors as ints.
My function to actually draw the screen:
void update()
{
for (int y = 0; y < SCREEN_HEIGHT; y++) //loop through all of the tiles
{
for (int x = 0; x < SCREEN_WIDTH; x++)
{
if (screen.at(y).at(x) != buffer.at(y).at(x) && screenColors.at(y).at(x) != bufferColors.at(y).at(x)) //only draw the tile if it has changed
{
pos.X = x; //set coords of cursor to tile to be drawn
pos.Y = y;
SetConsoleTextAttribute(hStdOut, bufferColors.at(y).at(x)); //set the text color
SetConsoleCursorPosition(hStdOut, pos); //move the cursor to the tile to be drawn
WriteConsole(hStdOut, &(buffer.at(y).at(x)), 1, dw, NULL); //actually draw the tile
screen.at(y).at(x) = buffer.at(y).at(x); //update 2d screen vector (used to read what is on the screen for other reasons)
}
}
}
SetConsoleCursorPosition(hStdOut, restPos); //move the cursor away, so it doesn't look ugly
}
My function to write to the buffer vector:
void draw2dVector(int x, int y, vector<vector<TCHAR>> draw, vector<vector<int>> colors)
{
for (unsigned int drawY = 0; drawY < draw.size(); drawY++)
{
for (unsigned int drawX = 0; drawX < draw.front().size(); drawX++)
{
buffer.at(y + drawY).at(x + drawX) = colors.at(drawY).at(drawX); // <- I found the problem. I am writing color ints to the buffer.
bufferColors.at(y + drawY).at(x + drawX) = colors.at(drawY).at(drawX);
}
}
}
My function to convert strings to vector<TCHAR>s.
vector<TCHAR> stringToTCHARvector(string str, int strLen) //convert a TCHAR string to a TCHAR vector
{
vector<TCHAR> result;
for (int i = 0; i < strLen; i++) //loop through the characters in the string
{
result.push_back(str[i]); //add the character to the TCHAR vector
}
return result;
}
I expect the output to look something like this:
###....### Inventory:
####....## Gold Piece
#..##...##
#......###
###.#..###
##########
But instead it gives me this:
êêêêêêêêêê *insert strange characters here, because stack overflow doesn't
êêêêêwwêêê show them*
êêêêwwwwww
êêêêwwwwww
êêêêêwwwww
êêêêêwwwww
UPDATE:
It turns out that the problem was that I was writing the ints from my color vector to my TCHAR buffer. I have resolved the issue. Thank you for your help.
I need a method that acts like system color without using a system call. I know there is setConsoleTextAttribute() but that doesn't fill the full foreground and background with color only new characters. I am using windows7 although i would like to make this compatible with all windows
As per your comments, here is a more complete solution to the problem you are trying to solve. It is based on my original answer (which can be found at the end of this answer).
I discovered one limitation in Windows API, namely that it cannot read the entire screen buffer for a default console mode window of 80 columns by 300 rows. It results in a ERROR_NOT_ENOUGH_MEMORY error due to insufficient Windows process heap (as best as I can tell from some Google searching). I wound up implementing a wrapper around the XxxConsoleOutput functions to support automatic subdivision of the screen buffer region as needed until the function succeeds or it fails to read a 1 X 1 (single character) region.
Again, this code is probably not perfect, it is meant to explain concepts, not to (necessarily) provide you with a complete solution. However, it now fills the entire console (previously I only filled the visible portion of the window) and sets the console's text attribute for future output.
Skip this code to read the original answer.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <iostream>
#include <vector>
using namespace std;
// Split a rectangular region into two smaller rectangles
// based on the largest dimension.
void SplitRegion(
SHORT width, SHORT height,
COORD dwBufferCoord, const SMALL_RECT& readRegion,
COORD& dwBufferCoordA, SMALL_RECT& readRegionA,
COORD& dwBufferCoordB, SMALL_RECT& readRegionB)
{
dwBufferCoordA = dwBufferCoordB = dwBufferCoord;
readRegionA = readRegionB = readRegion;
if (height >= width)
{
SHORT half = height / 2;
dwBufferCoordB.Y += half;
readRegionB.Top += half;
readRegionA.Bottom = readRegionB.Top - 1;
}
else
{
SHORT half = width / 2;
dwBufferCoordB.X += half;
readRegionB.Left += half;
readRegionA.Right = readRegionB.Left - 1;
}
}
// Utility function to figure out the distance
// between two points.
template <typename type>
inline type DiffHelper(type first, type second)
{
return (second >= first) ? (second - first + 1) : 0;
}
// A template that wraps up the shared code common between
// reading and writing the screen buffer. If it is ever
// given a region of zero width or height, it will
// "succeed". If it ever tries to subdivide a 1 by 1
// region, it will fail.
template <typename lpBufferType>
BOOL XferConsoleOutputWrapper(
HANDLE hConsoleOutput,
lpBufferType lpBuffer,
COORD dwBufferSize,
COORD dwBufferCoord,
SMALL_RECT& xferRegion,
BOOL (WINAPI * xferConsoleOutput)(
HANDLE, lpBufferType, COORD, COORD, PSMALL_RECT))
{
SHORT width = DiffHelper(xferRegion.Left, xferRegion.Right);
SHORT height = DiffHelper(xferRegion.Top, xferRegion.Bottom);
if ((width == 0) || (height == 0))
{
return TRUE;
}
BOOL success = xferConsoleOutput(hConsoleOutput,
lpBuffer, dwBufferSize, dwBufferCoord, &xferRegion);
if (!success)
{
if ((GetLastError() == ERROR_NOT_ENOUGH_MEMORY) &&
((width * height) > 1))
{
COORD dwBufferCoordA, dwBufferCoordB;
SMALL_RECT xferRegionA, xferRegionB;
SplitRegion(
width, height,
dwBufferCoord, xferRegion,
dwBufferCoordA, xferRegionA,
dwBufferCoordB, xferRegionB);
success =
XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoordA, xferRegionA, xferConsoleOutput) &&
XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoordB, xferRegionB, xferConsoleOutput);
}
}
return success;
}
// ReadConsoleOutput failed to read an 80 by 300 character screen
// buffer in a single call, resulting in ERROR_NOT_ENOUGH_MEMORY.
// ReadConsoleOutputWrapper will subdivide the operation into
// smaller and smaller chunks as needed until it succeeds in reading
// the entire screen buffer.
inline BOOL ReadConsoleOutputWrapper(
HANDLE hConsoleOutput,
PCHAR_INFO lpBuffer,
COORD dwBufferSize,
COORD dwBufferCoord,
SMALL_RECT& readRegion)
{
return XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoord, readRegion, ReadConsoleOutput);
}
// WriteConsoleOutputWrapper will subdivide the operation into
// smaller and smaller chunks as needed until it succeeds in writing
// the entire screen buffer. This may not be necessary as
// WriteConsoleOutput never failed, but it was simple to implement
// so it was done just to be safe.
inline BOOL WriteConsoleOutputWrapper(
HANDLE hConsoleOutput,
const CHAR_INFO* lpBuffer,
COORD dwBufferSize,
COORD dwBufferCoord,
SMALL_RECT& writeRegion)
{
return XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoord, writeRegion, WriteConsoleOutput);
}
void ConsoleFillWithAttribute(WORD fillAttribute)
{
// Get the handle to the output
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
// Get the information for the current screen buffer
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(hStdout, &info);
// Allocate a vector to hold the visible screen buffer data
vector<CHAR_INFO> buffer(info.dwSize.X * info.dwSize.Y);
// Initialize a couple of pointers to the begin and end of the buffer
CHAR_INFO* begin = buffer.data();
CHAR_INFO* end = begin + buffer.size();
// Start at the upper left corner of the screen buffer.
COORD coord;
coord.X = coord.Y = 0;
// Initialize the region to encompass the entire screen buffer.
SMALL_RECT region;
region.Left = region.Top = 0;
region.Right = info.dwSize.X - 1;
region.Bottom = info.dwSize.Y - 1;
// Read the buffer from the console into the CHAR_INFO vector.
ReadConsoleOutputWrapper(hStdout, buffer.data(), info.dwSize, coord, region);
// Change all the attributes to the specified fill attribute.
while (begin != end)
{
begin->Attributes = fillAttribute;
++begin;
}
// Write the buffer from the CHAR_INFO vector back to the console.
WriteConsoleOutputWrapper(hStdout, buffer.data(), info.dwSize, coord, region);
// Finally, set the console text attribute to the fill attribute
// so that all new text will be printed in the same manner as
// the attributes we just changed.
SetConsoleTextAttribute(hStdout, fillAttribute);
}
int main()
{
cout << "I would like to fill up the console with some text." << endl;
cout << "The quick brown fox jumped over the lazy dogs." << endl;
for (int i = 0; i < 100; ++i)
{
cout << ' ' << i;
}
cout << endl;
ConsoleFillWithAttribute(
BACKGROUND_BLUE | FOREGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN);
cout << endl;
cout << "This should also be printed in the new attribute" << endl;
return 0;
}
The original answer is below:
If I understand what you are asking, you want to be able to change the attributes for the entire console mode window. You might want to look into the ReadConsoleOutput and WriteConsoleOutput functions. You can use ReadConsoleOutput to read some or all of a console buffer into memory, then manipulate the attribute data as needed for your application, then use WriteConsoleOutput to write memory back to the console output buffer.
Here is some code that will change the attributes for the currently displayed portion of console (assuming output has not been redirected to a non-console handle):
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <iostream>
#include <vector>
using namespace std;
void ConsoleFillDisplayWithAttribute(WORD fillAttribute)
{
// Get the handle to the output
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
// Get the information for the current screen buffer
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(hStdout, &info);
// Calculate the size of the displayed portion of the screen buffer
COORD size;
size.X = (info.srWindow.Right - info.srWindow.Left + 1);
size.Y = (info.srWindow.Bottom - info.srWindow.Top + 1);
// Allocate a vector to hold the visible screen buffer data
vector<CHAR_INFO> buffer(size.X * size.Y);
COORD coord;
coord.X = coord.Y = 0;
// Read the buffer from the console into the CHAR_INFO vector
ReadConsoleOutput(hStdout, buffer.data(), size, coord, &info.srWindow);
// Initialize a couple of pointers to the begin and end of the buffer
CHAR_INFO* begin = buffer.data();
CHAR_INFO* end = begin + buffer.size();
// Change all the attributes to the specified fill attribute
while (begin != end)
{
begin->Attributes = fillAttribute;
++begin;
}
// Write the buffer from the CHAR_INFO vector back to the console
WriteConsoleOutput(hStdout, buffer.data(), size, coord, &info.srWindow);
}
int main()
{
cout << "I would like to fill up the console with some text." << endl;
cout << "The quick brown fox jumped over the lazy dogs." << endl;
for (int i = 0; i < 100; ++i)
{
cout << ' ' << i;
}
cout << endl;
ConsoleFillDisplayWithAttribute(
BACKGROUND_BLUE | FOREGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN);
return 0;
}
I'm a pretty novice c++ coder and I am starting to make a console adventure game.
My adventure game currently consists of a player character that walks around inside a console application window with an 80 character width x 40 lines.
I am not sure how to approach storing the maps for my game. Each map will consist of 80 x 40 ASCII characters with colour attributes.
Should I store each 80 x 40 map in its own char? so a single map would look something like...
int cHeight = 5; // Reduced size for this example
int cHeight = 10; // Reduced size for this example
// Set up the characters:
char map[cHeight][cWidth+1] = {
"1234567890",
"1234567890",
"1234567890",
"1234567890",
"1234567890",
};
CHAR_INFO mapA[cWidth * cHeight];
for (int y = 0; y < cHeight; ++y) {
for (int x = 0; x < cWidth; ++x) {
mapA[x + cWidth * y].Char.AsciiChar = map[y][x];
mapA[x + cWidth * y].Attributes = FOREGROUND_BLUE | Black; //I have an enum setup with background colours.
}
}
// Set up the positions:
COORD charBufSize = {cWidth,cHeight};
COORD characterPos = {0,0};
SMALL_RECT writeArea = {0,0,cWidth-1,cHeight-1};
// Write the characters:
WriteConsoleOutputA(wHnd, mapA, charBufSize, characterPos, &writeArea);
Im not sure if this is entirely the correct way to display the characters but I didn't think it was a good idea to just cout every character in the for loop.
So.. lets say my console window (in the above code) is 10 characters wide and 5 lines high.
In the above code I have a single map in the Char, so when loading each map I would put each one in their own array.
I was thinking of putting the entire map into a single Char, but then only displaying what I needed by offsetting the x and y in the for loop.
mapA[x + cWidth * y].Char.AsciiChar = map[y+offset][x+offset];
So the map would look more like this;
char map[cHeight][cWidth+1] = {
"1234567890ABCDEFGHIJ",
"1234567890ABCDEFGHIJ",
"1234567890ABCDEFGHIJ",
"1234567890ABCDEFGHIJ",
"1234567890ABCDEFGHIJ",
};
with the offset I could display '1234567890' on 5 rows separately from 'ABCDEFGHIJ' on 5 rows.
So in short I would like to know the most effective way to do this, should I have multiple Chars? Should I create a class? then I could store the characters an colours? (class' are still new to me in c++).
Should I draw the terrain only in the map and then add objects (houses, trees)?
Or just draw it all in the map manually?
I think I've just thought about this too long and need a bit of direction
Thanks!
The way I would do it would be to create a map of
Node* map[height][width]
This means you would create the map which are pointers to Node* elements and you could define the Node* element to be...
class Node{
char displayCharacter;
int posx,poxy
unsigned int r; //red
unsigned int g; //green
unsigned int b; //blue
unsigned int a; //alpha
display(); // This function will know how to display a node using the colour etc
};
Then you could for example if you wanted to create a house you would give it the center point of the model etc... to draw to a function
void createHouse(Node* center)
{
if((center->posh > 0)&&(center->posh< maxheight))
{
if(map[center->posy-1][center->posx]!=NULL)
{
map[center->posy-1][center->posx]->displayCharacter = '_';
map[center->posy-1][center->posx]->r = 255;
}
}
}
Then in main you would have something like...
while(true)
{
for(int i=0; i<maxheight; i++)
{
for(int j=0; j< maxwidth; j++)
{
map[i][j]->Display();
}
}
}
I hope all this sample code is of help to you and answered your question. I have not debugged or looked for any syntax errors. If there any errors in the code, you will have to fix them!
Good luck to you!