Compare commits

...

66 commits
v1.0 ... master

Author SHA1 Message Date
Albin Olsson a34d6bae27
Merge pull request #78 from PaulPorfiroff/allow-mapping-to-keycodes
Allow mapping to keycodes
2018-03-01 08:56:23 +01:00
Albin Olsson 3cc6e1eb8c
Merge pull request #98 from thebillywayne/master
Update README.md
2018-02-19 08:44:34 +01:00
Albin Olsson 5f9ea17074
Merge pull request #93 from thebinarypenguin/patch-1
change "conjugation" to "conjunction" in README.md
2018-02-19 08:43:58 +01:00
Billy Wayne McCann 791057b8af
Update README.md
Fixed a small spelling error ("relased" -> "released") in the README.
2018-02-17 00:32:25 -05:00
Ethan Zimmerman 97f79a3ec8 change "conjugation" to "conjunction" in README.md 2017-08-19 14:13:57 -04:00
Albin Olsson 6ded5b453b Merge pull request #89 from qu1x/add-foreground-flag
Add '-f' flag (implied by '-d' flag).
2017-08-07 13:19:39 +02:00
Albin Olsson 99dda44922 Merge pull request #91 from jbernard/master
Remove trailing whitespace from manpage
2017-08-07 13:10:19 +02:00
Jon Bernard d2a9bcb1cc Remove trailing whitespace from manpage 2017-07-07 07:20:52 -04:00
Rouven Spreckels d8dbbf8a25 Add '-f' flag (implied by '-d' flag). 2017-07-03 20:58:26 +02:00
Paul Porfiroff 6901647283 "Describe" changes in README. 2016-12-11 15:59:09 +03:00
Paul Porfiroff 48171b2443 Allow specifying keys to map to with keycodes. 2016-12-11 15:58:05 +03:00
Paul Porfiroff ac6e3723f5 Fix typo. 2016-12-11 14:59:31 +03:00
Albin Olsson 6331193ef0 Merge pull request #76 from orestisf1993/fix-50
Fix #50. Invalid '-t' argument
2016-11-19 12:17:36 +01:00
orestisf 853ca153fe Remove useless timeout_valid flag
It's always true.
2016-11-17 04:48:29 +02:00
orestisf ccf79e6118 Fix #50. Exit on invalid '-t' argument
Since the program has a default timeout anyway the '-t' flag is not needed.
2016-11-17 04:48:29 +02:00
Albin Olsson 5481a17e6b Merge pull request #68 from gunnihinn/master
Add installation instructions for Fedora systems
2016-11-15 18:42:45 +01:00
Albin Olsson aa3316795e Merge pull request #75 from ainola/patch-1
it's -> its grammar update
2016-11-15 18:20:51 +01:00
ainola 8d8e95e179 it's -> its grammar update
In this case it is grammatically correct to use its instead of it's.

