ncurses "get_wch" function behavior - c

I try to understand how the get_wch function from ncurses works.
Here is a piece of code that I use to test it under TTY, terminator, and konsole:
#include <stdlib.h>
#include <curses.h>
#include <locale.h>
int main(void)
{
// initialize curses
initscr();
setlocale(LC_ALL, ""); // Just a check to see if something change with it
wint_t char_code;
int key_code = get_wch(&char_code);
char truc [20];
sprintf(truc, "%d / %d", key_code, char_code);
refresh();
getch();
endwin();
printf("%d\n", KEY_CODE_YES);
printf(truc);
printf("\n");
return 0;
}
When I hit a "classic" key like 'a' or '?', I get a char_code (UTF-8 code, I supposed). But when I hit a function key like F1 or F12, I get a char_code 27, and a key_code 0, except for F11 (key_code: KEY_CODE_YES, char_code: 410).
The documentation say that:
When get_wch, wget_wch, mvget_wch, and mvwget_wch functions successfully report the pressing of a function key, they return KEY_CODE_YES. When they successfully report a wide character, they return OK.
F1 to F12 are the so called "function keys" ? If I'm right, could you explain me why the function return 0 as a key_code when I hit a Fx key ?

You'll get KEY_CODE_YES only with keypad set to true, see keypad man page about that :
The keypad option enables the keypad of the user's terminal. If enabled (bf is TRUE),
the user can press a function key (such as an arrow key) and wgetch returns a single value
representing the function key, as in KEY_LEFT. If disabled (bf is FALSE), curses does not
treat function keys specially and the program has to interpret the escape sequences
itself. If the keypad in the terminal can be turned on (made to transmit) and off (made to
work locally), turning on this option causes the terminal keypad to be turned on when
wgetch is called. The default value for keypad is false.
And you're not getting keycode 0 but the success status OK from get_wch, see related manpage.
Without it, you'll have to get multiple char_code to get the key code like :
wint_t char_code;
wint_t char_code2 = 0, char_code3 = 0;
int key_code = get_wch(&char_code);
if (key_code == OK) key_code = get_wch(&char_code2);
if (key_code == OK) key_code = get_wch(&char_code3);
For function keys, you'll the escape code in char_code (0x27), the function code in char_code2 (0x4b) and the specific function key_code in char_code3.
EDIT As noticed by #dratenik, with xterm/VT220+ terminals, the F5-F12 function keys output four char_code, so you'll need to query another char_code when you get [ (0x5b) in char_code2 and 1 (0x31) in char_code3 to get the specific function keys key_code for theses function keys. You'll find the possible key codes in this useful link provided by #dratenik.

Related

XServer first key typed is always in lowercase

I'm writing a method for keyboard handling where I'm passing the key unicodes and firing keystrokes using the XTestFakeKeyEvent Method of X11. Now my issue is that after the opening the display, if the very first Character I send is in Uppercase, it still is typed in lowercase. So provided is the minimal implementation.
Compiled using command: gcc typekeys.c -o typekeys.exe -lX11 -lXtst
#include <stdio.h>
#include <stdbool.h>
#include <X11/extensions/XTest.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
static Display *mainDisplay = NULL;
Display *XGetMainDisplayK(void) {
if (mainDisplay == NULL) {
mainDisplay = XOpenDisplay(NULL);
if (mainDisplay == NULL) {
printf("\nCould not open main display");
}
}
return mainDisplay;
}
void toggleKeySym(unsigned int key){
KeySym sym;
Display *dpy;
dpy = XGetMainDisplayK();
sym=key;
printf("\nKeysym recieved:%u \n",key);
int min, max, numcodes;
XDisplayKeycodes(dpy, &min, &max);
KeySym *keysym;
keysym = XGetKeyboardMapping(dpy, min, max-min+1, &numcodes);
keysym[(max-min-1)*numcodes]=sym;
XChangeKeyboardMapping(dpy, min, numcodes, keysym, (max-min));
XFree(keysym);
XFlush(dpy);
unsigned int code;
printf("Keysym value:%d \n",sym);
code=XKeysymToKeycode(dpy, sym);
printf("Code Generated:%u. \n",code);
XTestFakeKeyEvent(dpy, code, True, CurrentTime);
XTestFakeKeyEvent(dpy, code, False, CurrentTime);
XSync(dpy, false);
XFlush(dpy);
}
void main(){
printf("Start \n");
toggleKeySym(65);
toggleKeySym(65);
printf("Done.\n");
}
The problem is XKeysymToKeycode(dpy, sym);:
If you look at the output of your program, you will see that it outputs something like this:
Code Generated:38.
...
Code Generated:254.
At least on my computer it behaves this way.
(By the way: I can only reproduce the problem the first time after logging off from my computer and logging on again.)
However, your program should write the same value twice because the same key code (254) is pressed.
This means that XKeysymToKeycode(dpy, sym); has returned the wrong value.
The reason is that XKeysymToKeycode() works the following way:
The XOpenDisplay() function calls XGetKeyboardMapping() reading the keyboard information to some "internal" variable (of the type KeySym *); the information in the "internal" variable is then used by XKeysymToKeycode().
The "internal" variable is updated later - maybe during XSync(). However, it seems that the "internal" variable is not updated before XKeysymToKeycode() is called the first time, so XKeysymToKeycode() returns the information still based on the old value of the "internal" variable.
However, you know that you have changed the symbol generated by the key max-1, so you know that code should have the value max-1. Therefore, calling XKeysymToKeycode() is not necessary at all.

