Skip to main content

Command Palette

Search for a command to run...

Getting Started with ncurses in C++

Updated
•4 min read•View as Markdown
Getting Started with ncurses in C++
D

An engineer with a curious mind and a love for systems that go beyond the surface.I enjoy exploring how things work under the hood — whether it's rendering pixels, routing audio, or handling low-level protocols. My work spans across game development, system programming, and occasionally web tech when the problem is worth solving. I’m driven by the desire to build things that are fast, efficient, and just a bit unconventional. This blog is my space to document experiments, challenges, and the weirdly satisfying bugs that teach you more than tutorials ever could.

Outside of tech, I enjoy observing how people use technology, and occasionally breaking things just to fix them better.

This is the first part of my terminal game development series.

Why the Terminal?

You might be wondering why we would build a game that runs in a terminal when we can simply pick a graphics library and make something that looks much better.

The reason is simple. When you are learning game development, it is very easy to get lost in visuals. You start worrying about animations, sprites, and how smooth everything looks, instead of understanding how a game actually works.

Building a game in the terminal removes that distraction.

When there are no graphics to hide behind, you are forced to focus on the core concepts of game development. Things like how a game loop works, how input is handled, how the game state changes, and how rendering is structured.

The goal here is not to make something pretty. The goal is to understand the logic behind it.

Once these fundamentals are clear, moving to a graphics library becomes much easier, because you already know what needs to happen. You are just changing how it is displayed.

Code for This Series

All the code written throughout this series is available on GitHub.

Each part has its own source file, and every article ends with a fully working version of the game up to that point.

You can find the repository here:
GitHub Link

Feel free to clone it, run the examples, and experiment with the code as you follow along.

Setting up ncurses!!

Setting up ncurses is actually pretty straightforward. Good news already.

That said, you do need a Unix-like environment. A regular Windows terminal will not work here.

Your options are:

  • Linux

  • macOS

  • Windows with WSL (recommended)

  • Windows with MSYS2 / MSYS shell

Once you have a proper shell, installing ncurses is simple.

Ubuntu/Debian/WSL (Ubuntu)

sudo apt install libncurses-dev

Arch Linux

sudo pacman -S libncurses-dev

Mac OS

brew install ncurses

Hello World!!

Now let’s write a Hello World program like a good programmer.

#include <ncurses.h>

int main() {
  initscr();                 /* Start curses mode           */
  printw("Hello World !!!"); /* Print Hello World          */
  refresh();                 /* Print it on to the real screen */
  getch();                   /* Wait for user input */
  endwin();                  /* End curses mode          */

  return 0;
}

The code is mostly self-explanatory.

Every ncurses program follows a simple rule.

You must call initscr() first. This initializes ncurses and takes control of your terminal. Internally, ncurses sets up the main screen window called stdscr.

Once ncurses is initialized, you are free to use its printing and input functions like printw() and getch().

Nothing is actually shown on the screen until you call refresh(). This is what tells ncurses to push everything it has drawn to the real terminal.

Finally, endwin() is equally important. It restores your terminal back to its normal state. Forgetting this is a great way to confuse your shell and annoy yourself.

As a rule of thumb:

  • initscr() always comes first

  • endwin() always comes last

  • everything else lives in between

Compiling and Running the Code

To compile an ncurses program, you need to link against the ncurses library.

Use the following command:

g++ <filename>.cpp -lncurses -o <executable_name>

This tells the compiler to include ncurses while building the program.

Once compilation succeeds, run the executable using:

./<executable_name>

If everything is set up correctly, you should see “Hello World” printed inside the terminal window.

If the program does not exit cleanly or your terminal looks broken, it usually means endwin() was not called properly.

Now that you have made your first program, let’s step back and understand how ncurses actually works.

How ncurses Works

ncurses works by treating your terminal window as a grid made up of rows (Y) and columns (X).

It gives you control over which character appears at each (x, y) position on this grid.
Keep in mind, we are controlling characters, not pixels.

Along with drawing to the screen, ncurses also allows us to capture keyboard input directly, without waiting for the Enter key. This is what makes real-time interaction possible.

Internally, ncurses uses a buffered drawing system.

When you call functions like clear() or printw(), ncurses does not update the screen immediately. Instead, it updates an internal buffer. Only when you call refresh() does ncurses flush this internal buffer to the actual terminal screen.

This approach helps prevent screen tearing and flickering, and it is the reason why most ncurses programs follow a clear → draw → refresh pattern.

Wrapping Up

In this part, we set up ncurses, wrote our first program, and built a basic understanding of how ncurses works behind the scenes.

Nothing fancy yet, and that is intentional.

At this point, you should be comfortable with:

  • setting up ncurses

  • compiling and running a program

  • understanding how ncurses controls the terminal

  • knowing why refresh() matters

This foundation is important, because everything that comes next will build on it.

In the next part, we will start dealing with keyboard input and remove the biggest limitation of our current program: the fact that it just waits and does nothing.

That is where things start getting interesting.

More from this blog

Code. Chaos. Curiosity. | Devesh's Devlog

12 posts