Because this is so pedantic I want this to be more of a "thank you for this great utility, I use it every day" kind of PR.
2016-10-29 15:20:18 -06:00
Gunnar Þór Magnússon 958e3c7c8e Add installation instructions for Fedora systems
The original instructions were for derivatives of Debian. Adding
instructions for Fedora-based systems is just a matter of finding what
the corresponding devel packages are called there.
2016-05-25 16:38:07 -05:00
alols 7fca36453e Merge pull request #54 from mdom/patch-1
Shorten build instructions
2015-09-07 15:30:49 +02:00
Mario Domgoergen 7657ed7a65 Shorten build instructions
Just a tiny change: git clone automatically creates the xcape directory, so no need to create it. And thanks for the software, it's amazing!
2015-08-29 20:01:23 +02:00
alols c052d248b4 Update README.md
#53
2015-08-19 08:36:18 +02:00
Albin Olsson f69c5a3f2b Fix #51. Clicking the mouse should prevent key press generation. 2015-07-19 10:37:44 +02:00
Albin Olsson f3802fc086 Better error messages, fix #25 2015-03-01 08:57:41 +01:00
Albin Olsson 6d74db30c2 Call XInitThreads and XLock/UnlockDisplay, should fix occasional shutdown crashes. 2015-02-28 09:28:47 +01:00
Albin Olsson 9bb6416fa3 Bump copyright year 2015-02-27 19:11:15 +01:00
Albin Olsson a55e3e90cd Clean up on exit. Now runs cleanly with valgrind. 2015-02-27 19:10:51 +01:00
Albin Olsson 84e976c167 Make code follow C89 standard 2015-02-27 19:08:38 +01:00
alols 88f933ac5a Merge pull request #42 from phy1729/makefile-portability
Make Makefile more portable
2015-01-20 09:17:40 +01:00
Matthew Martin 59db9c6846 Make Makefile more portable
- Use -d instead of -D
- Add all target
- Mark install target as phony
2015-01-19 17:32:20 -06:00
alols c01ef4f2cf Merge pull request #36 from mar04/patch-1
Fix typo in man page.
2014-05-30 08:45:49 +02:00
Mariusz Libera d4ae3d74f6 Fix typo in man page. 2014-05-20 18:47:08 +02:00
Albin Olsson 8dc7b033bf Fix typo in README 2014-03-25 18:35:09 +01:00
alols 664a292d41 Merge pull request #30 from johnhill/manpage
Add manpage and update Makefile
2014-02-15 13:25:50 +01:00
John Hill 28fa283290 Add manpage and update Makefile 2014-02-13 19:03:06 +00:00
Albin Olsson 813713a0a9 Update README to avoid confusion on xcape's capabilities. 2014-01-22 23:32:00 +01:00
alols c8b4ec9b19 Merge pull request #27 from atykhonov/master
Fixed bad example
2014-01-18 04:36:02 -08:00
Andrey Tykhonov 3410230008 Used existed variable 2014-01-17 11:46:53 +02:00
Andrey Tykhonov f1c9869083 Fixed bad example 2014-01-17 01:53:19 +02:00
Albin Olsson eeb66320bb Add missing dependency to build instructions 2013-12-14 10:36:01 +01:00
Albin Olsson 5555d661ce Fix error in documentation. Issue #24 2013-12-05 22:09:11 +01:00
alols d5b4bbdee0 Merge pull request #23 from davidshepherd7/master
Improve instructions for emulating space2ctrl in readme
2013-11-25 23:52:32 -08:00
David Shepherd a58beb5569 Improve instructions for emulating space2ctrl in readme
In my old approach I bound space to a brand new keysym. This works well for
most programs but not for emacs! The problem is that emacs tries to warn
you when you press unbound keys, which causes slowdowns and odd behaviour.

The solution is to instead bind space to some unused modifier key that will
be recognised and ignored by emacs. I've found that "Hyper_L" works nicely.
2013-11-09 22:51:28 +00:00
alols 82014c8436 Merge pull request #22 from davidshepherd7/master
Additions to readme and debugging output during keymap creation
2013-09-26 11:42:40 -07:00
David Shepherd 51840b931e Add example: make space generate ctrl if held 2013-09-25 23:17:13 +01:00
David Shepherd efb3292a58 Revert "Change to using KeySyms rather than keycodes to store to-keys"
This reverts commit b1f0a0601b.
2013-09-25 23:00:51 +01:00
David Shepherd 08992b45d6 Modify readme to explain how to generate button presses with shift 2013-09-25 22:57:13 +01:00
David Shepherd 7ec9ffd5fa Convert numbered lists in readme to proper markdown lists 2013-09-25 22:06:05 +01:00
David Shepherd b1f0a0601b Change to using KeySyms rather than keycodes to store to-keys
Modify all code to store and manipulate KeySyms instead of KeyCodes for
output key data.

Incomplete in two places (both also noted by comments in the code):

* When creating the additional key press events I have not yet implemented
  the switch to using `XSendEvent` rather than `XTestFakeKeyEvent`. Instead
  I just convert back to a keycode assuming zero shift level and
  group. This gives the same behaviour as before.

* When checking that an event is not one that we generated I haven't managed
  to figure out how to get the keysym of the event from the data provided
  by X. Instead I compare the key codes for now, which will work until the
  above issue is fixed.
2013-09-25 00:36:30 +01:00
David Shepherd 9c460f46c7 Add debugging output in key map creation
Requires the addition of a debug flag to `parse_mapping` and `parse_token`.

