Keeping aspect ratio in C (ncurses) - c

I need to make a game using ncurses in C.
The game needs to be 80(char)x24(char).
I need an status bar on the bottom (5 char) so I hardcoded it.
I made this loop to keep things centralized, but I can't figure out how to keep the aspect ratio when the terminal screen is resized.
Is there any way to do it using ncurses?
I need the screen to always stay at least 80x24, perhaps using fullscreen mode always, I don't really need screen resizing.
Here's the loop to keep things in their places (status bar poorly hardcoded, the aspect ratio is a mess)
/* LOOP TO CENTRALIZE FOR ANY RESIZING */
while (1){
getmaxyx(stdscr, yMax, xMax);
clear();
mvprintw(yMax/24, xMax/80, "BEG");
mvprintw(yMax/2, xMax/2, "CENTER %d %d", yMax, xMax);
attron(COLOR_PAIR(1)); /* bottom status bar (5 lines) */
int i, j;
for ( j=(yMax-5) ; j <= yMax ; j++){
for ( i=0 ; i <= xMax ; i++ ){
mvprintw(j, i, " ");
}
}
attroff(COLOR_PAIR(1));
refresh();
}

Unless your program reads input, e.g., calls getch, ncurses will continue using the original screen-size (and look confused). When you call getch after ncurses receives a SIGWINCH, it returns KEY_RESIZE, and at that point ncurses updates its screen-size.

Related

How to display a moving array in the output console?

What's the better way than what I have done below:
system("cls") does the job but clearing the screen will later on mess with whatever that I want to display, the other negative side effect is the annoying blinks.
#include <stdio.h>
#include <stdlib.h>
int a[5]={1,0,1,1,0};
int b[5]={0,0,1,1,1};
int main()
{
srand(time(NULL));
while(1){
//display the arrays
for(int i=0; i<=4; i++){
printf("%d ",a[i]);
}
printf("\n");
for(int i=0; i<=4; i++){
printf("%d ",b[i]);
}
//shift every cell by 1
for (int i = 4 ; i >= 0; i--) {
a[i] = a[i - 1];
b[i] = b[i - 1];
}
sleep(1);
system("cls");
//keep generating 0s and 1s for the 1st cell of arrays
a[0] = rand() %2;
b[0] = rand() %2;
}
}
Without platform/terminal specific code that is not possible as there is no platform independent way to place the cursor or clear the screen.
The best you can do that is largely platform independent is return to the start of the current line, or back-space on the current line. That is to say you can only move the cursor backward on the current cursor line:
What you can do is:
Replace printf("\n"); with printf("\t");
Replace system("cls") ; with:
printf("\r");
fflush(stdout) ;
The output will be on one line with a TAB separation, and the line will be overwritten on each iteration.
1 1 1 0 1 1 0 1 0 0
Failing that you can either:
Use a platform independent console library such as ncurses,
On windows use the native Windows Console API.
Where supported use ANSI escape sequences.
The last option is simplest and while for a long time was not supported in Windows, Windows 10 now supports ANSI ESC sequences, so there are few reasons not to use that for this simple screen handling.
For example ESC[0;0H moves the cursor to the top-left. In this case you would simply replace the "cls" with:
printf( "\x1b[0;0H" ) ;
Note that in this case you also need either a newline or fflush(stdout) before the sleep() to ensure the second line is output before the clear screen:
printf("\n"); // Force output flush
sleep(1);
printf( "\x1b[0;0H" ) ; // Home cursor
If you have other content on the screen before this and you don't want to redraw everything, you could move the cursor by:
printf("\n"); // Force output flush
sleep(1);
printf( "\x1b[2A" ) ; // Cursor Up two lines

ncurses displays characters horizontally instead of vertically

I am writing a simple game in console using ncurses on linux and I have a very odd problem and I cannot find out what's wrong in my code the problem is that when I run my program my screen should look like this
which it does but after my draw() function runs more than 10 times i get something like this
here is my code:
#include <stdio.h>
#include <ncurses.h>
#include <stdlib.h>
#define END endwin();return 0
#define LN 5
#define RN 100
int display [LN][RN] = {
// 1 2 3 4 5 6 7 8 9 q w e r t y u
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//1
{0,0,'#',0,0,0,0,0,0,0,0,0,0,0,0},//2
{0,0,'#',0,0,0,0,0,0,0,0,0,0,0,0},//3
{0,0,'#',0,0,0,0,0,0,0,0,0,0,0,0},//4
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} //5
};
void setup(){//cr
initscr();
cbreak();
nodelay(stdscr,TRUE);
keypad(stdscr, TRUE);
noecho();
curs_set(0);
for(int i = 0 ; i < RN ; i++){
display[LN - 1][i] = 1;
}
return;
}
void draw(){
system("clear");
for(int i = 0; i < LN ; i++){
for(int j = 0; j < RN ; j++){
if(display[i][j] == 1){
addch(ACS_HLINE);
}
else if(display[i][j] == 0){
addch(' ');
}
else printw("%c",display[i][j]);
}
addch('\n');
}
refresh();
system("sleep 0.3");
return;
}
and I'm using arch
[n]curses keeps an internal map of what the screen is supposed to look like. It uses that map to do efficient updates when you call refresh(). This only works if the library is exclusively in control of the output to the terminal.
When you do terminal output through some other method, like system("clear"), the library no longer has an accurate map of the state of the terminal. The next update sends the escape sequences and printable strings necessary to go from state A to state B, but the terminal is actually in state C, and you end up with something crazy.
There is a curses erase() function which clears the screen, which you should use instead. It's equivalent to using a bunch of addch calls to write blanks in every position.
There's also a clear() function, which does an erase() and a clearok(TRUE), which tells the library to throw away its internal map and redraw the whole screen. This can be used to recover from the situation when something bad has happened, corrupting the screen. So, ironically, you could have gotten away with system("clear") if you'd done a clear() afterward.

