Ncurses background waiting for exit - c++

Consider following code. I would like the program to end after pressing eg. F10 . I don't want to change the behavior of the program, I would like to do in the background, waiting for the key press and then end. How to modify the program to achieve this?
#include <ncurses.h>
#include <unistd.h>
int main () {
int parent_x, parent_y;
int score_size =10;
int counter =0 ;
initscr();
noecho();
curs_set(FALSE);
getmaxyx(stdscr, parent_y, parent_x);
WINDOW *field = newwin(parent_y - score_size, parent_x, 0, 0);
WINDOW *score = newwin(score_size, parent_x, parent_y - score_size, 0);
while(true) {
mvwprintw(field, 0, counter, "Field");
mvwprintw(score, 0, counter, "Score");
wrefresh(field);
wrefresh(score);
sleep(5);
wclear(score);
wclear(field);
counter++;
}
delwin(field);
delwin(score);
endwin();
}

Maybe you could read input somewhere inside the while loop
keypad(field, TRUE);
int loop = 1;
while(loop) {
...
int c = wgetch(field);
switch(c) {
case KEY_F(10):
loop = 0;
break;
default:
break;
}
...
}

this link: https://www.mkssoftware.com/docs/man3/curs_inopts.3.asp
discusses cbreak() and halfdelay() (amongst other ncurses commands)
calling cbreak() near the beginning of the program.
then when looking for a keystroke (without out blocking forever) use halfdelay().
Between those two functions the code should be able to check for a certain keystroke and respond immediately, without having the program block, such as getch() would do.

Combining the two (neither is complete), to recognize F10 , you must call keypad, while to get single-character processing you need something like cbreak, or even raw. Here is a complete example:
#include <ncurses.h>
#include <stdlib.h>
int main (void) {
int parent_x, parent_y;
int score_size =10;
int counter =0 ;
bool loop = TRUE;
WINDOW *field;
WINDOW *score;
initscr();
cbreak();
noecho();
curs_set(FALSE);
getmaxyx(stdscr, parent_y, parent_x);
field = newwin(parent_y - score_size, parent_x, 0, 0);
score = newwin(score_size, parent_x, parent_y - score_size, 0);
keypad(field, TRUE);
halfdelay(1);
while(loop) {
int c = wgetch(field);
switch(c) {
case KEY_F(10):
loop = FALSE;
continue;
default:
break;
}
mvwprintw(field, 0, counter, "Field");
mvwprintw(score, 0, counter, "Score");
wrefresh(field);
wrefresh(score);
napms(5000); /* don't use sleep(5) */
wclear(score);
wclear(field);
counter++;
}
delwin(field);
delwin(score);
endwin();
return EXIT_SUCCESS;
}