For some reason there is no declaration of the function `parse_token`. I'm
not sure why so I won't add it.
2013-09-25 00:26:03 +01:00
joetw 39aa08c5da Merge pull request #19 from joetw/keycode_in_mapexpression
Closes #17: Allowing keycodes in map expressions
2013-05-14 04:13:59 -07:00
joetw 58d973848a Readme: minor cleanup 2013-05-14 13:00:56 +02:00
joetw 65197d93d2 Readme cleared up a little 2013-05-12 18:16:14 +02:00
joetw 478e6077c5 '#'-prefix syntax 2013-05-11 21:42:46 +02:00
joetw 19740b571f A bit more robust. Readme. 2013-05-10 22:49:55 +02:00
joetw 6829bf3d30 minor cleanup 2013-05-10 18:49:00 +02:00
joetw 9b6733cf18 [#17]: Allowing keycodes in map expressions. 2013-05-09 23:18:49 +02:00
alols 6644670090 Merge pull request #14 from rkj/master
Please pull small bugfix/checking of initial conditions
2013-02-03 02:47:46 -08:00
Roman Kamyk 82debc8d67 Fixed broken escape char 2013-01-31 17:32:05 -08:00
Roman Kamyk a036bc5c9f Added gitignore 2013-01-31 17:31:54 -08:00
Roman Kamyk 4932b18a67 Introduced check if XOpenDisplay is successful. It fails when DISPLAY is not set in ENV,
and caused segfault on later invocations that used self->data_conn or self->ctrl_conn.
2013-01-31 17:00:57 -08:00
Albin Olsson b8b353c43b Fix debug message 2012-12-14 18:30:23 +01:00
alols 2fa52dca29 Merge pull request #13 from otommod/master
Changed to use XkbKeycodeToKeysym
2012-12-14 09:20:45 -08:00
Otto Modinos c0621e486c changed to use XkbKeycodeToKeysym
It looks like XKeycodeToKeysym is depreceated, so use the Xkb one instead
2012-12-14 13:21:50 +02:00
alols b1b44f8d47 Merge pull request #12 from DexterTheDragon/master
Add pkg-config to build instructions in readme
2012-11-15 00:27:41 -08:00
Kevin Carter 41fd66988f Add pkg-config to list of dependencies to install 2012-11-13 12:05:55 -07:00
5 changed files with 374 additions and 80 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
xcape

View file

@ -1,5 +1,6 @@
INSTALL=install
PREFIX=/usr
MANDIR?=/local/man/man1
TARGET := xcape
@ -8,13 +9,18 @@ CFLAGS += `pkg-config --cflags xtst x11`
LDFLAGS += `pkg-config --libs xtst x11`
LDFLAGS += -pthread
all: $(TARGET)
$(TARGET): xcape.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
install:
$(INSTALL) -Dm 755 $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(TARGET)
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)$(MANDIR)
$(INSTALL) -m 0755 $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(TARGET)
$(INSTALL) -m 0644 xcape.1 $(DESTDIR)$(PREFIX)$(MANDIR)/xcape.1
clean:
rm $(TARGET)
.PHONY: clean
.PHONY: all clean install

View file

