Listening key events on system level - c++

I need to write an application, which will listen to some key events and after they occurs, application will do something (this isn't important for this question).
The application will run like a deamon - on background (possible on system tray) and waiting for input.
The question is, how can I listen to key events on system level? I prefer some Unix C solution (priority isn't portability to non-Unix system), but if there is some handy Qt class, why don't use it?
EDIT: Isn't there some way, to tell the operating system something like: "Hi! I am here, wake me up on 'some keyboard event'!"?

qxtglobalshortcut is for shortcuts. Qt offers different ways to handle native events. It is for example QWidget::nativeEvent or QAbstractNativeEventFilter.
But if you want to use system API, then you can try my code. It is code which executes inside separate thread and asynchronously invoke method to notify user when event occurs. Ready for copy-paste, but set name of your keyboard.
#include <QApplication>
#include <QDebug>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <termios.h>
#include <signal.h>
#include <QSystemTrayIcon>
#include <thread>
QSystemTrayIcon *tray;
void handler (int sig)
{
qDebug ("nexiting...(%d)n", sig);
exit (0);
}
void perror_exit (char *error)
{
perror (error);
handler (9);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
tray = new QSystemTrayIcon;
QPixmap px(20,20);
px.fill(Qt::green);
tray->setIcon(QIcon(px));
tray->show();
tray->showMessage("hello","hello",QSystemTrayIcon::Information,1000);
//need this to use invokeMthod
qRegisterMetaType<QSystemTrayIcon::MessageIcon>("QSystemTrayIcon::MessageIcon");
std::thread thread([tray]()
{
struct input_event ev[64];
int fd, rd, value, size = sizeof (struct input_event);
char name[256] = "Unknown";
char *device = NULL;
if ((getuid ()) != 0)
qDebug ("You are not root! This may not work...n");
//my keyboard,set name of yours
device = "/dev/input/by-id/usb-SIGMACHIP_USB_Keyboard-event-kbd";
//Open Device
if ((fd = open (device, O_RDONLY)) == -1)
qDebug ("%s is not a vaild device.n", device);
//Print Device Name
ioctl (fd, EVIOCGNAME (sizeof (name)), name);
qDebug ("Reading From : %s (%s)n", device, name);
while (1){
if ((rd = read (fd, ev, size * 64)) < size)
perror_exit ("read()");
value = ev[0].value;
if (value != ' ' && ev[1].value == 1 && ev[1].type == 1){ // Only read the key press event
qDebug ("Code[%d]n", (ev[1].code));
QMetaObject::invokeMethod(tray,"showMessage",Qt::QueuedConnection,Q_ARG(QString,"Was pressed"),Q_ARG(QString,QString::number(ev[1].code)),
Q_ARG(QSystemTrayIcon::MessageIcon,QSystemTrayIcon::Information),Q_ARG(int,500));
}
}
});
qDebug("after thread");
return a.exec();
}
I used code from here, but just changed it to be in Qt manner.
To run programm you must use sudo.
sudo /path/to/exe
#if you want to run it inside qt creator but with sudo
sudo /path/to/qtcreator

It's not Qt (yet) but is somehow connected, there is this class in the Qxt library called qxtglobalshortcut
Here is the link: http://libqxt.bitbucket.org/doc/tip/qxtglobalshortcut.html

Short: You can't do this.
Longer answer: You can, but you have to write an keyboard-driver.
Even with root/Administrator privileges, you need an input-widget which will have OS input focus. (This is to avoid grabbing the input from other input widgets, like password-fields or your chat). - I fully agree ddriver's comment.
If your service has a focused-widget, you can use Widget's text-events or use QObject::installEventFilter
Another point: You can reuse/notify the event within your service but not send to other applications. See notify. If the service handled the keyboard event, no other application will receive the event. And if focus-widget's application already accepted key-event, your service does not get it.
But I do agree: Some OS allow to access the keyboad-devices. (Like Chernobyl's answer). For these, you can implement an own-Keyboard-device driver / handler. Example for Linux embedded is: QWSServer.
Note for users: Using such driver is not save! Please be carefull if you use 3rd party keyboard-driver! For Windows it is highly recomanded only to use Windows-trusted drivers.
Anyway: Implementing an own keyboard-driver can solve Rainbow Tom's issue.

Related

Listen close event of iexplorer in my application

I am writing a win32 application by C++, and I want it to do something when all iexplorer.exe were closed.
I know that SetWindowsHook() may be useful in my case.
But if I have no idea about the process or thread ID of IE, because every time open IE would get a different thread ID.
If I do not use timer to check the process list to get the ID of iexplorer, does there have another approach to listen close event of IE in my win32 application?
The object for IE is called InternetExplorer. TheShellWindows object is a collection of InternetExplorer objects. But here it gets complicated. Not all InternetExplorer objects are what you would call an IE window. Some of them are "Windows Explorer" windows. See About the Browser (Internet Explorer).
The following is a managed C++ console program that lists the existing windows and sets a count of the number of existing windows. Then it uses WindowRegistered and WindowRevoked events to monitor creation and closing of windows. Those event are not documented very well. The sample below uses the Document member of each InternetExplorer object to determine if the window has HTML. However see the comment in c# - Distinguishing IE windows from other windows when using SHDocVw; it is possible for a IE window to not have HTML in it.
Note that the following sample is using an AutoResetEvent to keep the program going since it is a console program.
The following is the header:
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <ShlObj.h>
#include <comdef.h>
#include <vcclr.h>
The following is the program:
#include "stdafx.h"
using namespace System;
using namespace System::Threading;
static int Browsers = 0;
static gcroot<AutoResetEvent^> Event;
bool IsBrowser(SHDocVw::InternetExplorer ^ ie)
{
MSHTML::IHTMLDocument2^ Document;
try { Document = (MSHTML::IHTMLDocument2^)ie->Document; }
catch (Exception^ ex)
{
return false;
}
return Document != nullptr;
}
static void WindowRegistered(int lCookie) {
++Browsers;
Console::WriteLine("WindowRegistered");
}
static void WindowRevoked(int lCookie) {
--Browsers;
Console::WriteLine("WindowRevoked");
if (Browsers <= 0)
Event->Set();
}
int main(array<System::String ^> ^args)
{
SHDocVw::ShellWindows^ swList = gcnew SHDocVw::ShellWindowsClass();
Console::WriteLine(L"{0} instances", swList->Count);
for each (SHDocVw::InternetExplorer ^ ie in swList) {
Console::WriteLine(ie->LocationURL);
if (IsBrowser(ie)) {
Console::WriteLine("HTML document");
++Browsers;
}
else
Console::WriteLine("Not HTML");
}
if (Browsers == 0)
{
Console::WriteLine("No browsers");
return 0;
}
Event = gcnew AutoResetEvent(false);
swList->WindowRegistered += gcnew SHDocVw::DShellWindowsEvents_WindowRegisteredEventHandler(WindowRegistered);
swList->WindowRevoked += gcnew SHDocVw::DShellWindowsEvents_WindowRevokedEventHandler(WindowRevoked);
Event->WaitOne();
Console::WriteLine("No more browsers");
return 0;
}
Now I just realized that there is a problem with the way this works. The WindowRegistered and WindowRevoked handlers are incrementing the Browsers count even if the window is not an IE window. I don't know how to determine what window that the cookie passed to WindowRegistered and WindowRevoked represents. A few years ago I spent a couple of days or more tryinig to figure that out. So what you should do is to somehow re-list all the windows after each WindowRegistered and WindowRevoked event.
You need to add references for "Microsoft Internet Controls" (SHDocVw.dll) and "Microsoft HTML Object Library" (mshtml.dll) to the project. They are COM objects that should be in your "C:\Windows\System32" directory.

Port program that uses CreateEvent and WaitForMultipleObjects to Linux

I need to port a multiprocess application that uses the Windows API functions SetEvent, CreateEvent and WaitForMultipleObjects to Linux. I have found many threads concerning this issue, but none of them provided a reasonable solution for my problem.
I have an application that forks into three processes and manages thread workerpool of one process via these Events.
I had multiple solutions to this issue. One was to create FIFO special files on Linux using mkfifo on linux and use a select statement to awaken the threads. The Problem is that this solution will operate differently than WaitForMultipleObjects. For Example if 10 threads of the workerpool will wait for the event and I call SetEvent five times, exactly five workerthreads will wake up and do the work, when using the FIFO variant in Linux, it would wake every thread, that i in the select statement and waiting for data to be put in the fifo. The best way to describe this is that the Windows API kind of works like a global Semaphore with a count of one.
I also thought about using pthreads and condition variables to recreate this and share the variables via shared memory (shm_open and mmap), but I run into the same issue here!
What would be a reasonable way to recreate this behaviour on Linux? I found some solutions doing this inside of a single process, but what about doing this with between multiple processes?
Any ideas are appreciated (Note: I do not expect a full implementation, I just need some more ideas to get myself started with this problem).
You could use a semaphore (sem_init), they work on shared memory. There's also named semaphores (sem_open) if you want to initialize them from different processes. If you need to exchange messages with the workers, e.g. to pass the actual tasks to them, then one way to resolve this is to use POSIX message queues. They are named and work inter-process. Here's a short example. Note that only the first worker thread actually initializes the message queue, the others use the attributes of the existing one. Also, it (might) remain(s) persistent until explicitly removed using mq_unlink, which I skipped here for simplicity.
Receiver with worker threads:
// Link with -lrt -pthread
#include <fcntl.h>
#include <mqueue.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *receiver_thread(void *param) {
struct mq_attr mq_attrs = { 0, 10, 254, 0 };
mqd_t mq = mq_open("/myqueue", O_RDONLY | O_CREAT, 00644, &mq_attrs);
if(mq < 0) {
perror("mq_open");
return NULL;
}
char msg_buf[255];
unsigned prio;
while(1) {
ssize_t msg_len = mq_receive(mq, msg_buf, sizeof(msg_buf), &prio);
if(msg_len < 0) {
perror("mq_receive");
break;
}
msg_buf[msg_len] = 0;
printf("[%lu] Received: %s\n", pthread_self(), msg_buf);
sleep(2);
}
}
int main() {
pthread_t workers[5];
for(int i=0; i<5; i++) {
pthread_create(&workers[i], NULL, &receiver_thread, NULL);
}
getchar();
}
Sender:
#include <fcntl.h>
#include <stdio.h>
#include <mqueue.h>
#include <unistd.h>
int main() {
mqd_t mq = mq_open("/myqueue", O_WRONLY);
if(mq < 0) {
perror("mq_open");
}
char msg_buf[255];
unsigned prio;
for(int i=0; i<255; i++) {
int msg_len = sprintf(msg_buf, "Message #%d", i);
mq_send(mq, msg_buf, msg_len, 0);
sleep(1);
}
}

execl in child process works for only in specific cases

I have been bussy for the last five hours with this problem so I hope someone can help me out. In my C++ program (which I develop in QTcreator on lubuntu) I want to run airodump-ng in the child process of my program. The output of airodump-ng should be directed to the STDOUT of the parent proces. This works with many other programs but strangly enough not with airodump-ng. There is simply no output in the console. This, or my Linux crashes, I get logged out and when I log back in all my programs are closed. Does anybody know why?
#include <QCoreApplication>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//execl("/usr/sbin/airodump-ng", "airodump-ng", (char*)0 );
//dup2(1, 2); //pipe stderr to stdout
pid_t pidAirodump;
pid_t pidAircrack;
int pip[2];
if (pipe(pip) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
pidAirodump = fork();
if(pidAirodump > 0)//parent
{
pidAircrack = fork();
if(pidAircrack == 0)//pidAircrack
{
close(pip[0]);
dup2(pip[1], 2);
cout << "test" << endl;
//execl("/usr/sbin/arp", "arp", (char*)0 );
execl("/usr/sbin/airodump-ng", "airodump-ng ", "mon0", (char*)0 );
exit(0);
}
}
else//pidAirodump
{
exit(0);
}
wait(NULL);
return a.exec();
}
There's a few oddities in your program. But let's start at the question - you should distinguish between execl not working and the program you're trying to execute is misbehaving. You should not be able to crash linux from a user space program without special privilieges. The code snippet you've posted should not do that (what airpodump-ng does is another question).
If execl fails it will return and set errno, I suggest that you examine that after execl instead of just exiting.
Now for the oddities:
The first fork? Why do you do that? You basically forks and exits the child right away. You fork again and let the parent wait - this should trigger on the fact that the first child has terminated rather immediately.
The pipe, why do you do that if you wan't to keep the standard out? Instead you dup standard err to the write end of the pipe, but you doesn't seem to do anything with the read end.

Subprocess icon bounces in dock

My application starts a subprocess program to read video using the QuickTime framework via fork() and pipes. The subprocess goes into a wait loop when it is not busy, i.e. it does usleep until there is input. The subprocess is not a GUI application and it is written in C++.
When opening AVI video coded using the MSVC codec, a second copy of the application icon shows in the dock and bounces. After about 30 seconds in the Activity Monitor I can see that the subprocess changes to "not responding" even though CPU appears to be ~0%. The subprocess is still running and responding; it's just that Activity Monitor says otherwise.
If I look at the state of the subprocess, via gdb attach or check its output; everything looks fine. I can tell the subprocess to close the file and open another one and continue using it at which point the bouncing dock icon disappears and the process is not marked as not responding.
It's as if OSX thinks my subprocess has crashed (?) but I cannot detect an exception.
How can I stop the subprocess showing an icon in the dock, bouncing and being marked as not responding ?
This is how I set up communication with the subprocess:
#include <unistd.h>
#define READ 0
#define WRITE 1
// Start process
pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
// Set up pipes
if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return(-1);
pid = fork();
if(pid < 0)
return(pid);
else if(pid == 0)
{
// Set up communication via stdin/out
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
execvp(command, argv); // run subprocess
perror("execvp");
exit(1);
}
// Provide pointers to the file descriptors to the caller
if(infp == NULL)
close(p_stdin[WRITE]);
else
*infp = p_stdin[WRITE];
if(outfp == NULL)
close(p_stdout[READ]);
else
*outfp = p_stdout[READ];
return(pid);
}
See this SO question for more discussion of popen2().
Note: this code may or may not be the cause of my problem. As a first step, I would really like to prove what is the cause.
The “not responding” part is simple: Your subprocess is not running a runloop of any type, so from the POV of the system, it’s not handling events.
I’m a bit hazier on why your new process is getting a dock icon, but it basically boils down to fork() creating a process that inherits the attributes of the parent process (in this case, of it being a foreground application). OS X has a number of mechanisms to launch subprocesses in more sensible ways than fork(). If your app is in Cocoa, use NSTask, otherwise, take a look at posix_spawn(2).
This should be a drop in replacement for your routine:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <spawn.h>
#include <signal.h>
#include <crt_externs.h>
#define READ 0
#define WRITE 1
#define environ (*_NSGetEnviron())
pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return(-1);
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_init(&file_actions);
posix_spawn_file_actions_adddup2(&file_actions, p_stdin[READ], 0);
posix_spawn_file_actions_adddup2(&file_actions, p_stdout[WRITE], 1);
posix_spawn_file_actions_adddup2(&file_actions, 2, 2);
posix_spawnattr_t spawnAttributes;
posix_spawnattr_init(&spawnAttributes);
sigset_t no_signals;
sigset_t all_signals;
sigemptyset (&no_signals);
sigfillset (&all_signals);
posix_spawnattr_setsigmask(&spawnAttributes, &no_signals);
posix_spawnattr_setsigdefault(&spawnAttributes, &all_signals);
short flags = POSIX_SPAWN_CLOEXEC_DEFAULT | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
posix_spawnattr_setflags(&spawnAttributes, flags);
if (posix_spawn(&pid, command, &file_actions, &spawnAttributes, argv, environ)) {
perror("posix_spawn");
exit(1);
}
close(p_stdin[READ]);
if(infp == NULL)
close(p_stdin[WRITE]);
else
*infp = p_stdin[WRITE];
close(p_stdout[WRITE]);
if(outfp == NULL)
close(p_stdout[READ]);
else
*outfp = p_stdout[READ];
return(pid);
}
If you're using a compiler that supports c++11, I'd recommend checking out packaged_tasks.
Alternatively, you could also use condition_variables, but I'd try to get it working with packaged tasks first. Condition variables are more of the primitive than the higher-level packaged tasks. Either way, it's a lot easier (and standards compliant) to use these mechanisms than tradition IPC techniques.
That is, of course, if you don't have to have a separate process.
For further information, I'd highly recommend checking out The C++ Programming Language, 4th Edition. It has simple example of a producer/consumer mechanism with a simple vector that may suit you well. If you don't spring for the book, I'm sure you can find similar examples online for using condition_variables, futures or promises.
HTH
I ended up rewriting the subprocess as a MacOS XPC service. In the XPC service's property list I added LSBackgroundOnly to get Launch Services to ignore system event handling.

