{"id":7500,"date":"2024-09-16T03:00:00","date_gmt":"2024-09-16T07:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=7500"},"modified":"2024-09-07T12:06:00","modified_gmt":"2024-09-07T16:06:00","slug":"terminal-size-and-ncurses","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=7500","title":{"rendered":"Terminal size and ncurses"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"7500\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">1    <\/span>\r\n<\/div><\/div>\n<p>When programs like <strong>vi<\/strong> first came along, terminals had a predefined size: usually 80 columns and 24 lines (actually 25 lines, but the last line was reserved to display status information by the terminal). While you ran your program, the terminal couldn\u2019t change its size, because the dimensions were locked in by hardware.<\/p>\n\n\n\n<p>But with the graphical desktop, programs now had to adapt to a <em>terminal emulator<\/em>\u2014a window that simulated a terminal. And while the terminal window was often given an initial size of 80 columns and 24 lines, the user could change those dimensions, such as by resizing the window on the desktop.<\/p>\n\n\n\n<p>Fortunately, <a href=\"https:\/\/www.both.org\/?p=7495\">programming with ncurses<\/a> provides an easy way to notify the program when the terminal size has changed. Let\u2019s write a sample program to see how that works in ncurses.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"center-some-text\">Center some text<\/h2>\n\n\n\n<p>Let\u2019s begin by writing a demonstration that centers a single character on the screen. When writing programs using ncurses, the <code>initscr<\/code> function will <em>init<\/em>ialize the <em>scr<\/em>een, then set the global variables <code>LINES<\/code> and <code>COLS<\/code> with the screen dimensions. We can use those variables to center a character on the screen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;curses.h&gt;\n\nint main()\n{\n    initscr();\n\n    clear();\n    mvaddch(LINES \/ 2, COLS \/ 2, 'x');\n    refresh();\n\n    getch();\n\n    endwin();\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p>In ncurses, you can position the cursor using the <code>move<\/code> function, then add text to the screen using <code>addstr<\/code> to <em>add<\/em> a <em>str<\/em>ing, or <code>addch<\/code> to <em>add<\/em> a <em>ch<\/em>aracter. But because <em>moving to a new location<\/em> then <em>adding some text<\/em> is a very common thing to do in a program, ncurses provides functions that combine those actions. In this case, I\u2019ve used <code>mvaddch<\/code> to move to a new location then add a single character to the screen.<\/p>\n\n\n\n<p>The <code>refresh<\/code> function updates the screen after I\u2019ve added my text. And the <code>getch<\/code> function waits for the user to press a key on the keyboard.<\/p>\n\n\n\n<p>If we compile and run this sample program, we\u2019ll see a single letter <code>x<\/code> in the middle of the screen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gcc -o center center.c -lncurses<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"827\" height=\"638\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2024\/09\/center1.png\" alt=\"screenshot: centering an x on the screen\" class=\"wp-image-7501\"\/><\/figure>\n\n\n\n<p>My terminal window is 80 columns and 25 lines, so <code>COLS\/2<\/code> is 40 and <code>LINES\/2<\/code> is 12.5, which rounds down to 12 when converted to an integer. The <code>x<\/code> is printed at column 40 and line 12.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"resizing-the-screen\">Resizing the screen<\/h2>\n\n\n\n<p>Let\u2019s update the program to enter a loop while it waits for a keystroke, exiting only when the user presses lowercase <code>q<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;curses.h&gt;\n\nint main()\n{\n    int key;\n\n    initscr();\n\n    clear();\n    mvaddch(LINES \/ 2, COLS \/ 2, 'x');\n    refresh();\n\n    do {\n        key = getch();\n    } while (key != 'q');\n\n    endwin();\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p>This does the same thing as the previous program, except it waits for the user to press <code>q<\/code> before it quits. The <code>x<\/code> remains at column 40 and line 12, even when I resize the terminal window to some other size, like 50 columns wide and 15 lines tall:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gcc -o center2 center2.c -lncurses<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"527\" height=\"408\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2024\/09\/center2.png\" alt=\"screenshot: the x remains at 40,12\" class=\"wp-image-7502\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"recognizing-the-new-size\">Recognizing the new size<\/h2>\n\n\n\n<p>To accommodate this, the <code>getch<\/code> ncurses function returns a specific value (<code>KEY_RESIZE<\/code>) when the terminal has a new size. We can use this to adapt our program to recognize when the terminal has been resized, so we can clear the screen and redraw the <code>x<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;curses.h&gt;\n\nint main()\n{\n    int key;\n\n    initscr();\n\n    clear();\n    mvaddch(LINES \/ 2, COLS \/ 2, 'x');\n    refresh();\n\n    do {\n        key = getch();\n\n        if (key == KEY_RESIZE) {\n            clear();\n            mvaddch(LINES \/ 2, COLS \/ 2, 'x');\n            refresh();\n        }\n    } while (key != 'q');\n\n    endwin();\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p>Now the program will \u201cknow\u201d when the terminal size has changed, so it can recenter the <code>x<\/code> on the screen, such as changing the terminal window to 40 columns and 10 lines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gcc -o center3 center3.c -lncurses<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"427\" height=\"293\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2024\/09\/center3.png\" alt=\"screenshot: the x is always centered\" class=\"wp-image-7503\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"terminal-size-and-ncurses\">Terminal size and ncurses<\/h2>\n\n\n\n<p>Recognizing when the screen has changed size is a powerful feature in the ncurses library. It\u2019s useful for any kind program that draws to the screen, because these programs typically need to position text at specific locations. Add this feature to your next ncurses program so your program can keep track of the terminal size and adjust accordingly.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1 When programs like vi first came along, terminals had a predefined size: usually 80 columns and 24<\/p>\n","protected":false},"author":33,"featured_media":3514,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[5,150],"tags":[91,152],"class_list":["post-7500","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","category-programming","tag-linux","tag-programming"],"modified_by":"Jim Hall","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/7500","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7500"}],"version-history":[{"count":1,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/7500\/revisions"}],"predecessor-version":[{"id":7504,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/7500\/revisions\/7504"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/3514"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7500"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7500"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7500"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}