@ -12,39 +12,89 @@ editor ;)
Minimal building instructions
-----------------------------
$ sudo apt-get install git gcc make libx11-dev libxtst-dev
$ mkdir xcape
First install the development dependencies. On Debian-based systems
(including Ubuntu and Linux Mint), run:
$ sudo apt-get install git gcc make pkg-config libx11-dev libxtst-dev libxi-dev
On Fedora-based systems, run:
$ sudo dnf install git gcc make pkgconfig libX11-devel libXtst-devel libXi-devel
Then run:
$ git clone https://github.com/alols/xcape.git
$ cd xcape
$ git clone https://github.com/alols/xcape.git .
$ make
$ sudo make install
Usage
-----
$ xcape [-d] [-t <timeout ms>] [-e <map-expression>]
$ xcape [-d] [-f] [-t <timeout ms>] [-e <map-expression>]
### `-d`
Debug mode. Does not fork into the background.
Debug mode. Does not fork into the background. Prints debug information.
### `-f`
Foreground mode. Does not fork into the background.
### `-t <timeout ms>`
If you hold a key longer than this timeout, xcape will not generate a key
event. Default is 50 ms.
event. Default is 500 ms.
### `-e <map-expression>`
The expression has the grammar `'ModKey=Key[|OtherKey][;NextExpression]'`
The list of key names is found in the header file `X11/keysymdef.h`
(remove the `XK_` prefix).
The list of key names is found in the header file `X11/keysymdef.h` (remove
the `XK_` prefix). Note that due to limitations of X11 shifted keys *must*
be specified as a shift key followed by the key to be pressed rather than
the actual name of the character. For example to generate "{" the
expression `'ModKey=Shift_L|bracketleft'` could be used (assuming that you
have a key with "{" above "[").
#### Example
You can also specify keys in decimal (prefix `#`), octal (`#0`), or
hexadecimal (`#0x`). They will be interpreted as keycodes unless no corresponding
key name is found.
xcape -e 'Shift_L=Escape;Control_L=Control_L|O'
#### Examples
+ This will make Left Shift generate Escape when pressed and released on
its own, and Left Control generate Ctrl-O combination when pressed and
released on its own.
xcape -e 'Shift_L=Escape;Control_L=Control_L|O'
+ In conjunction with xmodmap it is possible to make an ordinary key act
as an extra modifier. First map the key to the modifier with xmodmap
and then the modifier back to the key with xcape. However, this has
several limitations: the key will not work as ordinary until it is
released, and in particular, *it may act as a modifier unintentionally if
you type too fast.* This is not a bug in xcape, but an unavoidable
consequence of using these two tools together in this way.
As an example, we can make the space bar work as an additional ctrl
key when held (similar to
[Space2ctrl](https://github.com/r0adrunner/Space2Ctrl)) with the
following sequence of commands.
# Map an unused modifier's keysym to the spacebar's keycode and make it a
# control modifier. It needs to be an existing key so that emacs won't
# spazz out when you press it. Hyper_L is a good candidate.
spare_modifier="Hyper_L"
xmodmap -e "keycode 65 = $spare_modifier"
xmodmap -e "remove mod4 = $spare_modifier" # hyper_l is mod4 by default
xmodmap -e "add Control = $spare_modifier"
# Map space to an unused keycode (to keep it around for xcape to
# use).
xmodmap -e "keycode any = space"
# Finally use xcape to cause the space bar to generate a space when tapped.
xcape -e "$spare_modifier=space"
This will make Left Shift generate Escape when pressed and released on
it's own, and Left Control generate Ctrl-O combination when pressed and
released on it's own.
Note regarding xmodmap
----------------------
@ -52,11 +102,11 @@ Note regarding xmodmap
If you are in the habit of remapping keycodes to keysyms (eg, using xmodmap),
there are two issues you may encounter.
1) You will need to restart xcape after every time you modify the mapping from
1. You will need to restart xcape after every time you modify the mapping from
keycodes to keysyms (eg, with xmodmap), or xcape will still use the old
mapping.
2) The key you wish to send must have a defined keycode. So for example, with
2. The key you wish to send must have a defined keycode. So for example, with
the default mapping `Control_L=Escape`, you still need an escape key defined
in your xmodmap mapping. (I get around this by using 255, which my keyboard
cannot send).

96
xcape.1 Normal file
View file

