Package: libncursesw6 Version: 6.4+20231121-1 Severity: normal Dear Maintainer,
I am attaching a repro.c program that when built with cc -O3 -g -Wall -Wextra -DNCURSES_WIDECHAR -D_GNU_SOURCE repro.c -lncursesw -ltinfo -o repro presents a UI in the form of first line -> L1 L2 L3 L4 L5 last line Mousing is enabled, and if you click on a line the cursor on the left moves to select it. The first line and last line are on stdscr, the rest of the screen is urlswin = newpad(LINES, COLS), rendered via pnoutrefresh(urlswin, /**/ url[first_on_page].cursor_y + fudge, 0, /**/ 1 /*title line*/, 0, LINES - 2, COLS); On KEY_MOUSE and BUTTON1_CLICKED: getmouse(&ev); if(!wmouse_trafo(urlswin, &ev.y, &ev.x, false)) break; and the line targeted is found and selected. Clicking on first line correctly hits the break. Clicking on last line incorrectly continues on. This shows as scrolling to the next screen (since the line selected is L6, which would be under last line). This /only happens/ if the lines go all the way to the line above last. If you click on any line below a non-full screen (s/1000/10/ or scroll down), nothing happens. Is this a weird thing with the edge of the screen? Or with how pnoutrefresh accounts for the undrawn parts of the pad? Best, наб -- System Information: Debian Release: 12.4 APT prefers stable-updates APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable-debug'), (500, 'stable') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 6.1.0-9-amd64 (SMP w/24 CPU threads; PREEMPT) Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_FIRMWARE_WORKAROUND, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), LANGUAGE=en_GB:en Shell: /bin/sh linked to /usr/bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages libncursesw6 depends on: ii libc6 2.36-9+deb12u3 ii libtinfo6 6.4+20231121-1 Versions of packages libncursesw6 recommends: ii libgpm2 1.20.7-10+b1 libncursesw6 suggests no packages. -- no debconf information
#include <assert.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <locale.h> #include <ncurses.h> #include <regex.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> struct url { char * url; size_t urllen; int cursor_y, cursor_x; int last_y; }; int main() { setlocale(LC_ALL, ""); size_t urlcount = 1000; struct url * url = reallocarray(NULL, urlcount, sizeof(*url)); for(size_t i = 0; i < urlcount; ++i) url[i].urllen = asprintf(&url[i].url, "L%zu", i); size_t current = 0, oldcurrent = 0; initscr(); cbreak(); noecho(); curs_set(1); keypad(stdscr, TRUE); mousemask(BUTTON1_CLICKED | BUTTON5_PRESSED | BUTTON4_PRESSED, NULL); mouseinterval(5); // title line #define urlswin_logical_height (LINES - 1 - 1) // status line WINDOW * urlswin = newpad(LINES, COLS); if(!urlswin) return endwin(), 1; int page_of_current = 0; size_t first_on_page = 0; int fudge = 0; for(bool done = false; !done;) { { clear(); mvwaddstr(stdscr, 0, 0, "first line"); mvwaddstr(stdscr, LINES - 1, 0, "last line"); wnoutrefresh(stdscr); wclear(urlswin); wmove(urlswin, 0, 0); for(size_t i = 0; i < urlcount; ++i) { #define EXPAND() \ { \ int sizey, sizex; \ getmaxyx(urlswin, sizey, sizex); \ if(wresize(urlswin, sizey * 2, sizex) == ERR) \ break; \ } #define EXPANDONSCROLL(...) \ if(__VA_ARGS__ != OK) { \ wmove(urlswin, starty, startx); \ EXPAND(); \ goto again; \ } int starty, startx; getyx(urlswin, starty, startx); again: EXPANDONSCROLL(waddstr(urlswin, " ")); getyx(urlswin, url[i].cursor_y, url[i].cursor_x); --url[i].cursor_x; EXPANDONSCROLL(wprintw(urlswin, "%4zu", i + 1)); EXPANDONSCROLL(waddch(urlswin, ' ')); EXPANDONSCROLL(waddnstr(urlswin, url[i].url, url[i].urllen)); url[i].last_y = getcury(urlswin); if(i != urlcount - 1) if(waddch(urlswin, '\n') != OK) { EXPAND(); waddch(urlswin, '\n'); } } mvwaddstr(urlswin, url[oldcurrent].cursor_y, url[oldcurrent].cursor_x - 2, " "); wstandout(urlswin); mvwaddstr(urlswin, url[current].cursor_y, url[current].cursor_x - 2, "->"); wstandend(urlswin); page_of_current = url[current].cursor_y / urlswin_logical_height; first_on_page = 0; while(url[first_on_page].cursor_y / urlswin_logical_height != page_of_current) ++first_on_page; fudge = 0; // try to get the last-URL-on-the-page to be included when it's selected if(url[current].last_y - url[first_on_page].cursor_y > urlswin_logical_height) // unclear why +1 needed here; this means we overscroll (sometimes) // compare the first screen on a rows 54; cols 172; teletype of // ./urlview text.uv <(for i in A B C D E F H I J K L M; do tr Q $i < text.uv; done) // and // ./urlview text.uv <(for i in A B C D E F H I J K L M; do tr Q $i < text.uv; done | fold -w 300) // the last link on the first screen appear to have a completely identical last_y, but the former is taller by a line? fudge = (url[current].last_y - url[first_on_page].cursor_y) - urlswin_logical_height + 1; pnoutrefresh(urlswin, /**/ url[first_on_page].cursor_y + fudge, 0, /**/ 1 /*title line*/, 0, urlswin_logical_height, COLS); doupdate(); oldcurrent = current; // same projection as pnoutrefresh() above move(url[current].cursor_y - (url[first_on_page].cursor_y + fudge) + 1 /*title line*/, url[current].cursor_x - 0 + 0); } errno = 0; switch(getch()) { case KEY_MOUSE: { MEVENT ev; getmouse(&ev); if(ev.bstate & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED)) { if(!wmouse_trafo(urlswin, &ev.y, &ev.x, false)) break; ev.y += url[first_on_page].cursor_y + fudge; for(size_t hit = first_on_page; hit != urlcount; ++hit) if(ev.y >= url[hit].cursor_y && ev.y <= url[hit].last_y) { current = hit; break; } } } break; case ERR: if(errno == EINTR) continue; else if(errno) err(1, "getch()"); __attribute__((fallthrough)); case 'q': done = true; break; } } endwin(); }
signature.asc
Description: PGP signature