Xkb: How to convert a keycode to keysym - linux

I am simply trying to take a KeyCode and a modifier mask and convert it to a KeySym using the Xkb extension. I cant seem to figure out why this doesn't work. Its obvious that the modifiers dont match but I dont know why. I don't even know if I am converting the group correctly.
#include <stdio.h>
#include <stdlib.h>
#include <X11/X.h>
#include <X11/XKBlib.h>
void check(XkbDescPtr keyboard_map, KeyCode keycode, unsigned int mask) {
//What the hell is diff between XkbKeyGroupInfo and XkbKeyNumGroups?
unsigned char info = XkbKeyGroupInfo(keyboard_map, keycode);
int num_groups = XkbKeyNumGroups(keyboard_map, keycode);
int key_width = XkbKeyGroupsWidth(keyboard_map, keycode);
//int num_syms = XkbKeyNumSyms(keyboard_map, keycode);
//Get the group
unsigned int group = 0; // What should this default to?
switch (XkbOutOfRangeGroupAction(info)) {
case XkbRedirectIntoRange:
/* If the RedirectIntoRange flag is set, the four least significant
* bits of the groups wrap control specify the index of a group to
* which all illegal groups correspond. If the specified group is
* also out of range, all illegal groups map to Group1.
*/
printf("XkbRedirectIntoRange\n");
group = XkbOutOfRangeGroupInfo(info);
if (group >= num_groups) {
group = 0;
}
break;
case XkbClampIntoRange:
/* If the ClampIntoRange flag is set, out-of-range groups correspond
* to the nearest legal group. Effective groups larger than the
* highest supported group are mapped to the highest supported group;
* effective groups less than Group1 are mapped to Group1 . For
* example, a key with two groups of symbols uses Group2 type and
* symbols if the global effective group is either Group3 or Group4.
*/
printf("XkbClampIntoRange\n");
group = num_groups - 1;
break;
case XkbWrapIntoRange:
/* If neither flag is set, group is wrapped into range using integer
* modulus. For example, a key with two groups of symbols for which
* groups wrap uses Group1 symbols if the global effective group is
* Group3 or Group2 symbols if the global effective group is Group4.
*/
printf("XkbWrapIntoRange\n");
default:
printf("Default\n");
if (num_groups != 0) {
group %= num_groups;
}
break;
}
printf("Group Info %d, %d, %d\n", group, num_groups, key_width);
//printf("Mask Info %d, %d, %d, %d, %d, %d, %d, %d\n", ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask);
XkbKeyTypePtr key_type = XkbKeyKeyType(keyboard_map, keycode, group);
KeySym keysym = NoSymbol;
int i;
for (i = 0; i < key_type->map_count; i++) {
if (key_type->map[i].active && key_type->map[i].mods.mask == mask) {
keysym = XkbKeySymEntry(keyboard_map, keycode, i, group);
}
}
//printf("%s\n", XKeysymToString(keysym));
printf("KeyCode: %d\n", (int) keycode);
printf("KeySym: %d\n", (int) keysym);
}
int main(int argc, const char * argv[]) {
Display * display;
//Try to attach to the default X11 display.
display = XOpenDisplay(NULL);
if(display == NULL) {
printf("Error: Could not open display!\n");
return EXIT_FAILURE;
}
//Get the map
XkbDescPtr keyboard_map = XkbGetMap(display, XkbAllClientInfoMask, XkbUseCoreKbd);
KeyCode keycode = 56; // b
check(keyboard_map, keycode, ShiftMask | LockMask | ControlMask);
//Close the connection to the selected X11 display.
XCloseDisplay(display);
return EXIT_SUCCESS;
}