How to abort getchar in a console application when closing it

I've written a simple command line tool that uses getchar to wait for a termination signal (something like: 'Press enter to stop'). I however also want to handle the SC_CLOSE case (clicking the 'close' button). I did this by using SetConsoleCtrlHandler. But how do I cancel my getchar?
I tried doing fputc('\n', stdin);, but that results in a deadlock.
I can call ExitProcess, but then I get a crash in CThreadLocalObject::GetData when deleting a global CWnd, because the CThreadLocalObject is already deleted (okay, maybe I was lying when claiming it was a simple console application). I guess this might have something to do with the fact that the HandlerRoutine is called from a separate thread (not the main thread).
Maybe there's some sort of getchar with a timeout that I can call instead?
Maybe there's some sort of getchar with a timeout that I can call instead?
You can read console input asynchronously:
#ifdef WIN32
#include <conio.h>
#else
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#endif
int main(int argc, char* argv[])
{
while(1)
{
#ifdef WIN32
if (kbhit()){
return getc(stdin);
}else{
Sleep(1000);
printf("I am still waiting for your input...\n");
}
#else
struct timeval tWaitTime;
tWaitTime.tv_sec = 1; //seconds
tWaitTime.tv_usec = 0; //microseconds
fd_set fdInput;
FD_ZERO(&fdInput);
FD_SET(STDIN_FILENO, &fdInput);
int n = (int) STDIN_FILENO + 1;
if (!select(n, &fdInput, NULL, NULL, &tWaitTime))
{
printf("I am still waiting for your input...\n");
}else
{
return getc(stdin);
}
#endif
}
return 0;
}
In such a way, you can introduce bool bExit flag which indicates if your programs is required to terminate. You can read input in specialized thread or wrap this code into the function and call it periodically.