Linux - SoftwareThis forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I have a wireless numpad that was designed for windows,
so some of the keys rather than sending a keycode send a sequence of keys to do a task.
for example the excel button sends(from xev)
win+R
excel
return
is there a program that can look for this sequence then perform a different action?
i know xmodmap can map individual keys but couldn't get it doing multiple.
Set up a keyboard shortkut from the initial keypress (Super + R) to the run app dialog of your desktop environment. For example, xfrun4 if using XFCE 4. Then, put your desired action into a script named excel somewhere in your PATH.
Yes, I know. That's ugly. But so is a keyboard which types complete commands, instead of keyboard events. And this is the way the keyboard works in Windows.
If that is too ugly for you, there is a cleaner alternative. Unfortunately I cannot find a suitable tool ready for this, so it'd require a bit of X11 programming. You need a simple application that captures X11 input for a short while using an invisible window, or until return/enter is pressed, then closes the window and executes a script with the input as a parameter. I'd say this needs about 200 lines of C or C++, using plain X11 libraries.
Nominal Animal
Last edited by Nominal Animal; 03-21-2011 at 02:52 AM.
The usage of this program is simple: You give it a timeout in (integer) seconds, and the path to a script to run. If the user types a command within the given period and presses enter, the script is run, with an additional parameter containing the numbers and (lowercase) letters of the typed command. (Shift, Control, Caps Lock and all such are ignored; the letters will always be in lower case.)
In your case, set an application shortcut to x11grab 2 /home/cohesus/scripts/numpad for the Super+R (aka Win+R) keypress. Then, in the /home/cohesus/scripts/numpad script, check if $1 matches excel and do the action you wish:
Code:
#!/bin/bash
[ "$1" == "excel" ] && exec oocalc
The timeout is just a safety feature, in case you press Super+R yourself. You can set the timeout to as low as 1 second. However, note that x11grab will only run the command if it receives the enter or return keypress before the timeout expires. So, if the wireless keypad is slow, x11grab might give up too early, leaving the last letters and the return to whatever application you had active.
I would appreciate very much if you tried this, and let me know if it works for you. If so, I could clean up the code and write a manpage for it.
Nominal Animal
Last edited by Nominal Animal; 03-21-2011 at 02:51 AM.
Got it working in the command line bring up a dialogue box.
Got it working with win+R nicely as well.
The only issue I have is that if I am in a area that can take an input like a terminal or document.
the shortcut gets triggers x11grab, x11grab exits but with no output and 'excel' is passed onto the input box as typed text
this happens within a second even if x11grab set to 99 seconds.
Thank you so much this is a really useful application and I really appreciate it.
Internal help text: run without parameters or with -h to see the help.
Supports decimal timeouts (e.g. 1.25)
Grabs the pointer, keyboard, and input focus for the duration, or until Return or Enter is pressed
Hides the mouse pointer too for good measure
Lists all keypresses using symbolic X11 names, separated by spaces, as a single parameter to the script
For example, typing "4.2 !" might show up as "4 period 2 space Shift_L 1".
Should be easy to hack and develop further
Testing mode: run x11grab 10 echo and type something for less than then seconds and press Return or Enter, and you'll see the symbolic names of the keys you pressed, in order
Save the following as x11grab.c:
Code:
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysymdef.h>
#define MAX_KEYPRESSES 1023
#define POLLING_INTERVAL_US 10
/* Timeout flag.
*/
volatile sig_atomic_t timeout = 0;
/* Timeout signal handler.
*/
void signal_timeout(int signum)
{
timeout = 1;
}
/* Set a timeout (in wall time).
*/
int set_timeout(const double seconds)
{
struct itimerval interval;
struct sigaction handler;
if (seconds < 0.0)
return EINVAL;
handler.sa_handler = signal_timeout;
sigemptyset(&handler.sa_mask);
handler.sa_flags = 0;
if (sigaction(SIGALRM, &handler, NULL) == -1)
return errno;
interval.it_interval.tv_sec = 0;
interval.it_interval.tv_usec = 0;
interval.it_value.tv_sec = (int)seconds;
interval.it_value.tv_usec = (int)(1000000.0 * (seconds - (double)interval.it_value.tv_sec));
if (interval.it_value.tv_usec < 0) interval.it_value.tv_usec = 0;
if (interval.it_value.tv_usec > 999999) interval.it_value.tv_usec = 999999;
timeout = !(interval.it_value.tv_sec || interval.it_value.tv_usec);
if (setitimer(ITIMER_REAL, &interval, NULL) == -1)
return errno;
return 0;
}
/* Cancel pending timeout.
*/
int cancel_timeout(void)
{
struct itimerval interval;
interval.it_interval.tv_sec = 0;
interval.it_interval.tv_usec = 0;
interval.it_value.tv_sec = 0;
interval.it_value.tv_usec = 0;
if (setitimer(ITIMER_REAL, &interval, NULL) == -1)
return errno;
signal(SIGALRM, SIG_DFL);
return 0;
}
/* Execute the first argument. If the first string contains a slash,
* use execv(), otherwise use execvp() to search for the file in PATH.
* Returns errno if fails.
*/
int execv_auto(char *const args[])
{
if (!args)
return EINVAL;
if (!args[0])
return EINVAL;
if (!args[0][0])
return EINVAL;
if (strchr(args[0], '/'))
execv(args[0], args);
else
execvp(args[0], args);
return errno;
}
/* Output usage information.
*/
int usage(const char *const argv0, const int status)
{
fprintf(stderr, "x11grab version 0.2 by Nominal Animal\n");
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s seconds executable [ arguments ... ]\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "This program is designed to grab X11 keypresses, up to the next return.\n");
fprintf(stderr, "If the specified timeout in seconds elapses before the return keypress,\n");
fprintf(stderr, "nothing is done; the already grabbed input is quietly discarded.\n");
fprintf(stderr, "\n");
fprintf(stderr, "If a return keypress occurs before the timeout, the specified executable\n");
fprintf(stderr, "is executed with the parameters given on the command line, plus\n");
fprintf(stderr, "the captured keypresses as a single parameter, using X11 symbolic names,\n");
fprintf(stderr, "each keypress symbol separated by a space.\n");
fprintf(stderr, "\n");
fprintf(stderr, "There are absolutely no guarantees: use only at your own risk.\n");
fprintf(stderr, "You may modify, copy, and create any derivatives freely.\n");
fprintf(stderr, "\n");
return status;
}
/* Automatically grown string buffer.
*/
char *buffer = NULL;
size_t size = 0;
size_t used = 0;
int oom = 0;
/* Add the X11 symbolic names of the keycodes to the buffer.
*/
void add_keysym(const KeySym symbol)
{
char *s;
size_t n;
if (oom || symbol == NoSymbol)
return;
/* Look up the symbolic string.
*/
s = XKeysymToString(symbol);
if (!s)
return;
n = strlen(s);
if (n < 1)
return;
/* Make sure buffer has enough space.
*/
if (used + n + 2 >= size) {
char *temp;
size = ((used + n) | 1023) + 1025;
temp = realloc(buffer, size);
if (!temp) {
oom = 1;
size = 0;
used = 0;
free(buffer);
return;
}
buffer = temp;
}
/* Add a space as a separator.
*/
if (used)
buffer[used++] = ' ';
/* Copy symbol, add EOS.
*/
memcpy(buffer + used, s, n);
used += n;
buffer[used] = 0;
return;
}
int main(int argc, char **argv)
{
Display *display;
int screen;
Window window, parent, root, oldfocus;
int x, y, rootx, rooty, arg, revert;
unsigned int buttons;
Colormap colormap;
XColor black, white;
unsigned long blackpixel, whitepixel;
Pixmap empty;
Cursor hidden;
XEvent event;
KeySym enterkey, returnkey, key;
Bool more;
double seconds = -1.0;
/* Usage or help requested?
*/
if (argc < 2)
return usage(argv[0], 0);
if (!strcmp("-h", argv[1]) || !strcmp("--help", argv[1]))
return usage(argv[0], 0);
if (argc < 3)
return usage(argv[0], 1);
/* Parse and set the timeout.
*/
{ char *ends = NULL;
if (argv[1])
seconds = strtod(argv[1], &ends);
if (seconds < 0.0 || !ends || *ends) {
fprintf(stderr, "%s: Invalid timeout.\n", argv[1]);
return 1;
}
}
if (set_timeout(seconds)) {
fprintf(stderr, "%s: Invalid timeout.\n", argv[1]);
return 1;
}
/* Open the X11 connection.
*/
display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "No X11 connection.\n");
return 1;
}
screen = DefaultScreen(display);
/* Initialize colormap and colors.
*/
colormap = DefaultColormap(display, screen);
black.pixel = blackpixel = BlackPixel(display, screen);
white.pixel = whitepixel = WhitePixel(display, screen);
XQueryColor(display, colormap, &black);
XQueryColor(display, colormap, &white);
/* Determine which window has the pointer; it'll be out parent.
*/
XQueryPointer(display, DefaultRootWindow(display), &root, &parent, &rootx, &rooty, &x, &y, &buttons);
/* Get the pointer coordinates relative to the parent window.
*/
XQueryPointer(display, parent, &root, &parent, &rootx, &rooty, &x, &y, &buttons);
/* Create an invisible window.
*/
window = XCreateSimpleWindow(display, parent, x, y, 1, 1, 0, blackpixel, blackpixel);
if (!window) {
XCloseDisplay(display);
fprintf(stderr, "Cannot create a window.\n");
return 1;
}
/* Create an invisible mouse cursor.
*/
empty = XCreatePixmap(display, window, 1, 1, 1);
if (!empty) {
XDestroyWindow(display, window);
XCloseDisplay(display);
fprintf(stderr, "Cannot create a one-bit pixmap.\n");
return 1;
}
hidden = XCreatePixmapCursor(display, empty, empty, &black, &white, 0, 0);
if (!hidden) {
XFreePixmap(display, empty);
XDestroyWindow(display, window);
XCloseDisplay(display);
fprintf(stderr, "Cannot create a pixmap cursor.\n");
return 1;
}
XDefineCursor(display, window, hidden);
/* Filter unneeded events.
*/
XSelectInput(display, window, KeyPressMask | ExposureMask | StructureNotifyMask);
/* Make the window "visible".
*/
XMapRaised(display, window);
/* Limit the pointer to the safe window, and grab the keyboard.
*/
XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(display, window, False, 0, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
/* Save old input focus, then grab that too.
*/
XGetInputFocus(display, &oldfocus, &revert);
XSetInputFocus(display, window, RevertToNone, CurrentTime);
/* For maximum compatibility, look up "Return" and "Enter" keys.
*/
returnkey = XStringToKeysym("Return");
enterkey = XStringToKeysym("Enter");
/* Main event loop.
*/
while (!timeout) {
/* Sleep instead of blocking.
*/
if (!XPending(display)) {
usleep(POLLING_INTERVAL_US);
continue;
}
XNextEvent(display, &event);
/* Handle expose events.
*/
if (event.type == Expose) {
do {
more = XCheckTypedEvent(display, Expose, &event);
} while (more && !timeout);
continue;
}
/* Ignore other than keypress events.
*/
if (event.type != KeyPress)
continue;
/* Map the keycode to a keysym, without any modifier keys (index 0).
*/
key = XKeycodeToKeysym(display, ((XKeyPressedEvent *)&event)->keycode, 0);
if (key == NoSymbol)
continue;
/* Return or Enter keypress?
*/
if (key == XK_Return || key == returnkey ||
key == XK_KP_Enter || key == enterkey ||
key == XK_ISO_Enter)
break;
/* Append to the keypress buffer.
*/
add_keysym(key);
}
/* Cancel the timeout.
*/
cancel_timeout();
/* Tear down the graphics stuff.
*/
XSetInputFocus(display, oldfocus, revert, CurrentTime);
XUngrabPointer(display, CurrentTime);
XUngrabKeyboard(display, CurrentTime);
XUndefineCursor(display, window);
XFreePixmap(display, empty);
XDestroyWindow(display, window);
XCloseDisplay(display);
/* Timeout exceeded? OOM (out of memory)?
*/
if (timeout || oom)
return 1;
/* Move all argument strings down two steps,
* add the new parameter, and a final NULL.
*/
for (arg = 2; arg < argc; arg++)
argv[arg - 2] = argv[arg];
argv[argc - 2] = buffer;
argv[argc - 1] = NULL;
/* Execute the command.
*/
execv_auto(argv);
/* Failed.
*/
return 1;
}
Make sure you have the basic X11 development libraries (libx11-dev) installed, then compile and install x11grab:
I'd appreciate it if you could test this. Any feedback (especially on any problems) is thoroughly welcome.
If this is useful enough, I might add a bit of a configurability (different terminator keys for example), and push it upstream.
Nominal Animal
Last edited by Nominal Animal; 03-21-2011 at 06:44 AM.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.