I was finally able to figure it out after a lot of trial and error. XKeycodeToKeysym is apparently broken and the index value calculations are not defined for extended indexes.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/X.h>
#include <X11/XKBlib.h>
KeySym KeyCodeToKeySym(Display * display, KeyCode keycode, unsigned int event_mask) {
KeySym keysym = NoSymbol;
//Get the map
XkbDescPtr keyboard_map = XkbGetMap(display, XkbAllClientInfoMask, XkbUseCoreKbd);
if (keyboard_map) {
//What is diff between XkbKeyGroupInfo and XkbKeyNumGroups?
unsigned char info = XkbKeyGroupInfo(keyboard_map, keycode);
unsigned int num_groups = XkbKeyNumGroups(keyboard_map, keycode);
//Get the group
unsigned int group = 0x00;
switch (XkbOutOfRangeGroupAction(info)) {
case XkbRedirectIntoRange:
/* If the RedirectIntoRange flag is set, the four least significant
* bits of the groups wrap control specify the index of a group to
* which all illegal groups correspond. If the specified group is
* also out of range, all illegal groups map to Group1.
*/
group = XkbOutOfRangeGroupInfo(info);
if (group >= num_groups) {
group = 0;
}
break;
case XkbClampIntoRange:
/* If the ClampIntoRange flag is set, out-of-range groups correspond
* to the nearest legal group. Effective groups larger than the
* highest supported group are mapped to the highest supported group;
* effective groups less than Group1 are mapped to Group1 . For
* example, a key with two groups of symbols uses Group2 type and
* symbols if the global effective group is either Group3 or Group4.
*/
group = num_groups - 1;
break;
case XkbWrapIntoRange:
/* If neither flag is set, group is wrapped into range using integer
* modulus. For example, a key with two groups of symbols for which
* groups wrap uses Group1 symbols if the global effective group is
* Group3 or Group2 symbols if the global effective group is Group4.
*/
default:
if (num_groups != 0) {
group %= num_groups;
}
break;
}
XkbKeyTypePtr key_type = XkbKeyKeyType(keyboard_map, keycode, group);
unsigned int active_mods = event_mask & key_type->mods.mask;
int i, level = 0;
for (i = 0; i < key_type->map_count; i++) {
if (key_type->map[i].active && key_type->map[i].mods.mask == active_mods) {
level = key_type->map[i].level;
}
}
keysym = XkbKeySymEntry(keyboard_map, keycode, level, group);
XkbFreeClientMap(keyboard_map, XkbAllClientInfoMask, true);
}
return keysym;
}
int main(int argc, const char * argv[]) {
Display * display;
//Try to attach to the default X11 display.
display = XOpenDisplay(NULL);
if(display == NULL) {
printf("Error: Could not open display!\n");
return EXIT_FAILURE;
}
KeyCode keycode = 56; // b
unsigned int event_mask = ShiftMask | LockMask;
KeySym keysym = KeyCodeToKeySym(display, keycode, event_mask);
printf("KeySym: %s\n", XKeysymToString(keysym));
//Close the connection to the selected X11 display.
XCloseDisplay(display);
return EXIT_SUCCESS;
}

From the man page of XKeycodeToKeysym:
The XKeycodeToKeysym function uses internal Xlib tables and returns the
KeySym defined for the specified KeyCode and the element of the KeyCode
vector. If no symbol is defined, XKeycodeToKeysym returns NoSymbol.
XKeycodeToKeysym predates the XKB extension. If you want to lookup a
KeySym while using XKB you have to use XkbKeycodeToKeysym.
I don't see that in your sample code.
Read the full manpage for more info.

Related

MPI4PY: ring communication with neighbor_alltoallw