@ -0,0 +1,96 @@
.TH XCAPE 1 2017-07-03 "John Hill" "xcape Manual"
.SH NAME
xcape \- use a modifier key as another key
.SH SYNOPSIS
.B xcape
[\fB-d\fR]
[\fB-f\fR]
[\fB-t\fR \fItimeout\fR]
[\fB-e\fR \fImap-expression\fR]
.SH DESCRIPTION
\fBxcape\fR allows a modifier key to be used as another key when it is pressed
and released on its own. The default behaviour is to generate the \fIEscape\fR
key in place of \fIControl_L\fR (Left Control).
.SH OPTIONS
.TP
.BR \-d
Debug mode. Will run as a foreground process and print debug information.
.TP
.BR \-f
Foreground mode. Will run as a foreground process.
.TP
.BR \-t " " \fItimeout\fR
Give a \fItimeout\fR in milliseconds. If you hold a key longer than
\fItimeout\fR a key event will not be generated.
.TP
.BR \-e " " \fImap-expression\fR
Use \fImap-expression\fR as the expression(s).
.SH EXPRESSION SYNTAX
Expression syntax is \'\fBModKey\fR=\fBKey\fR[|\fBOtherKey\fR]\'. Multiple
expressions can be passed, delimited by semi-colons (;).
.PP
A list of keysyms can be found in the header file <\fIX11/keysymdef.h\fR>
(without the \fIXK_\fR prefix).
.PP
Note that shifted keys must be specified as a shift key followed by the key to
be pressed rather than the actual name of the character. For example to
generate "\fI{\fR" the expression
\'\fIModKey\fR=\fIShift_L\fR|\fIbracketleft\fR\' could be used
(assuming that you have a key with \'{\' above \'[\').
.PP
You can also specify \fBModKey\fR in decimal (prefix \fI#\fR), octal
(\fI#0\fR), or hexadecimal (\fI#0x\fR). It will be interpreted as a keycode
unless no corresponding key name is
found.
.SH EXAMPLES
.PP
Make Left Shift generate Escape when pressed and released on it's own, and Left
Control generate Ctrl\-O combination when pressed and released on it's own:
.RS
\fBxcape\fR \fB-e\fR '\fIShift_L\fR=\fIEscape\fR;\fIControl_L\fR=\fIControl_L\fR|\fIO\fR'
.RE
.PP
In conjugation with xmodmap it is possible to make an ordinary key act as an
extra modifier. First map the key to the modifier with xmodmap and then the
modifier back to the key with \fBxcape\fR. As an example, we can make the space
bar work as an additional ctrl key when held with the following sequence of
commands:
.PP
First, map an unused modifier's keysym to the spacebar's keycode and make it a
control modifier. It needs to be an existing key so that emacs won't spazz out
when you press it. Hyper_L is a good candidate.
.PP
.RS
.nf
\fBspare_modifier\fR="\fIHyper_L\fR"
\fBxmodmap\fR \fB-e\fR "\fBkeycode\fR \fI65\fR = \fI$spare_modifier\fR"
\fBxmodmap\fR \fB-e\fR "\fBremove\fR \fImod4\fR = \fI$spare_modifier\fR"
# hyper_l is mod4 by default
\fBxmodmap\fR \fB-e\fR "\fBadd\fR \fIControl\fR = \fI$spare_modifier\fR"
.fi
.RE
.PP
Next, map space to an unused keycode (to keep it around for \fBxcape\fR to use).
.PP
.RS
\fBxmodmap\fR \fR-e\fR "\fBkeycode\fR \fIany\fR = \fIspace\fR"
.RE
.PP
Finally use \fBxcape\fR to cause the space bar to generate a space when tapped.
.PP
.RS
\fBxcape\fR \fB-e\fR "\fI$spare_modifier\fR=\fIspace\fR"
.RE
.SH SEE ALSO
\fBxmodmap\fR(1), \fBxev\fR(1)
.SH AUTHOR
\fBxcape\fR was written by Albin Olsson
(albin dot olsson at gmail dot com)

265
xcape.c
View file

@ -1,7 +1,7 @@
/************************************************************************
* xcape.c
*
* Copyright 2012 Albin Olsson
* Copyright 2015 Albin Olsson
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -25,6 +25,7 @@
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/extensions/record.h>
@ -43,7 +44,9 @@ typedef struct _Key_t
typedef struct _KeyMap_t
{
KeySym from;
Bool UseKeyCode; /* (for from) instead of KeySym; ignore latter */
KeySym from_ks;
KeyCode from_kc;
Key_t *to_keys;
Bool used;
Bool pressed;
@ -59,11 +62,11 @@ typedef struct _XCape_t
XRecordContext record_ctx;
pthread_t sigwait_thread;
sigset_t sigset;
Bool foreground;
Bool debug;
KeyMap_t *map;
Key_t *generated;
struct timeval timeout;
Bool timeout_valid;
} XCape_t;
/************************************************************************
@ -73,31 +76,49 @@ void *sig_handler (void *user_data);
void intercept (XPointer user_data, XRecordInterceptData *data);
KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping);
KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping, Bool debug);
void delete_mapping (KeyMap_t *map);
Key_t *key_add_key (Key_t *keys, KeyCode key);
void delete_keys (Key_t *keys);
void print_usage (const char *program_name);
/************************************************************************
* Main function
***********************************************************************/
int main (int argc, char **argv)
{
XCape_t *self = malloc (sizeof (XCape_t));
int dummy, ch;
static char default_mapping[] = "Control_L=Escape";
char *mapping = default_mapping;
XRecordRange *rec_range = XRecordAllocRange();
XRecordClientSpec client_spec = XRecordAllClients;
self->foreground = False;
self->debug = False;
self->timeout.tv_sec = 0;
self->timeout.tv_usec = 500000;
self->timeout_valid = True;
self->generated = NULL;
while ((ch = getopt (argc, argv, "de:t:")) != -1)
rec_range->device_events.first = KeyPress;
rec_range->device_events.last = ButtonRelease;
while ((ch = getopt (argc, argv, "dfe:t:")) != -1)
{
switch (ch)
{
case 'd':
self->debug = True;
/* imply -f (no break) */
case 'f':
self->foreground = True;
break;
case 'e':
mapping = optarg;
@ -107,27 +128,44 @@ int main (int argc, char **argv)
int ms = atoi (optarg);
if (ms > 0)
{
self->timeout_valid = True;
self->timeout.tv_sec = ms / 1000;
self->timeout.tv_usec = (ms % 1000) * 1000;
}
else
{
self->timeout_valid = False;
fprintf (stderr, "Invalid argument for '-t': %s.\n", optarg);
print_usage (argv[0]);
return EXIT_FAILURE;
}
}
break;
default:
fprintf (stdout, "Usage: %s [-d] [-t timeout_ms] [-e <mapping>]\n", argv[0]);
fprintf (stdout,
"Runs as a daemon unless -d flag is set\n");
print_usage (argv[0]);
return EXIT_SUCCESS;
}
}
if (optind < argc)
{
fprintf (stderr, "Not a command line option: '%s'\n", argv[optind]);
print_usage (argv[0]);
return EXIT_SUCCESS;
}
if (!XInitThreads ())
{
fprintf (stderr, "Failed to initialize threads.\n");
exit (EXIT_FAILURE);
}
self->data_conn = XOpenDisplay (NULL);
self->ctrl_conn = XOpenDisplay (NULL);
if (!self->data_conn || !self->ctrl_conn)
{
fprintf (stderr, "Unable to connect to X11 display. Is $DISPLAY set?\n");
exit (EXIT_FAILURE);
}
if (!XQueryExtension (self->ctrl_conn,
"XTEST", &dummy, &dummy, &dummy))
{
@ -139,19 +177,22 @@ int main (int argc, char **argv)
fprintf (stderr, "Failed to obtain xrecord version\n");
exit (EXIT_FAILURE);
}
if (!XkbQueryExtension (self->ctrl_conn, &dummy, &dummy,
if (!XkbQueryExtension (self->ctrl_conn, &dummy, &dummy,
&dummy, &dummy, &dummy))
{
fprintf (stderr, "Failed to obtain xkb version\n");
exit (EXIT_FAILURE);
}
self->map = parse_mapping (self->ctrl_conn, mapping);
self->map = parse_mapping (self->ctrl_conn, mapping, self->debug);
if (self->map == NULL)
{
fprintf (stderr, "Failed to parse_mapping\n");
exit (EXIT_FAILURE);
}
if (self->debug != True)
if (self->foreground != True)
daemon (0, 0);
sigemptyset (&self->sigset);
@ -162,11 +203,6 @@ int main (int argc, char **argv)
pthread_create (&self->sigwait_thread,
NULL, sig_handler, self);
XRecordRange *rec_range = XRecordAllocRange();
rec_range->device_events.first = KeyPress;
rec_range->device_events.last = ButtonRelease;
XRecordClientSpec client_spec = XRecordAllClients;
self->record_ctx = XRecordCreateContext (self->ctrl_conn,
0, &client_spec, 1, &rec_range, 1);
@ -185,15 +221,23 @@ int main (int argc, char **argv)
exit (EXIT_FAILURE);
}
pthread_join (self->sigwait_thread, NULL);
if (!XRecordFreeContext (self->ctrl_conn, self->record_ctx))
{
fprintf (stderr, "Failed to free xrecord context\n");
}
if (self->debug) fprintf (stdout, "main exiting\n");
XFree (rec_range);
XCloseDisplay (self->ctrl_conn);
XCloseDisplay (self->data_conn);
if (self->debug) fprintf (stdout, "main exiting\n");
delete_mapping (self->map);
free (self);
return EXIT_SUCCESS;
}
@ -213,6 +257,8 @@ void *sig_handler (void *user_data)
if (self->debug) fprintf (stdout, "Caught signal %d!\n", sig);
XLockDisplay (self->ctrl_conn);
if (!XRecordDisableContext (self->ctrl_conn,
self->record_ctx))
{
@ -222,6 +268,8 @@ void *sig_handler (void *user_data)
XSync (self->ctrl_conn, False);
XUnlockDisplay (self->ctrl_conn);
if (self->debug) fprintf (stdout, "sig_handler exiting...\n");
return NULL;
@ -259,8 +307,7 @@ void handle_key (XCape_t *self, KeyMap_t *key,
key->pressed = True;
if (self->timeout_valid)
gettimeofday (&key->down_at, NULL);
gettimeofday (&key->down_at, NULL);
if (mouse_pressed)
{
@ -273,19 +320,16 @@ void handle_key (XCape_t *self, KeyMap_t *key,
if (key->used == False)
{
struct timeval timev = self->timeout;
if (self->timeout_valid)
{
gettimeofday (&timev, NULL);
timersub (&timev, &key->down_at, &timev);
}
gettimeofday (&timev, NULL);
timersub (&timev, &key->down_at, &timev);
if (!self->timeout_valid || timercmp (&timev, &self->timeout, <))
if (timercmp (&timev, &self->timeout, <))
{
for (k = key->to_keys; k != NULL; k = k->next)
{
if (self->debug) fprintf (stdout, "Generating %s!\n",
XKeysymToString (XKeycodeToKeysym (self->ctrl_conn,
k->key, 0)));
XKeysymToString (XkbKeycodeToKeysym (self->ctrl_conn,
k->key, 0, 0)));
XTestFakeKeyEvent (self->ctrl_conn,
k->key, True, 0);
@ -311,6 +355,8 @@ void intercept (XPointer user_data, XRecordInterceptData *data)
static Bool mouse_pressed = False;
KeyMap_t *km;
XLockDisplay (self->ctrl_conn);
if (data->category == XRecordFromServer)
{
int key_event = data->data[0];
@ -349,33 +395,36 @@ void intercept (XPointer user_data, XRecordInterceptData *data)
{
mouse_pressed = False;
}
else
for (km = self->map; km != NULL; km = km->next)
{
for (km = self->map; km != NULL; km = km->next)
if ((km->UseKeyCode == False
&& XkbKeycodeToKeysym (self->ctrl_conn, key_code, 0, 0)
== km->from_ks)
|| (km->UseKeyCode == True
&& key_code == km->from_kc))
{
if (XkbKeycodeToKeysym (self->ctrl_conn, key_code, 0, 0)
== km->from)
{
handle_key (self, km, mouse_pressed, key_event);
}
else if (km->pressed && key_event == KeyPress)
{
km->used = True;
}
handle_key (self, km, mouse_pressed, key_event);
}
else if (km->pressed
&& (key_event == KeyPress || key_event == ButtonPress))
{
km->used = True;
}
}
}
exit:
XUnlockDisplay (self->ctrl_conn);
XRecordFreeData (data);
}
KeyMap_t *parse_token (Display *dpy, char *token)
KeyMap_t *parse_token (Display *dpy, char *token, Bool debug)
{
KeyMap_t *km = NULL;
KeySym ks;
char *from, *to, *key;
KeyCode code;
KeyCode code; /* keycode */
long parsed_code; /* parsed keycode value */
to = token;
from = strsep (&to, "=");
@ -383,14 +432,54 @@ KeyMap_t *parse_token (Display *dpy, char *token)
{
km = calloc (1, sizeof (KeyMap_t));
if ((ks = XStringToKeysym (from)) == NoSymbol)
if (!strncmp (from, "#", 1)
&& strsep (&from, "#") != NULL)
{
fprintf (stderr, "Invalid key: %s\n", token);
return NULL;
errno = 0;
parsed_code = strtoul (from, NULL, 0); /* dec, oct, hex automatically */
if (errno == 0
&& parsed_code <=255
&& XkbKeycodeToKeysym (dpy, (KeyCode) parsed_code, 0, 0) != NoSymbol)
{
km->UseKeyCode = True;
km->from_kc = (KeyCode) parsed_code;
if (debug)
{
KeySym ks_temp = XkbKeycodeToKeysym (dpy, (KeyCode) parsed_code, 0, 0);
fprintf(stderr, "Assigned mapping from \"%s\" ( keysym 0x%x, "
"key code %d)\n",
XKeysymToString(ks_temp),
(unsigned) ks_temp,
(unsigned) km->from_kc);
}
}
else
{
fprintf (stderr, "Invalid keycode: %s\n", from);
return NULL;
}
}
else
{
if ((ks = XStringToKeysym (from)) == NoSymbol)
{
fprintf (stderr, "Invalid key: %s\n", token);
return NULL;
}
km->from = ks;
km->to_keys = NULL;
km->UseKeyCode = False;
km->from_ks = ks;
km->to_keys = NULL;
if (debug)
{
fprintf(stderr, "Assigned mapping from \"%s\" ( keysym 0x%x, "
"key code %d)\n",
XKeysymToString (km->from_ks),
(unsigned) km->from_ks,
(unsigned) XKeysymToKeycode (dpy, km->from_ks));
}
}
for(;;)
{
@ -398,31 +487,58 @@ KeyMap_t *parse_token (Display *dpy, char *token)
if (key == NULL)
break;
if ((ks = XStringToKeysym (key)) == NoSymbol)
if (!strncmp (key, "#", 1)
&& strsep (&key, "#") != NULL)
{
fprintf (stderr, "Invalid key: %s\n", key);
return NULL;
errno = 0;
parsed_code = strtoul (key, NULL, 0); /* dec, oct, hex automatically */
if (!(errno == 0
&& parsed_code <=255
&& XkbKeycodeToKeysym (dpy, (KeyCode) parsed_code, 0, 0) != NoSymbol))
{
fprintf (stderr, "Invalid keycode: %s\n", key);
return NULL;
}
code = (KeyCode) parsed_code;
}
else
{
if ((ks = XStringToKeysym (key)) == NoSymbol)
{
fprintf (stderr, "Invalid key: %s\n", key);
return NULL;
}
code = XKeysymToKeycode (dpy, ks);
if (code == 0)
{
fprintf (stderr, "WARNING: No keycode found for keysym "
"%s (0x%x) in mapping %s. Ignoring this "
"mapping.\n", key, (unsigned int)ks, token);
return NULL;
}
}
code = XKeysymToKeycode (dpy, ks);
if (code == 0)
{
fprintf (stderr, "WARNING: No keycode found for keysym "
"%s (0x%x) in mapping %s. Ignoring this "
"mapping.\n", key, (unsigned int)ks, token);
return NULL;
}
km->to_keys = key_add_key (km->to_keys, code);
if (debug)
{
KeySym ks_temp = XkbKeycodeToKeysym (dpy, code, 0, 0);
fprintf(stderr, "to \"%s\" (keysym 0x%x, key code %d)\n",
XKeysymToString(ks_temp),
(unsigned) ks_temp,
(unsigned) code);
}
}
}
else
fprintf (stderr, "WARNING: Mapping without = has no effect: '%s'\n", token);
return km;
}
KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping)
KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping, Bool debug)
{
char *token;
KeyMap_t *rval, *km, *nkm;
@ -435,7 +551,7 @@ KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping)
if (token == NULL)
break;
nkm = parse_token (ctrl_conn, token);
nkm = parse_token (ctrl_conn, token, debug);
if (nkm != NULL)
{
@ -451,3 +567,28 @@ KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping)
return rval;
}
void delete_mapping (KeyMap_t *map)
{
while (map != NULL) {
KeyMap_t *next = map->next;
delete_keys (map->to_keys);
free (map);
map = next;
}
}
void delete_keys (Key_t *keys)
{
while (keys != NULL) {
Key_t *next = keys->next;
free (keys);
keys = next;
}
}
void print_usage (const char *program_name)
{
fprintf (stdout, "Usage: %s [-d] [-f] [-t timeout_ms] [-e <mapping>]\n", program_name);
fprintf (stdout, "Runs as a daemon unless -d or -f flag is set\n");
}