The easiest way is to replace your sleep(5) with a call to select() with a 5 second timeout, for example:
#include <stdlib.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
int main(void) {
int parent_x, parent_y;
int score_size = 10;
int counter = 0;
WINDOW * mainwin = initscr();
noecho();
crmode();
keypad(mainwin, TRUE);
wrefresh(mainwin);
curs_set(FALSE);
getmaxyx(stdscr, parent_y, parent_x);
WINDOW *field = newwin(parent_y - score_size, parent_x, 0, 0);
WINDOW *score = newwin(score_size, parent_x, parent_y - score_size, 0);
while ( true ) {
mvwprintw(field, 0, counter, "Field");
mvwprintw(score, 0, counter, "Score");
wrefresh(field);
wrefresh(score);
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
int status = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
if ( status == -1 ) {
perror("error calling select()");
exit(EXIT_FAILURE);
}
else if ( status == 1 ) {
if ( wgetch(mainwin) == KEY_F(10) ) {
break;
}
}
wclear(score);
wclear(field);
counter++;
}
delwin(field);
delwin(score);
endwin();
}
The drawback with this is that if you press a key other than F10, your loop will repeat immediately and the 5 second delay will restart. The easiest way to fix this is to make your delay contingent on a timer, for instance:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
/* Signal handler, doesn't need to do anything */
void handler(int signum)
{
(void) signum; /* Do nothing */
}
int main(void)
{
int parent_x, parent_y;
int score_size = 10;
int counter = 0;
/* Register signal handler for SIGALRM */
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if ( sigaction(SIGALRM, &sa, NULL) == -1 ) {
perror("error calling sigaction()");
exit(EXIT_FAILURE);
}
/* Create timer */
struct itimerval itv;
itv.it_interval.tv_sec = 5;
itv.it_interval.tv_usec = 0;
itv.it_value.tv_sec = 5;
itv.it_value.tv_usec = 0;
if ( setitimer(ITIMER_REAL, &itv, NULL) != 0 ) {
perror("seeor calling setitimer()");
exit(EXIT_FAILURE);
}
/* Initialize curses */
WINDOW * mainwin = initscr();
noecho();
crmode();
keypad(mainwin, TRUE);
wrefresh(mainwin);
curs_set(FALSE);
/* Create windows */
getmaxyx(stdscr, parent_y, parent_x);
WINDOW *field = newwin(parent_y - score_size, parent_x, 0, 0);
WINDOW *score = newwin(score_size, parent_x, parent_y - score_size, 0);
while ( true ) {
mvwprintw(field, 0, counter, "Field");
mvwprintw(score, 0, counter, "Score");
wrefresh(field);
wrefresh(score);
/* Wait for available input */
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
int status = select(STDIN_FILENO + 1, &fds, NULL, NULL, NULL);
if ( status == -1 ) {
/* select() returned an error... */
if ( errno == EINTR ) {
/* Interrupted by SIGALRM, so update counter */
wclear(score);
wclear(field);
counter++;
}
else {
/* Other error, so quit */
delwin(field);
delwin(score);
endwin();
perror("error calling select()");
exit(EXIT_FAILURE);
}
}
else if ( status == 1 ) {
/* Input ready, so get and check for F10 */
if ( wgetch(mainwin) == KEY_F(10) ) {
break;
}
}
}
/* Clean up and exit */
delwin(field);
delwin(score);
endwin();
return 0;
}
which will do exactly what you're looking for. There's a small possibility SIGALRM might fire in between select() calls, causing an update to be skipped. You can take care of this by switching to pselect(), which I'll leave as an exercise for you.

Related

How to fix the next thread to be more correct? Using Pthread

I'm investigating the use of PThread.
The main process opens the camera and gets a matrix. Then calls the thread that running job in robot and I want it to be parallel. Basically it works and runs. But still feel unprofessional- because of the bool.
In the code below, this is an example (with fprintf).
I'd love to know how I can fix it without harm parallelism.
In the next code I do not show the call to the robot or camera opening.
There is a feeling that a mutex is needed.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <opencv2/opencv.hpp>
#include <unistd.h> /// for sleep
bool inThread = false;
void *print_message_function( void *ptr );
int main()
{
char mkey = 0;
pthread_t thread1;
char *message1 = "Thread 1";
int iret1;
cv::Mat bgr_image = imread("image.bmp",cv::IMREAD_COLOR);
while(mkey!=27){
if(!inThread){
inThread = true;
iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);
}
printf("In Main");
imshow("mat", bgr_image);
mkey = cv:: waitKey(5);
}
return 0;
}
void *print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s \n", message);
sleep(2);
inThread = false;
pthread_exit(NULL);
}
The code works great and does not fall, but it seems unprofessional. Is there a chance that when you update the flag, it will check what is in the flag and fall?
inThread is concurrently read/written so its access shall be protected.
Using a mutex this can for example be done like follows.
Define a global mutex and initialise it:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
Include errno to be able to do convenient error checking/logging for the pthread_*() calls:
#include <errno.h>
Change this
if(!inThread){
inThread = true;
iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);
}
to become
errno = pthread_mutex_lock(&m);
if (errno) {
perror("pthread_mutex_lock() failed");
exit(EXIT_FAILURE);
}
if (!inThread) {
inThread = true;
errno = pthread_mutex_unlock(&m);
if (errno) {
perror("pthread_mutex_unlock() failed");
exit(EXIT_FAILURE);
}
...
}
else {
errno = pthread_mutex_unlock(&m);
if (errno) {
perror("pthread_mutex_unlock() failed");
exit(EXIT_FAILURE);
}
}
And change this
inThread = false;
to become
errno = pthread_mutex_lock(&m);
if (errno) {
perror("pthread_mutex_lock() failed");
exit(EXIT_FAILURE);
}
inThread = false;
errno = pthread_mutex_unlock(&m);
if (errno) {
perror("pthread_mutex_unlock() failed");
exit(EXIT_FAILURE);
}