Please Help!
I am using MPI (= Message Passing Interface) in python for a ring communication, which means that every rank are sending and receiving from each other. I know one way to realize this is by using for instance MPI.COMM_WORLD.issend()and MPI.COMM_WORLD.recv(), this is working and done.
Now I want to realize the same Output on a different way by using MPI.Topocomm.Neighbor_alltoallw but this is not working. I wrote a C Code and is working there, so the same output can be reached with this function, but when I implement this in python it is not working. Please find below the C Code and the Python Code
The definition of the Function says (mpi4py Package for Python):
Neighbor_alltoallw(...)
Topocomm.Neighbor_alltoallw(self, sendbuf, recvbuf)
Neighbor All-to-All Generalized
I do not understand following things:
why is recbuf not a return value? it seems to be an argument here
how can this be implmented for a ring communication in Python?
Thank you for your time and support!
my working C Code:
#include <stdio.h>
#include <mpi.h>
#define to_right 201
#define max_dims 1
int main (int argc, char *argv[])
{
int my_rank, size;
int snd_buf, rcv_buf;
int right, left;
int sum, i;
MPI_Comm new_comm;
int dims[max_dims],
periods[max_dims],
reorder;
MPI_Aint snd_displs[2], rcv_displs[2];
int snd_counts[2], rcv_counts[2];
MPI_Datatype snd_types[2], rcv_types[2];
MPI_Status status;
MPI_Request request;
MPI_Init(&argc, &argv);
/* Get process info. */
MPI_Comm_size(MPI_COMM_WORLD, &size);
/* Set cartesian topology. */
dims[0] = size;
periods[0] = 1;
reorder = 1;
MPI_Cart_create(MPI_COMM_WORLD, max_dims, dims, periods,
reorder,&new_comm);
/* Get coords */
MPI_Comm_rank(new_comm, &my_rank);
/* MPI_Cart_coords(new_comm, my_rank, max_dims, my_coords); */
/* Get nearest neighbour rank. */
MPI_Cart_shift(new_comm, 0, 1, &left, &right);
/* Compute global sum. */
sum = 0;
snd_buf = my_rank;
rcv_buf = -1000; /* unused value, should be overwritten by first MPI_Recv; only for test purpose */
rcv_counts[0] = 1; MPI_Get_address(&rcv_buf, &rcv_displs[0]); snd_types[0] = MPI_INT;
rcv_counts[1] = 0; rcv_displs[1] = 0 /*unused*/; snd_types[1] = MPI_INT;
snd_counts[0] = 0; snd_displs[0] = 0 /*unused*/; rcv_types[0] = MPI_INT;
snd_counts[1] = 1; MPI_Get_address(&snd_buf, &snd_displs[1]); rcv_types[1] = MPI_INT;
for( i = 0; i < size; i++)
{
/* Substituted by MPI_Neighbor_alltoallw() :
MPI_Issend(&snd_buf, 1, MPI_INT, right, to_right,
new_comm, &request);
MPI_Recv(&rcv_buf, 1, MPI_INT, left, to_right,
new_comm, &status);
MPI_Wait(&request, &status);
*/
MPI_Neighbor_alltoallw(MPI_BOTTOM, snd_counts, snd_displs, snd_types,
MPI_BOTTOM, rcv_counts, rcv_displs, rcv_types, new_comm);
snd_buf = rcv_buf;
sum += rcv_buf;
}
printf ("PE%i:\tSum = %i\n", my_rank, sum);
MPI_Finalize();
}
My not working Python Code:
from mpi4py import MPI
size = MPI.COMM_WORLD.Get_size()
my_rank = MPI.COMM_WORLD.Get_rank()
to_right =201
max_dims=1
dims = [max_dims]
periods=[max_dims]
dims[0]=size
periods[0]=1
reorder = True
new_comm=MPI.Intracomm.Create_cart(MPI.COMM_WORLD,dims,periods,True)
my_rank= new_comm.Get_rank()
left_right= MPI.Cartcomm.Shift(new_comm,0,1)
left=left_right[0]
right=left_right[1]
sum=0
snd_buf=my_rank
rcv_buf=-1000 #unused value, should be overwritten, only for test purpose
for counter in range(0,size):
MPI.Topocomm.Neighbor_alltoallw(new_comm,snd_buf,rcv_buf)
snd_buf=rcv_buf
sum=sum+rcv_buf
print('PE ', my_rank,'sum=',sum)

Simple ncurses app not reacting to arrow keys

I compiled this simple ncurses program and the up down keys are unresponsive.
Any idea why this does not work?
I am using Fedora Linux 5.7.16-200.fc32.x86_64 the default terminal emulator is XTerm(351). I got no errors or warning building ncurses or making the app.
cc -o test test.c -lncurses
/* test.c */
#include <stdlib.h>
#include <stdio.h>
#include <curses.h>
int main(void) {
WINDOW * mainwin, * childwin;
int ch;
/* Set the dimensions and initial
position for our child window */
int width = 23, height = 7;
int rows = 25, cols = 80;
int x = (cols - width) / 2;
int y = (rows - height) / 2;
/* Initialize ncurses */
if ( (mainwin = initscr()) == NULL ) {
fprintf(stderr, "Error initialising ncurses.\n");
exit(EXIT_FAILURE);
}
/* Switch of echoing and enable keypad (for arrow keys) */
noecho();
keypad(mainwin, TRUE);
/* Make our child window, and add
a border and some text to it. */
childwin = subwin(mainwin, height, width, y, x);
box(childwin, 0, 0);
mvwaddstr(childwin, 1, 4, "Move the window");
mvwaddstr(childwin, 2, 2, "with the arrow keys");
mvwaddstr(childwin, 3, 6, "or HOME/END");
mvwaddstr(childwin, 5, 3, "Press 'q' to quit");
refresh();
/* Loop until user hits 'q' to quit */
while ( (ch = getch()) != 'q' ) {
switch ( ch ) {
case KEY_UP:
if ( y > 0 )
--y;
break;
case KEY_DOWN:
if ( y < (rows - height) )
++y;
break;
case KEY_LEFT:
if ( x > 0 )
--x;
break;
case KEY_RIGHT:
if ( x < (cols - width) )
++x;
break;
case KEY_HOME:
x = 0;
y = 0;
break;
case KEY_END:
x = (cols - width);
y = (rows - height);
break;
}
mvwin(childwin, y, x);
}
/* Clean up after ourselves */
delwin(childwin);
delwin(mainwin);
endwin();
refresh();
return EXIT_SUCCESS;
}
The example doesn't repaint the child-window (so nothing seems to happen), and doesn't use cbreak (so nothing happens until you press Return (i.e., newline).
I did this change to see what it does:
> diff -u foo.c.orig foo.c
--- foo.c.orig 2020-08-30 06:00:47.000000000 -0400
+++ foo.c 2020-08-30 06:02:50.583242935 -0400
## -29,6 +29,7 ##
/* Switch of echoing and enable keypad (for arrow keys) */
+ cbreak();
noecho();
keypad(mainwin, TRUE);
## -85,6 +86,7 ##
}
mvwin(childwin, y, x);
+ wrefresh(childwin);
}
Some terminal descriptions may use the same character ControlJ for cursor-down (and get mapped into KEY_ENTER rather than KEY_DOWN—see source code). After allowing for the other two problems, you may be seeing that.

