PDCursesMod
PDCursesMod copied to clipboard
getch() + backspace key doesn't produce KEY_BACKSPACE
PDCurses/PDCursesMod seems to differ from ncurses in regards to how the backspace key is handled.
In ncurses, calling getch() with keypad enabled followed by pressing the backspace key will produce KEY_BACKSPACE which is defined as 263. If keypad is not enabled, the backspace key produces 127 (ASCII code for delete) instead.
In PDCurses/PDCursesMod, callling getch() followed by pressing the backspace key will produce 8 ('\b') (regardless of whether keypad is enabled). This value makes more sense imo because it is the ASCII code for backspace. However, the curses.h distributed with PDCurses/PDCursesMod does not define KEY_BACKSPACE as 8 which means that applications which rely on KEY_BACKSPACE may not function properly when built against pdcurses.
To ensure correct behavior in both ncurses and pdcurses, applications should probably check for both KEY_BACKSPACE as well as '\b'. I'm not sure if there is a way to get getch() to return KEY_BACKSPACE in PDCurses/PDCursesMod, but I couldn't find one.
You are correct. PDCurses and PDCursesMod (henceforth PDCurses*) return 8 in all cases, keypad == TRUE
or keypad == FALSE
. I see that some of my older code, embarrassingly, checks for 127, 8, or 263. At the very least, that last should have been KEY_BACKSPACE
.
This also means that in PDCurses*, you can only distinguish between ^H and backspace by checking PDC_get_key_modifiers() & PDC_KEY_MODIFIER_CONTROL
. It also means (as you've figured out) that there is no way to actually get KEY_BACKSPACE
returned from getch()
.
I rather like the way ncurses handles this. True, backspace is ASCII 8. But so is Ctrl-H, and having a way to distinguish between that and a "real" backspace seems desirable. The downside is that I'll bet you plenty of programs just look for 8 to be returned; we'd be breaking existing behavior.
And I think that'll stop me from trying to "properly" fix the problem. If we were starting from scratch, I think I'd go with the ncurses behavior here. But I'm reluctant to break several decades of software over it.
Would there be any negative consequence to defining KEY_BACKSPACE as 8? Such a change doesn't make it easier to differentiate between backspace and Ctrl-H, but it would mean that applications would only need to check for KEY_BACKSPACE in either ncurses or pdcurses*.
And I think that'll stop me from trying to "properly" fix the problem. If we were starting from scratch, I think I'd go with the ncurses behavior here. But I'm reluctant to break several decades of software over it.
We can all agree on this. What options do we have then?
- forcing applications to adjust (that's always bad)
- redefine of
KEY_BACKSPACE
as suggested by Lyle - would work (may raise a compiler warning in a switch statement when both\b
and that macro is used, I'm not sure) - macro workaround:
- have curses.h define
initscr
toinitscr_like_ncurses
(or similar) if the user definesNCURSES_COMPAT
or similar - in
initscr_like_ncurses
call the realinitscr
and do everything that may be necessary for ncurses compat (could also be definition of color pairs or whatever) including setting a staticint wants_key_backspace_for_keypad
(orbackspace_is_127_or_key_backspace
or whatever) - if this is set make the getch return depending on that
- have curses.h define
The macro approach has guaranteed zero effect on existing uses (but also: no benefits if nothing is done), and would allow people to "just recompile with a define set" for ncurses compatibility (which may means that more things have to be adjusted, though).
may raise a compiler warning in a switch statement when both
\b
and that macro is used, I'm not sure
Yes, it appears that duplicate cases in a switch statement result in an error in gcc, which means that redefining KEY_BACKSPACE would likely have consequences for some programs.
Well, we can't really break lots of existing programs, and duplicate values in switch statements will break gcc and (I suspect) any halfway decent compiler. Which would leave Simon's macro workaround, or something similar.
We might eventually accumulate a few of these "alternative behaviors" and need something like this. But thus far, we've handled this particular issue by checking for 8, 127, or KEY_BACKSPACE
, and I don't think it's caused much trouble. In my case, when I first hit Backspace in a curses program and got an odd result, I just thought "oh, I'm getting a different result for backspace on this platform; let's check for that value, too."
Had the macro solution been available, I might have noticed it and #defined the relevant symbol. Probably not, though, given that checking 8, 127, and KEY_BACKSPACE
is easy... it's unlikely that I'd have even realized that the macro was an option.
I'm open to counter-argument, but I'd currently say : leave this as is.
In the context of handling backspace during string input, it is trivial to check for 8, 127, and KEY_BACKSPACE. In that context, it's fine if Ctrl-H is treated as backspace. However, I do think there is some precedence for implementing optional ncurses compatibility features. Although I have not used it yet, PDC_NCMOUSE appears to be an example of such a feature. Following that naming convention, we could add PDC_NCBACKSPACE to toggle backspace handling behavior.
The area where I could see this being more annoying is programs which allow users to remap the controls. Either the inability to distinguish between Ctrl-H and the backspace key has to be accepted as a platform-specific limitation or the application needs to handle pdcurses as a special case. I suppose the latter might look something like this:
int wgetch_wrapper(WINDOW *window)
{
int input = wgetch(window);
#ifdef PDCURSES
if(input == '\b' && !(PDC_get_key_modifiers() & PDC_KEY_MODIFIER_CONTROL))
{
input = is_keypad(window) ? KEY_BACKSPACE : 127;
}
#endif
return input;
}
Personally, I'd rather just compile with -DPDC_NCBACKSPACE than have to put the above function in my project. However, this is not a hill I'm prepared to die on either.
Sounds like a good proposal for me. Instead of redefining initscr this likely would redefine getch/wgetch to one with an additional parameter containing the additional flag wants_keybackspace.
I'd be reluctant to change the ABI for just this. But this isn't the first time I've wanted to be able to tweak some obscure internal setting, or set or retrieve some internal value for debugging purposes (see, for example, the PDC_check_color_pair_table()
function at the end of pdcurses/color.c
.) I recently wanted to know if the window had the input focus or not.
So thinking more broadly, I could imagine...
#ifdef PDC_EXT_NCBACKSPACE
PDC_extension( PDC_EXT_NCBACKSPACE);
#endif
where the above function takes variable arguments:
int PDC_extension( const int extension_idx, ...);
No added arguments are used in the above case. But if we added some code to, say, change the rate at which text blinks from the default 500 milliseconds, then one might call PDC_extension( PDC_EXT_BLINKRATE, 300);
to get slightly faster blinking. The color pair debugging function would be accessed with the index PDC_EXT_COLOR_PAIR_CHECK
followed by a pointer to an array of five integers. And so on.
Any such use should be conditional on the relevant index being #defined, of course.
Just noticed that both the MATE and XFCE terminals provide, under 'preferences', options to decide what gets emitted when you hit Backspace. On xfce4-terminal, you can have it emit Ctrl-H, Del (ASCII 127), or a VT escape sequence (escape, [, 3, ~, but that may vary). Which means there's no way (that I can see) to avoid checking for ^H, 127, and KEY_BACKSPACE
; depending on terminal settings, you really might get any of these. So I'll close this as "not planned".
Which means there's no way (that I can see) to avoid checking for ^H, 127, and
KEY_BACKSPACE
; depending on terminal settings, you really might get any of these.
... and the point of the issue is: can PDCurses please check for these and always return KEY_BACKSPACE
(= no matter what the terminal setting is)?
So I'll close this as "not planned".
I'm all for closing issues that just lay around, but to just recheck: Is there a reason to not do the translation as requested in PDCurses?
Note: For a bunch of reasons we do have a translation routine in GnuCOBOL that is executed after getch ()
which includes this translation, but the main reason for this is that we also target multiple curses versions (including real old *nix implementations), I can see the reason to only target PDCurses and hope to just check one defined key.
Not all terminal apps have easy key configuration. Properly detecting keypress instead of terminal characters is important. I support any modification in PDCursesMod. It would be an extension, like the extended color options of PDCursesMod compared to PDCurses. With the current state it is impossible to consider CTRL+H for search-and-replace function and backspace for delete character at the same time, because for PDCursesMod it is the same keycode.
Just investigated this again.
I had pointed out above that in some cases, we can't tell the difference between Backspace and Ctrl-H. With some terminal settings, Backspace returns the same key code as Delete, and again, we can't tell the difference between them.
However... it looks to be a rare situation, limited to certain Linux terminals that allow you to set what key code is returned when Backspace is hit, which means it only affects the VT port on a few terminals with odd settings. With every other platform, we can reliably distinguish Backspace from Ctrl-H and anything else. And ~99% of the time, we can do so on VT as well.
Which meant it'd be possible to have an extension function, as described above, where you could specify what PDCursesMod returns when Backspace is hit. Shouldn't be difficult to implement. The real time issue, as usual, is the fact that we have a lot of platforms to test it on.
It is great news you are considering this again. Maybe finally we could extend the combination of keys to actual keys. For instance, the old terminals have ^M as the enter key, ^H for backspace, and similar combinations for other common combinations, not to mention the old use of the ESC key to generate escape codes.
These days it is really easy to differentiate the actual key. I understand that this removes the possibility of using nano.exe through a ssh or similar when using keyboard keycodes. In any case, the wingui version will never be working in a terminal client-server framework. So, in most cases, the windows versions will be used locally, with access to the actual keycodes, not old obscure unix codes.
Thanks for reconsidering.
An interesting thought. I gotta admit, though, that I'm now leaning toward not adding another extension for this purpose. Consider :
We'd like to distinguish Ctrl-I from Tab, Ctrl-M from Enter, and Ctrl-[ from Escape. None of those can be distinguished in ncurses, VT, or framebuffer/DRM, and I don't see how they ever could be.
On all other platforms, these three pairs can already be distinguished, in both PDCurses and PDCursesMod, by checking PDC_get_key_modifiers()
to see if the PDC_KEY_MODIFIER_CONTROL
bit is set. The same can be done for Backspace on VT and framebuffer/DRM 99% of the time. For example,
int c = getch( );
if( !PDC_get_key_modifiers( ))
{
if( 8 == c || 127 == c || KEY_BACKSPACE == c)
/* backspace was hit, not Ctrl-H */
if( 13 == c || 10 == c || KEY_ENTER == c)
/* enter was hit, not Ctrl-M or Ctrl-J */
if( 9 == c)
/* tab was hit, not Ctrl-I */
if( 27 == c)
/* Escape was hit, not Ctrl-[ */
}
I would advise this as the way to handle the problem. It will work for PDCurses and PDCursesMod, rather than locking you into a PDCursesMod-only solution. (And further requiring a current version of PDCursesMod.) You do have to modify your code, but you'd have to do that anyway.
Or, to put it another way : what advantage would an extension to cause Backspace to return KEY_BACKSPACE
have over use of the existing PDC_get_key_modifiers()
function? I can't see one. And one could always write, say,
int my_getch( void)
{
int rval = getch( );
if( 8 == rval && !PDC_get_key_modifiers( ))
rval = KEY_BACKSPACE;
return( rval);
}