C - Creating animation in terminal - auto-updating 2D array

I'm trying to make an animation which will make a basic circle from dots. I got stuck, because i do not know how to make an array to auto-update herself to make an animation. My program has an issue, because it shows only the last point on circle and other, previous points has vanished due to system("clear") command, but i do not know how to make it the proper way.
Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(void){
char tab[43][132];
int a, b, t;
double x_kropki, y_kropki;
for (t=0 ; t<360 ; t++) {
x_kropki=floor(10*cos((t*pi)/180))+60;
y_kropki=floor(10*sin((t*pi)/180))+20;
for (a=0 ; a<43 ; a++, printf("\n")) for (b=0 ; b<132 ; b++) {
if ((int)y_kropki==a && (int)x_kropki==b){
tab[a][b]='.';
printf("%c", tab[a][b]);
}else {
tab[a][b]=' ';
printf("%c", tab[a][b]);
}
}
system("clear");
}
return 0;
}
What is the animation supposed to look like? Do you want the circle to grow slowly? Then you need to add a delay (sleep or similar) or else it will finish the whole process too quickly for the screen to draw and for your eyes to notice.
Also you should not clear the progress after every new dot (of the 360 dots in total, it seems). To achieve that, you will need to change your approach a bit. Here's what the loop could look like:
Draw nothing
Draw dot 1
Clear
Draw dot 1 and 2
Clear
Draw dot 1 and 2 and 3
You see that after clearing, you need to repeat printing the progress so far. At loop iteration 180, you need to print the last 179 dots again plus the 180th. Wait a few milliseconds, then clear, then the same for 181.
How you do that? You repeat the for loop:
int dot, maxDots;
for (maxDots = 0; maxDots < 360; maxDots++) {
for (dot = 0; dot < maxDots; dot++) {
// your location calculations and printing for each dot
}
system("clear");
}
This should at least give you some kind of growing circle. But you will notice that the printing approach is hard to get right, because once a line has been finished, you cannot go back. If you start at the top and go your way around 180 degrees, down line by line, you will then need to go up line by line until you reach the top again. That won't work easily. Instead of printing directly, as #Weather Vane suggested, store the to-be-printed result of each animation stage in a buffer. That is an abstraction of on-screen coordinates. A very simple approach would be a two-dimensional array that you can manipulate freely, then print the whole array en bloc.

C - how to read color of a screen pixel (FAST)? (in Windows)