How to return a int converted to char array back to main for displaying it

My doubts are as follows :
1 : how to send 'str' from function 'fun' , So that i can display it in main function.
2 : And is the return type correct in the code ?
2 : the current code is displaying some different output.
char * fun(int *arr)
{
char *str[5];
int i;
for(i=0;i<5;i++)
{
char c[sizeof(int)] ;
sprintf(c,"%d",arr[i]);
str[i] = malloc(sizeof(c));
strcpy(str[i],c);
}
return str;
}
int main()
{
int arr[] = {2,1,3,4,5},i;
char *str = fun(arr);
for(i=0;i<5;i++)
{
printf("%c",str[i]);
}
return 0;
}
how to send 'str' from function 'fun' , So that i can display it in main function.
This is the way:
char* str = malloc( size );
if( str == NULL ) {
fprintf( stderr,"Failed to malloc\n");
}
/* Do stuff with str, use str[index],
* remember to free it in main*/
free(str);
And is the return type correct in the code ?
No, Probably char** is the one you need to return.
the current code is displaying some different output.
Consider explaining what/why do you want to do ? The way you have written, seems completely messed up way to me. You're passing array of integer but not its length. How is the fun() supposed to know length of array? Another problem is array of pointers in fun().
You can't write a int to a char (See the both size). So I used char array instead.
However, I'm not sure if this is what you want to do (might be a quick and dirty way of doing it):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char**
fun(int *arr, int size)
{
char **str = malloc( sizeof(char*)*size );
if( str == NULL ) {
fprintf( stderr, "Failed malloc\n");
}
int i;
for(i=0;i<5;i++) {
str[i] = malloc(sizeof(int));
if( str == NULL ) {
fprintf( stderr, "Failed malloc\n");
}
sprintf(str[i],"%d",arr[i]);
}
return str;
}
int
main()
{
int arr[] = {2,1,3,4,5},i;
char **str = fun(arr, 5);
for(i=0;i<5;i++) {
printf("%s\n",str[i]);
free(str[i]);
}
free(str);
return 0;
}
I made these changes to your code to get it working:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **fun(int *arr)
{
char **str = malloc(sizeof(char *) * 5);
int i;
for(i = 0; i < 5; i++) {
if ((arr[i] >= 0) && (arr[i] <= 9)) {
char c[2] ;
sprintf(c, "%d", arr[i]);
str[i] = (char *) malloc(strlen(c) + 1);
strcpy(str[i],c);
}
}
return str;
}
int main()
{
int arr[] = {2, 1, 3, 4, 5}, i;
char **str = fun(arr);
for(i = 0; i < 5; i++) {
printf("%s", str[i]);
free(str[i]);
}
printf("\n");
free(str);
return 0;
}
Output
21345
I added a check to make sure that arr[i] is a single digit number. Also, returning a pointer to a stack variable will result in undefined behavior, so I changed the code to allocate an array of strings. I don't check the return value of the malloc calls, which means this program could crash due to a NULL pointer reference.
This solution differs from the others in that it attempts to answer your question based on the intended use.
how to send 'str' from function 'fun' , So that i can display it in main function.
First, you need to define a function that returns a pointer to array.
char (*fun(int arr[]))[]
Allocating variable length strings doesn't buy you anything. The longest string you'll need for 64bit unsigned int is 20 digits. All you need is to allocate an array of 5 elements of 2 characters long each. You may adjust the length to suit your need. This sample assumes 1 digit and 1 null character. Note the allocation is done only once. You may choose to use the length of 21 (20 digits and 1 null).
For readability on which values here are related to the number of digits including the terminator, I'll define a macro that you can modify to suit your needs.
#define NUM_OF_DIGITS 3
You can then use this macro in the whole code.
char (*str)[NUM_OF_DIGITS] = malloc(5 * NUM_OF_DIGITS);
Finally the receiving variable in main() can be declared and assigned the returned array.
char (*str)[NUM_OF_DIGITS] = fun(arr);
Your complete code should look like this:
Code
char (*fun(int arr[]))[]
{
char (*str)[NUM_OF_DIGITS] = malloc(5 * NUM_OF_DIGITS);
int i;
for(i=0;i<5;i++)
{
snprintf(str[i],NUM_OF_DIGITS,"%d",arr[i]); //control and limit to single digit + null
}
return str;
}
int main()
{
int arr[] = {24,1,33,4,5},i;
char (*str)[NUM_OF_DIGITS] = fun(arr);
for(i=0;i<5;i++)
{
printf("%s",str[i]);
}
free(str);
return 0;
}
Output
2413345
With this method you only need to free the allocated memory once.