C++ Ubuntu select() if serial interface has data on asynchronous read

I´m writing an asynchronous serial data reader class for Ubuntu using C++ and termios and I´m facing difficulties checking is there is data available.
Here is my code:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
class MySerialClass {
public:
MySerialClass(std::string port);
virtual ~MySerialClass();
void openSerial();
void closeSerial();
void configureSerial();
void writeSerial(std::vector<char> data);
void readSerial(std::vector<char> &data, unsigned int numBytes);
private:
int fd = 0; // The serial file descriptor
fd_set fdset; // The set to check on select
std::string portName = "";
};
MySerialClass::MySerialClass(std::string port) : portName(port) {}
MySerialClass::~MySerialClass() {}
void MySerialClass::openSerial()
{
fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
}
void MySerialClass::closeSerial()
{
close(fd);
}
void MySerialClass::configureSerial()
{
struct termios config = { 0 };
tcgetattr(fd, &config);
config.c_iflag = IGNPAR | ICRNL;
config.c_oflag = 0;
config.c_lflag = ICANON;
config.c_cc[VINTR] = 0; /* Ctrl-c */
config.c_cc[VQUIT] = 0; /* Ctrl-\ */
config.c_cc[VERASE] = 0; /* del */
config.c_cc[VKILL] = 0; /* # */
config.c_cc[VEOF] = 4; /* Ctrl-d */
config.c_cc[VTIME] = 0; /* inter-character timer unused */
config.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */
config.c_cc[VSWTC] = 0; /* '\0' */
config.c_cc[VSTART] = 0; /* Ctrl-q */
config.c_cc[VSTOP] = 0; /* Ctrl-s */
config.c_cc[VSUSP] = 0; /* Ctrl-z */
config.c_cc[VEOL] = 0; /* '\0' */
config.c_cc[VREPRINT] = 0; /* Ctrl-r */
config.c_cc[VDISCARD] = 0; /* Ctrl-u */
config.c_cc[VWERASE] = 0; /* Ctrl-w */
config.c_cc[VLNEXT] = 0; /* Ctrl-v */
config.c_cc[VEOL2] = 0; /* '\0' */
speed_t sp = B9600;
config.c_cflag |= CSIZE;
config.c_cflag |= CS8;
cfsetispeed(&config, sp);
cfsetospeed(&config, sp);
tcsetattr(fd, TCSAFLUSH, &config);
}
void MySerialClass::writeSerial(std::vector<char> data)
{
char buffer[1024];
if (data.size() > 1024)
return;
int index = 0;
for (char &item : data)
buffer[index++] = item;
unsigned int size = data.size();
write(fd, &buffer[0], size);
}
void MySerialClass::readSerial(std::vector<char> &data, unsigned int numBytes)
{
char buffer[1024];
data.clear();
if (numBytes > 1024)
return;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
int ret = select(fd + 1, 0, 0, 0, &tv);
std::cout << "Select returns: " << ret << std::endl;
if (!ret)
return;
read(fd, &buffer[0], numBytes);
for (unsigned int i = 0; i < numBytes; i++)
data.push_back(buffer[i]);
}
int main()
{
MySerialClass serial("/dev/ttyS1");
serial.openSerial();
serial.configureSerial();
while(1)
{
std::vector<char> retData;
serial.readSerial(retData, 100);
std::string retString(retData.begin(), retData.end());
if (retString == "END")
{
serial.closeSerial();
break;
}
}
}
It compiles fine, but it never receives data as the select() statement always returns zero. The code with blocking option and without the select() works fine (just comment the select() line and remove O_NODELAY from open()).
I´m pretty sure this problem is related to the way select() is being used (it´s my first time with select()).
Can someone help me to solve that ? The code is available at Coliru here
BTW: Another doubt I have relative to select() is that this class will be used on a multithreaded environmet. I need to make sure that each class instance will check only for its port busy (it´s own fd), no other threads port busy.
Didn't specify a fd_set to read. Try this:
fd_set readfs; /* file descriptor set */
FD_ZERO(&readfs); /* clear the set */
FD_SET(fd, &readfs); /* put the fd in the set */
int ret = select(fd + 1, &readfs, 0, 0, &tv);
Edit: That should also solve your other question. Each select is only looking at the file descriptors you tell it to look at.
Gah. Bad English grammar, but it looks even worse corrected.