So I am looking for a way to read a color of a screen pixel in C code.
I already found implementation in C for *nix (which uses X11/Xlib library, that as I understood is for *nix systems only) and I tried the code on a linux machine, and it ran pretty fast (it reads 8K of pixels in about 1 second).
Here's the code in C that I've found and forked:
#include <X11/Xlib.h>
void get_pixel_color (Display *d, int x, int y, XColor *color)
{
XImage *image;
image = XGetImage (d, RootWindow (d, DefaultScreen (d)), x, y, 1, 1, AllPlanes, XYPixmap);
color->pixel = XGetPixel (image, 0, 0);
XFree (image);
XQueryColor (d, DefaultColormap(d, DefaultScreen (d)), color);
}
// Your code
XColor c;
get_pixel_color (display, 30, 40, &c);
printf ("%d %d %d\n", c.red, c.green, c.blue);
And I was looking for equivalent solution for Windows as well.
I came across this code (I've put the code about reading screen pixel in a 'for' loop):
FARPROC pGetPixel;
HINSTANCE _hGDI = LoadLibrary("gdi32.dll");
if(_hGDI)
{
pGetPixel = GetProcAddress(_hGDI, "GetPixel");
HDC _hdc = GetDC(NULL);
if(_hdc)
{
int i;
int _red;
int _green;
int _blue;
COLORREF _color;
ReleaseDC(NULL, _hdc);
for (i=0;i<8000;i++)
{
_color = (*pGetPixel) (_hdc, 30 ,40);
_red = GetRValue(_color);
_green = GetGValue(_color);
_blue = GetBValue(_color);
}
ReleaseDC(NULL, _hdc);
printf("Red: %d, Green: %d, Blue: %d", _red, _green, _blue);
}
FreeLibrary(_hGDI);
(using gdi32.dll and windows.h...)
and the 'for' portion of the code (where we read 8K of pixels) runs ALOT slower than the solution in C.
it takes 15 seconds to finish compared to 1 second with X11/Xlib.h library!
So, how can I make it better? or there is any other better and FASTER implementation to read pixel's colors with C code in Windows machine?
Thanks ahead!
I would suggest using loop unwinding. Basically, what this does is execute multiple cycles of your loop in a single iteration:
// Loop the equivalent of `n` cycles, ignoring the least significant bit
for (unsigned int i = 0; i < (n & ~0x01); i += 2)
{
do_some_operation(i);
do_some_operation(i + 1);
}
// Perform the last cycle manually, if one needs to be completed
if (n & 0x01)
{
do_some_operation(n - 1);
}
In this code, the loop ignores the least significant bit of n (which determines the parity of n) so that we are safe to increment i by 2 and perform the equivalent of 2 cycles in just 1 cycle, meaning that this loop is ~2 times faster than a conventional for (unsigned int i = 0; i < n; i++) loop. The final if statement checks the parity of n. If n is odd, the last cycle of the loop is performed.
Of course, this could be reimplemented to increment i by more than 2, but this would become increasingly complex. There is also an alternative to this, Duff's Device. It is basically the same idea, but uses a switch/case block.
After many tests, I've found that just about /anything/ you do to read pixels off the screen in windows using the GDI takes ~16ms (or about 1 frame) whether it is reading a single pixel, or reading even a small area with BitBlt. There doesn't seem to be any clear solution. I will be experimenting with the media libraries to see if I can get anywhere, but the Internet is pretty sure doing anything like this in Windows is just an awful mess, and really terrible things have to be done to do things like VNC or Fraps.

gotoxy() function using printf() 's position

Hello there
i am working a project which need the gotoxy() function
i have read gotoxy() implementation for Linux using printf
i wonder why the
void gotoxy(int x,int y)
{
printf("%c[%d;%df",0x1B,y,x);
}
need to change the x y order in printf, is that just to fit the coordinate system?
in my way, i change it to printf("%c[%d;%df",0x1B,x,y) to meet my needs
stil, during my using this gotoxy() in for loop like this:
for( int i = 0; i < 12; i++ ) {
for( int j = 0; j < 12; j++ ) {
gotoxy( i , j );
usleep(500000);
}
}
when i = 0 and i = 0, the cursor are on the first row
i wonder why cursor does't go to second row when i = 1?
OP: "why the need to change the x y order".
The cursor position command's format is
Force Cursor Position <ESC>[{ROW};{COLUMN}f
The need arises because to match that format and have your y variable as the ROW, y comes first. (You could rotate your screen 90 degrees instead).
OP: why cursor does't go to second row when i = 1?
The home position, at the upper left of the screen is the Origin being line 1, column 1
Note: You can put the escape character in the format,
printf("\x1B[%d;%df", y, x);
fflush(stdout); // #jxh
The order of x and y matters because the names of the variables have no meaning to the operation of the gotoxy() function.
That function is outputing a terminal command sequence that moves to the specified coordinates. When the terminal sees that command sequence and processes it, y is expected first.
By the way, be careful with this solution as this is highly dependent on the type of terminal within which the program is run. In order to get wide terminal support with random movement and "drawing" on a terminal screen, ncurses or curses are your best bet. They are challenging to learn at first though.
The column and row positions do not start at 0 when using the terminal escape sequences. They start at 1.
You need to flush stdout to see the cursor move.
void gotoxy(int x,int y)
{
printf("%c[%d;%df",0x1B,y,x);
fflush(stdout);
}
GotoXY is a function or procedure that positions the cursor at (X,Y), X in horizontal, Y in vertical direction relative to the origin of the current window. The origin is located at (1,1), the upper-left corner of the window.
The above C code worked after I converted to Android script (I think I'm using Korn Shell).
function gotoxy()
{
printf "\033[$1;$2f"
}
I've beenn using "\033[r;cH" all this time and it was working.

Resources