Compiler not Recognizing Control Keys like Return key in C

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
char b;
// the loop is executed until the "Return key" or "Enter" is pressed by the user
while(b!='\n')
{
b=getch();
putch('*');
}
return 0;
}
The program is something like typing password.As you press alphabets the output displays successive "*"s.
Until you press Return key and the program gets finished.
My problem is that my compiler(Codeblocks 13.12-->GNU GCC) doesn't understand the control keys like Return.Even when I replace '\n' with the ASCII code 10(the concerning ASCII code for Return key) the program makes a mistake again.The mistake is to continue displaying "*"s even when Return key is pressed!
What's going on with the compiler and how it 'll be fixed?
Try using
/* 13 refers to '\r' (carriage return) */
while(b != 13) {
//Code
}

custom readline functions in bash commandline?

Code from GNU readline and key bindings :
#include <stdio.h>
#include <readline/readline.h>
int my_cool_readline_func (int count, int key) {
printf ("key pressed: %d\n", key);
rl_on_new_line ();
return 0;
}
int main(void) {
rl_command_func_t my_cool_readline_func;
rl_bind_key ('\t', my_cool_readline_func);
rl_bind_key (27, my_cool_readline_func); /* ascii code for ESC */
rl_bind_keyseq ("\\C-a", my_cool_readline_func);
while (1) {
char *line = readline ("rl> ");
}
}
Is it possible to use this kind of custom readline functions in bash commandline?
For example, I have tried these (a custom function can be much more complex, of course):
bind '"\C-t" beginning-of-line shell-kill-word'
bind '"\C-t" beginning-of-line, shell-kill-word'
bind '"\C-t" beginning-of-line; shell-kill-word'
None of them worked.
I think this binding is the key to understanding how this is done:
"\C-x\"": "\"\"\C-b"
Read "When I press Ctrl+X and then ", insert two quotes (\"\") and move the cursor once to the left (\C-b) so it ends up between the two double quotes.
On the right hand side of the :, you can only have a single readline function or a "macro" or a single shell function. A macro is a sequence of keystrokes.
So what you need to do is assign the functions you want to keys and then use the keys in the macro:
bind '"\C-t":"\C-a\ed"'
See also: Complex keybinding in bash

Ncurses reading numpad keys and escaping

I am trying to use ESC to escape from a program using getch(). I created a small program to demonstrate my problem.
#include <ncurses.h>
int main(void) {
int key = 0;
initscr();
noecho();
keypad(stdscr, TRUE);
do {
key = getch();
clear();
mvprintw(0, 0, "Key = %d\n", key);
refresh();
} while (key != 27);
clear();
refresh();
endwin();
return 0;
}
I am trying to allow a user to use either the arrow keys or keypad (whichever is more convenient)
the issue lies within the keypad (whether numlock is on or not). When I compile and run the program and try and use the numpad keys in this simple test it exits as soon as I touch a numpad key. If I remove the while (key != 27) (esc being 27) condition it reads the keys and displays their numbers. Why does it exit the loop when the numpad keys register as
ENTER 343
UP 120
DOWN 114
LEFT 116
RIGHT 118
Any help is much appreciated!
I found a fix in the source for Dungeon Crawl Stone Soup. It basically sets the keycodes for those.
{DCSS-dir}/source/libunix.cc (333)
define_key("\033Op", 1000);
define_key("\033Oq", 1001);
define_key("\033Or", 1002);
define_key("\033Os", 1003);
define_key("\033Ot", 1004);
define_key("\033Ou", 1005);
define_key("\033Ov", 1006);
define_key("\033Ow", 1007);
define_key("\033Ox", 1008);
define_key("\033Oy", 1009);
// non-arrow keypad keys (for macros)
define_key("\033OM", 1010); // Enter
define_key("\033OP", 1011); // NumLock
define_key("\033OQ", 1012); // /
define_key("\033OR", 1013); // *
define_key("\033OS", 1014); // -
define_key("\033Oj", 1015); // *
define_key("\033Ok", 1016); // +
define_key("\033Ol", 1017); // +
define_key("\033Om", 1018); // .
define_key("\033On", 1019); // .
define_key("\033Oo", 1020); // -
// variants. Ugly curses won't allow us to return the same code...
define_key("\033[1~", 1031); // Home
define_key("\033[4~", 1034); // End
define_key("\033[E", 1040); // center arrow
The XTERM terminal emulator sends an escape for certain numpad keys if Num Lock is off.
You can turn on Num Lock, use something other than the numpad, use something other than ESC to break your loop, or try to find a terminal emulator that doesn't do this. There is no way for your program to distinguish between ESC and certain numpad characters when Num Lock is off within the confines of your terminal emulator.

How can I get the keyboard state in Linux?

I want to check if the user pressed down the Shift key when the program starts. (That means, press down the Shift key before the program is started) It's a simple console program, nothing related to X.
This maybe similar to the Win32 GetKeyboardState() function.
I want to know whether I can do this and how, but not any pros and cons with accessing the terminal directly.
I think there would be a way to do this. The thing is that you would have to read directly from the keyboard device. You would not be getting input from the terminal. I have the same problem. I have a program that runs (in the background) and I want to know if the user is holding down the shift key.
I believe this is possible and a place to start might be /dev/input/by-path/*-kbd.
This file does give input every time a key is pressed or reptadly if it is held down so it might be worth a look. (Try cat /dev/input/by-path/*-kbd)
If you do figure this out I would love to hear how you did it.
EDIT: I have found the solution
I have figured out how do do this. My program is as follows:
#include <stdlib.h>
#include <stdio.h>
#include <linux/input.h>
void usage ( int argc, char *argv[] )
{
printf("Usage:\n\t%s key\n\nvalid keys are:\n\tlshift\t- Left Shift key\n" , argv[0]);
exit(EXIT_FAILURE);
}
int main ( int argc, char *argv[], char *env[] )
{
if ( argc != 2 ) usage(argc, argv);
int key;
if ( strcmp(argv[1], "lshift") == 0 ) key = KEY_LEFTSHIFT;
else if ( strcmp(argv[1], "rshift") == 0 ) key = KEY_RIGHTSHIFT;
else if ( strcmp(argv[1], "lalt") == 0 ) key = KEY_LEFTALT;
else if ( strcmp(argv[1], "ralt") == 0 ) key = KEY_RIGHTALT;
else if ( strcmp(argv[1], "lctrl") == 0 ) key = KEY_LEFTCTRL;
else if ( strcmp(argv[1], "rctrl") == 0 ) key = KEY_RIGHTCTRL;
FILE *kbd = fopen("/dev/input/by-path/platform-i8042-serio-0-event-kbd", "r");
char key_map[KEY_MAX/8 + 1]; // Create a byte array the size of the number of keys
memset(key_map, 0, sizeof(key_map)); // Initate the array to zero's
ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map); // Fill the keymap with the current keyboard state
int keyb = key_map[key/8]; // The key we want (and the seven others arround it)
int mask = 1 << (key % 8); // Put a one in the same column as out key state will be in;
return !(keyb & mask); // Returns true if pressed otherwise false
}
The info message is lacking (I'm too lazy). But essentially the first argument is compared to a list of keys and the appropriate key identifier is used. It returns true if the key is pressed and false if not.
Please Note
You will need to change the name of they keyboard device. I do not know of a way to find the default keyboard device. (if you know I would love to hear ;) )
This works beautifully: I use it to start the autostart of Xorg if I hold down the shift key.
AFAIK this cannot be done without Xlib (aka. X) with no root level permissions.
Using XQueryKeymap() will do what you want. however you pointed out that X cannot be used. Regardless, opening display connection will also be required.
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <stdbool.h>
#include <stdio.h>
int main()
{
Display* dpy = XOpenDisplay(NULL);
char keys_return[32];
XQueryKeymap( dpy, keys_return );
KeyCode kc2 = XKeysymToKeycode( dpy, XK_Shift_L );
bool bShiftPressed = !!( keys_return[ kc2>>3 ] & ( 1<<(kc2&7) ) );
printf("Shift is %spressed\n", bShiftPressed ? "" : "not ");
XCloseDisplay(dpy);
}
I have found a very simple way through gtk/gdk.
int main ( int argc, char *argv[], char *env[] )
{
gtk_init(&argc, &argv);
GdkModifierType button_state;
gdk_window_get_pointer(NULL, NULL, NULL, &button_state);
if(button_state & GDK_CONTROL_MASK) {
printf("ctrl key is pressed");
}
}
You can't.
The Shift key isn't considered as a character key, so even if you access the terminal directly, you won't be able to detect this key.
Maybe you shouldn't have to. Imagine for example that you are using a US keyboard where numbers are accessible on the top row without modifiers, and also checking for the Shift key. People with other keyboard layout may have to use Shift modifiers to access the numbers. If your program react to this Shift press, then your program is basically unusable. The same thing applies for other modifier keys : you may detect some of them only after a normal character key is pressed. Or worse, they may need to use the Shift key to use 'enter' to run your program.
Also, what Shift key do you want to monitor? the one on the local machine, or the one where the user is? remember that SSH exists and is commonly used to access a pseudoterminal remotely.
If you are root and want to monitor the Shift key on the local machine, you can read the evdev devices for events about the Shift key. But this is only possible because of automatic key repeating, so you won't detect a Shift key that is pressed right before running your program, but only a few second before.
Of course you can't do that on the remote machine, that would be a security flaw.
And anyway, why would you want to do that? Wouldn't an X application be the right thing to do in your case?

Resources