Weird characters in COM Serial Port echo of HELLO WORLD

I have code that sends HELLO WORLD:$ from my PC to COM6 to a TIVAC board. I have confirmed through IAR that the board receives the right message. Note that $ is the terminating character.
I have it set up on the TIVAC board to echo the same message through UART and have confirmed manually through Putty that the echo is correct. However when using this following program which sends the same message and listens for the echo I get weird characters in the echo as shown in this image:
It might be an error in the encoding but how do I fix that?
#include <string>
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <io.h>
#include <stdlib.h>
#include <iostream>
#include <winbase.h>
#include <tchar.h>
HANDLE GetSerialPort(char *);
void delay();
int main(void)
{
//
COMMTIMEOUTS timeouts;
HANDLE h1;
char h1_buffer[] = {"HELLO WORLD:$"};
char h2_buffer[24];
DWORD byteswritten, bytesread;
char c1[] = {"COM6"};
char c2[] = {"COM6"};
h1 = GetSerialPort(c1);
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
WriteFile(h1, h1_buffer, strlen(h1_buffer), &byteswritten, NULL);
do
{
bool exit = FALSE;
ReadFile(h1, h2_buffer, strlen(h2_buffer) + 1, &bytesread, NULL);
if(bytesread)
{
h2_buffer[strlen(h2_buffer)] = '\0';
std::string mystring(h2_buffer);
std::cout << "String is : " << mystring << "\n" ;
printf("GOT IT %d\n", strlen(h2_buffer));
ReadFile(h1, h2_buffer, strlen(h2_buffer) + 1, &bytesread, NULL);
printf("%s\n", h2_buffer);
printf("GOT IT %d\n", strlen(h2_buffer));
}
else
{
char stop;
printf("Nothing read\n");
printf("Do you want to exit? ");
scanf(" %c", stop);
if(stop == 'N' || stop == 'n')
{
exit = TRUE;
}
}
}while(1);
printf("EXIT ");
CloseHandle(h1);
}
HANDLE GetSerialPort(char *p)
{
HANDLE hSerial;
hSerial = CreateFile(p,GENERIC_READ | GENERIC_WRITE, 0,0,OPEN_EXISTING,0, 0);
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
dcbSerialParams.BaudRate=CBR_115200;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
dcbSerialParams.fParity = 0;
dcbSerialParams.ByteSize=DATABITS_8;
dcbSerialParams.fDtrControl = 0;
dcbSerialParams.fRtsControl = 0;
return hSerial;
}
void delay ()
{
int i = 1000000000;
printf("In delay\n");
while(i>0)
{
i--;
}
}
Many problems in this code.
Calling strlen() on uninitialised memory will give undefined behaviour.
You don't check for a partial write on the WriteFile() call.
Don't check the return value on ReadFile()
Call strlen() on the data received from ReadFile() instead of using bytesread.
Etc.
You should not be using strlen() on data you get from somewhere else like this -- you should be checking your data and paying attention to the byte counts from your I/O calls.