linux terminal output

Hi I wrote a simple c prog to just accept a password while diplaying * to hide the input. But the * for the last character entered is not appearing at the right place.
the code is below
int main(){
int choice = 0;
char pass[8];
FILE *input;
FILE *output;
struct termios initial_settings, new_settings;
if(!isatty(fileno(stdout))){
fprintf(stderr,"Not a terminal \n");
}
input = fopen("/dev/tty","r");
output = fopen("/dev/tty","w");
if(!input || !output){
fprintf(stderr,"error opening");
exit(1);
}
tcgetattr(fileno(input),&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
new_settings.c_lflag &= ~ISIG;
if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
fprintf(stderr,"could not set attributes\n");
}
int count = 0;
char ch;
printf("Please enter the password: ");
while (count<8){
ch = fgetc(input);
if(ch == '\n' || ch == '\r'){
break;
}else{
fputc('*',stdout);
pass[count] = ch;
count++;
}
tcdrain(fileno(stdout));
}
fprintf(output,"you have entered :%s \n",pass);
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}
The output is as follows:
Please enter the password:* * * * * * *
you have entered :12345678
* pasman#pasman-laptop:~$
Its an 8 character password & Notice that 7 *s appear as expected but the last * is appearing at the end of main.
You're mixing stdio and another stream, output, talking directly to the tty. They have different buffers, and get flushed at different times. You really should just use one of them.
It's because you break before you write the last *: so
add
fputc('*',stdout);
before
tcdrain(fileno(stdout));

How to set last Unused drive letter to Combobox in MFC (VC++)?

How to set last Unused drive letter to Combobox in MFC (VC++) ?
My code is like this :
TCHAR g_szDrvMsg[] = _T("A:\n");
int main(int argc, char* argv[]) {
ULONG uDriveMask = _getdrives();
if (uDriveMask == 0)
{
printf( "_getdrives() failed with failure code: %d\n",
GetLastError()); //So GetLastError retuns a sring or char*?
}
else
{
printf("The following logical drives are being used:\n");
while (uDriveMask) {
if (!(uDriveMask & 1))
m_objCmbdrive.AddString(g_szDrvMsg);
++g_szDrvMsg[0];
uDriveMask >>= 1;
}
}
}
m_objCmbdrive.SetCurSel();
What value i should pass to SetCurSel to set Drive letter in descending order.
This code gives me Drive All drive letters which are being used in the system.
how to get all unused one out ?
To select the last item in your combobox, you can do:
m_objCmbdrive.SetCurSel(m_objCmbdrive.GetCount() - 1);
To fill your combobox with unused drive letters in descending order, use the InsertString() method:
for (int i = 0; i < 26; ++i) {
if (!(uDriveMask & 1)) {
m_objCmbdrive.InsertString(0, g_szDrvMsg);
}
++g_szDrvMsg[0];
uDriveMask >>= 1;
}

Resources