Dead lock pthread C++ with signaling

I need 2 threads: TC and TS, such that they are composed in two main sections each accessing shared data and the threads must to be synchronized. The synchronization should be like this:
The red codes are working with shared data U, but get U can be placed before the dashed rectangle on the TS thread, riht side.
The Xcurrent can be in TC or in TS the tasks, but is a shared hardware with send Ucurrent and get must be right after send was finished.
A dead lock appears and I can't figure out an elegant solution.
Thread TS:
#include <stdio.h> /* printf, scanf, NULL */
#include <stdlib.h> /* malloc, free, rand */
#define _USE_MATH_DEFINES
#include <math.h>
#include <windows.h>
#include <pthread.h>
#include <time.h>
// CRLT-C var
static int stop = 0;
// TIMEING vars
__int64 frequencyT, startT, endT = 0;
double baseAngleLast;
double pendulAngleLast;
// THREADING vars
bool startedS = false, Usent = false;
double * Ucmd;
pthread_mutex_t mutexU = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutexBoard = PTHREAD_MUTEX_INITIALIZER;
unsigned int Ulenght = 6;
pthread_cond_t signal_to_read_state = PTHREAD_COND_INITIALIZER;
pthread_cond_t signal_to_read_cmd = PTHREAD_COND_INITIALIZER;
DWORD sleepTime = 30; // in milliseconds
int currentState = 3;
void *sendingCMD(void * )
{
double * U_tmp;
U_tmp = (double*)malloc(sizeof(double)*Ulenght);
startedS = true;
printf("Sin\n");
while (stop == 0)
{
printf("Smu-\n");
//get U
pthread_mutex_lock( &mutexU );
printf("Smu..\n");
pthread_cond_wait(&signal_to_read_cmd, &mutexU);
memcpy(U_tmp,Ucmd,sizeof(double)*Ulenght);
pthread_mutex_unlock( &mutexU );
printf("Smu+\n");
pthread_mutex_lock( &mutexBoard );
for (unsigned int i = 0; i<Ulenght; i++)
{
//send CMD signal
printf("%f ", U_tmp[i]);
if (i == Ulenght -1) printf("\n");
}
Sleep(sleepTime); // miliseconds
currentState = currentState + 1;
pthread_cond_signal(&signal_to_read_state);
pthread_mutex_unlock( &mutexBoard );
printf("Smb\n");
}
printf("Task S terminated. \n");
return (NULL);
}
void *computingU(void *)
{
double * U_tmp;
U_tmp = (double*)malloc(sizeof(double)*Ulenght);
int currentStateTMP =0;
bool fisrtLoop = true;
printf("Uin\n");
while (stop == 0)
{
printf("Umb- \n");
// get current state
pthread_mutex_lock( &mutexBoard );
if (!fisrtLoop)
{
printf("UmbFalse \n");
pthread_cond_wait(&signal_to_read_state, &mutexBoard);
}
else
{
printf("UmbTrue \n");
fisrtLoop=false;
}
currentStateTMP =currentState;
pthread_mutex_unlock( &mutexBoard );
printf("Umb+ \n");
pthread_mutex_lock( &mutexU );
for (unsigned int i=0;i<Ulenght;i++)
{
Ucmd[i] = Ucmd[i]+ (double)currentStateTMP/i;
}
pthread_cond_signal(&signal_to_read_cmd);
pthread_mutex_unlock( &mutexU );
printf("Umu\n");
}
return (NULL);
}
void signal_handler(int signal)
{
stop = 1;
}
int main(int argc, char* argv[])
{
//initializing output buffer to 0[V]
Ucmd= (double*)malloc(sizeof(double)*Ulenght);
for (unsigned int i=0;i<Ulenght;i++)
Ucmd[i] = 0;
//init threads
int rc1, rc2;
pthread_t threadU, threadS;
/* Create independent threads each of which will execute functionC */
if( (rc1=pthread_create( &threadU, NULL, &computingU, NULL)) ) {
printf("ThreadU creation failed: %d\n", rc1);
}
if( (rc2=pthread_create( &threadS, NULL, &sendingCMD, NULL)) )
{
printf("ThreadS creation failed: %d\n", rc2);
}
while (stop == 0);
printf("Main terminated, closing board in 10ms. \n");
Sleep(10);
return 0;
}
The blocking appears at:
TC at pthread_cond_wait(&signal_to_read_state, &mutexBoard);
TS at pthread_cond_wait(&signal_to_read_cmd, &mutexU);
btw why dose not recognize stackoverflow the code segment I pasted above in case i copy paste from a VS2010?

Listening to keyboard events without consuming them in X11 - Keyboard hooking

I tried to write a program which hooks keyboard messages to pronounce the name of each key whenever it is pressed in Ubuntu (KDE); without interfering with normal action of keyboard in programs (just announcing the key name).
This is my program:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
void SendPressKeyEvent(Display *display, XKeyEvent xkey)
{
Window current_focus_window;
int current_focus_revert;
XGetInputFocus(display, &current_focus_window, &current_focus_revert);
xkey.type = KeyPress;
xkey.display = display;
xkey.window = current_focus_window;
xkey.root = DefaultRootWindow(display);
xkey.subwindow = None;
xkey.time = 1000 * time(0);
xkey.x = 0;
xkey.y = 0;
xkey.x_root = 0;
xkey.y_root = 0;
xkey.same_screen = True;
XSendEvent(display, InputFocus, True, KeyPressMask, (XEvent *)(&xkey));
}
void SendReleaseKeyEvent(Display *display, XKeyEvent xkey)
{
Window current_focus_window;
int current_focus_revert;
XGetInputFocus(display, &current_focus_window, &current_focus_revert);
xkey.type = KeyRelease;
xkey.display = display;
xkey.window = current_focus_window;
xkey.root = DefaultRootWindow(display);
xkey.subwindow = None;
xkey.time = 1000 * time(0);
xkey.x = 0;
xkey.y = 0;
xkey.x_root = 0;
xkey.y_root = 0;
xkey.same_screen = True;
XSendEvent(display, InputFocus, True, KeyReleaseMask, (XEvent *)(&xkey));
}
void *TaskCode(void* arg)
{
switch(*(int*)arg)
{
case 38:
system("espeak -v en " "\"a\"");
}
return 0;
}
int main()
{
Display *display = XOpenDisplay(0);
if(display == 0)
exit(1);
XGrabKeyboard(display, DefaultRootWindow(display), True, GrabModeAsync, GrabModeAsync, CurrentTime);
XEvent event;
while(true)
{
XNextEvent(display, &event);
if(event.type == Expose)
{
}
if(event.type == KeyPress)
{
SendPressKeyEvent(display,event.xkey);
if(event.xkey.keycode == 38)
{
pthread_t thread;
int thread_arg = event.xkey.keycode;
pthread_create(&thread,0, TaskCode, (void*) &thread_arg);
}
}
if(event.type == KeyRelease)
SendReleaseKeyEvent(display,event.xkey);
}
XCloseDisplay(display);
}
This program is just for the key a which can be extended to other keys.
But when this program is running, some programs (e.g. Chromium) do not show the blinker (cursor) in their edit boxes. Also all KDE hotkeys become disabled.
How can this be fixed?
Here's my quick and dirty example
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <ctype.h>
int main ()
{
Display* d = XOpenDisplay(NULL);
Window root = DefaultRootWindow(d);
Window curFocus;
char buf[17];
KeySym ks;
XComposeStatus comp;
int len;
int revert;
XGetInputFocus (d, &curFocus, &revert);
XSelectInput(d, curFocus, KeyPressMask|KeyReleaseMask|FocusChangeMask);
while (1)
{
XEvent ev;
XNextEvent(d, &ev);
switch (ev.type)
{
case FocusOut:
printf ("Focus changed!\n");
printf ("Old focus is %d\n", (int)curFocus);
if (curFocus != root)
XSelectInput(d, curFocus, 0);
XGetInputFocus (d, &curFocus, &revert);
printf ("New focus is %d\n", (int)curFocus);
if (curFocus == PointerRoot)
curFocus = root;
XSelectInput(d, curFocus, KeyPressMask|KeyReleaseMask|FocusChangeMask);
break;
case KeyPress:
printf ("Got key!\n");
len = XLookupString(&ev.xkey, buf, 16, &ks, &comp);
if (len > 0 && isprint(buf[0]))
{
buf[len]=0;
printf("String is: %s\n", buf);
}
else
{
printf ("Key is: %d\n", (int)ks);
}
}
}
}
It's not reliable but most of the time it works. (It is showing keys I'm typing into this box right now). You may investigate why it does fail sometimes ;) Also it cannot show hotkeys in principle. Hotkeys are grabbed keys, and only one client can get a grabbed key. Absolutely nothing can be done here, short of loading a special X11 extension designed for this purpose (e.g. XEvIE).
Thanks to n.m.'s answer and parsa's comment, this is my final code:
#include <X11/Xlib.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
void* TaskCode(void* parg)
{
int keycode = *((int*)parg);
cout<< "\n\n" << keycode << "\n\n";
if(keycode == XKeysymToKeycode(XOpenDisplay(0),'a'))
system("espeak -v en " "\"a\"");
delete (int*)parg;
return 0;
}
void Action(int keycode)
{
pthread_t thread;
pthread_attr_t attrs;
pthread_attr_init(&attrs);
pthread_attr_setdetachstate(&attrs,PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attrs, 1000);
int* pthread_arg = new int;
*pthread_arg = keycode;
pthread_create(&thread,&attrs, TaskCode, (void*) pthread_arg);
}
int MyX11ErrorHandler(Display *, XErrorEvent *error_event)
{
cout << "\n\n" "An X11-Functions error occured. Probably the focused window was closed.""\n"
"This error will be ignored." "\n";
cout<< "error_code: " << (unsigned)error_event -> error_code << "\n";
cout<< "minor_code: " << (unsigned)error_event -> minor_code << "\n";
cout<< "request_code: " << (unsigned)error_event -> request_code << "\n";
cout<< "resourceid: " << error_event -> resourceid << "\n";
cout<< "serial; " << error_event -> serial << "\n";
cout<< "type: " << error_event -> type << "\n\n";
return 0;
}
int main()
{
Display* display = XOpenDisplay(0);
Window root = DefaultRootWindow(display);
Window current_focus_window;
int revert;
XSetErrorHandler(MyX11ErrorHandler);
XGetInputFocus(display, &current_focus_window, &revert);
XSelectInput(display,current_focus_window,KeyPressMask | KeyReleaseMask | FocusChangeMask);
while(true)
{
XEvent event;
XNextEvent(display, &event);
switch (event.type)
{
case FocusOut:
if(current_focus_window != root)
XSelectInput(display, current_focus_window, 0);
XGetInputFocus(display, &current_focus_window, &revert);
if(current_focus_window == PointerRoot)
current_focus_window = root;
XSelectInput(display, current_focus_window, KeyPressMask|KeyReleaseMask|FocusChangeMask);
break;
case KeyPress:
Action(event.xkey.keycode);
break;
}
}
}
Add these to a Qt Creator's project .pro file:
LIBS += -lX11
LIBS += -lpthread
LIBS += -lXtst
Any improvement suggestions is appreciated.
To archive I also add my final code with grabbing:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
void* TaskCode(void* parg)
{
int keycode = *((int*)parg);
cout<< "\n\n" << keycode << "\n\n";
system("espeak -v en " "\"a\"");
delete (int*)parg;
return 0;
}
void SendKeyEvent(Display *display, XEvent event)
{
Window current_focus_window;
XKeyEvent& xkey = event.xkey;
int current_focus_revert;
XGetInputFocus(display, &current_focus_window, &current_focus_revert);
xkey.state = Mod2Mask;
XSendEvent(display, InputFocus, True, xkey.type, (XEvent *)(&event));
}
int GrabKey(Display* display, Window grab_window, int keycode)
{
unsigned int modifiers = Mod2Mask; // numlock on
//Window grab_window = DefaultRootWindow(display);
Bool owner_events = True;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(display, keycode, modifiers, grab_window, owner_events, pointer_mode, keyboard_mode);
return keycode;
}
void UngrabKey(Display* display, Window grab_window, int keycode)
{
unsigned int modifiers = Mod2Mask; // numlock on
// Window grab_window = DefaultRootWindow(display);
XUngrabKey(display,keycode,modifiers,grab_window);
}
void Action(int keycode)
{
pthread_t thread;
int* pthread_arg = new int;
*pthread_arg = keycode;
pthread_create(&thread,0, TaskCode, (void*) pthread_arg);
}
int main()
{
Display* display = XOpenDisplay(0);
Window root = DefaultRootWindow(display);
XEvent event;
int keycode = XKeysymToKeycode(display,'a');
GrabKey(display,root,keycode);
XSelectInput(display, root, KeyPressMask | KeyReleaseMask);
while(true)
{
XNextEvent(display, &event);
switch(event.type)
{
case KeyPress:
Action(event.xkey.keycode);
case KeyRelease:
SendKeyEvent(display,event);
default:
break;
}
}
XCloseDisplay(display);
}
Everything is good except that, unlike the code in question, it ignores language layout. Pressing a types a whatever regradless of language layout!
As an alternative to listening to X events, it's also possible to listen to Linux input events directly: https://stackoverflow.com/a/27693340/21501
This has the benefit that it's possible to modify the event stream in-flight, and block, edit or generate input events.
The proper way to listen to all events is using the X Record Extension Library, part of libXtst, apparently installed on pretty much every X system. It is documented here, but as the docs are patchy, you will need to browse previous implementations of this. Here is a nice working demo, and here is a more capable and complete implementation.
A simplified version of the first example is included below.
#include <stdio.h>
#include <X11/XKBlib.h>
#include <X11/extensions/record.h>
void key_pressed_cb(XPointer arg, XRecordInterceptData *d);
int scan(int verbose) {
XRecordRange* rr;
XRecordClientSpec rcs;
XRecordContext rc;
Display *dpy = XOpenDisplay(NULL);
rr = XRecordAllocRange();
rr->device_events.first = KeyPress;
rr->device_events.last = ButtonReleaseMask;
rcs = XRecordAllClients;
rc = XRecordCreateContext (dpy, 0, &rcs, 1, &rr, 1);
XFree (rr);
XRecordEnableContext(dpy, rc, key_pressed_cb, NULL);
}
void key_pressed_cb(XPointer arg, XRecordInterceptData *d) {
if (d->category != XRecordFromServer)
return;
int key = ((unsigned char*) d->data)[1];
int type = ((unsigned char*) d->data)[0] & 0x7F;
int repeat = d->data[2] & 1;
if(!repeat) {
switch (type) {
case KeyPress:
printf("key press %d\n", key);
break;
case KeyRelease:
printf("key release %d\n", key);
break;
case ButtonPress:
printf("button press %d\n", key);
break;
case ButtonRelease:
printf("button release %d\n", key);
break;
default:
break;
}
}
XRecordFreeData (d);
}
int main() {
scan(True);
return 0;
}
gcc -o x1 x1.c -lX11 -lXtst