Compare commits

..

46 Commits

Author SHA1 Message Date
Jacopo De Simoi 998cc7ba6c Be more lenient with compose+shift 6 years ago
Albin Olsson a34d6bae27
Merge pull request #78 from PaulPorfiroff/allow-mapping-to-keycodes 8 years ago
Albin Olsson 3cc6e1eb8c
Merge pull request #98 from thebillywayne/master 8 years ago
Albin Olsson 5f9ea17074
Merge pull request #93 from thebinarypenguin/patch-1 8 years ago
Billy Wayne McCann 791057b8af
Update README.md 8 years ago
Ethan Zimmerman 97f79a3ec8 change "conjugation" to "conjunction" in README.md 9 years ago
Albin Olsson 6ded5b453b Merge pull request #89 from qu1x/add-foreground-flag 9 years ago
Albin Olsson 99dda44922 Merge pull request #91 from jbernard/master 9 years ago
Jon Bernard d2a9bcb1cc Remove trailing whitespace from manpage 9 years ago
Rouven Spreckels d8dbbf8a25 Add '-f' flag (implied by '-d' flag). 9 years ago
Paul Porfiroff 6901647283 "Describe" changes in README. 9 years ago
Paul Porfiroff 48171b2443 Allow specifying keys to map to with keycodes. 9 years ago
Paul Porfiroff ac6e3723f5 Fix typo. 9 years ago
Albin Olsson 6331193ef0 Merge pull request #76 from orestisf1993/fix-50 9 years ago
orestisf 853ca153fe Remove useless timeout_valid flag 9 years ago
orestisf ccf79e6118 Fix #50. Exit on invalid '-t' argument 9 years ago
Albin Olsson 5481a17e6b Merge pull request #68 from gunnihinn/master 9 years ago
Albin Olsson aa3316795e Merge pull request #75 from ainola/patch-1 9 years ago
ainola 8d8e95e179 it's -> its grammar update 10 years ago
Gunnar Þór Magnússon 958e3c7c8e Add installation instructions for Fedora systems 10 years ago
alols 7fca36453e Merge pull request #54 from mdom/patch-1 11 years ago
Mario Domgoergen 7657ed7a65 Shorten build instructions 11 years ago
alols c052d248b4 Update README.md 11 years ago
Albin Olsson f69c5a3f2b Fix #51. Clicking the mouse should prevent key press generation. 11 years ago
Albin Olsson f3802fc086 Better error messages, fix #25 11 years ago
Albin Olsson 6d74db30c2 Call XInitThreads and XLock/UnlockDisplay, should fix occasional shutdown crashes. 11 years ago
Albin Olsson 9bb6416fa3 Bump copyright year 11 years ago
Albin Olsson a55e3e90cd Clean up on exit. Now runs cleanly with valgrind. 11 years ago
Albin Olsson 84e976c167 Make code follow C89 standard 11 years ago
alols 88f933ac5a Merge pull request #42 from phy1729/makefile-portability 11 years ago
Matthew Martin 59db9c6846 Make Makefile more portable 11 years ago
alols c01ef4f2cf Merge pull request #36 from mar04/patch-1 12 years ago
Mariusz Libera d4ae3d74f6 Fix typo in man page. 12 years ago
Albin Olsson 8dc7b033bf Fix typo in README 12 years ago
alols 664a292d41 Merge pull request #30 from johnhill/manpage 12 years ago
John Hill 28fa283290 Add manpage and update Makefile 12 years ago
Albin Olsson 813713a0a9 Update README to avoid confusion on xcape's capabilities. 12 years ago
alols c8b4ec9b19 Merge pull request #27 from atykhonov/master 12 years ago
Andrey Tykhonov 3410230008 Used existed variable 12 years ago
Andrey Tykhonov f1c9869083 Fixed bad example 12 years ago
Albin Olsson eeb66320bb Add missing dependency to build instructions 12 years ago
Albin Olsson 5555d661ce Fix error in documentation. Issue #24 12 years ago
alols d5b4bbdee0 Merge pull request #23 from davidshepherd7/master 12 years ago
David Shepherd a58beb5569 Improve instructions for emulating space2ctrl in readme 13 years ago
alols 82014c8436 Merge pull request #22 from davidshepherd7/master 13 years ago
David Shepherd 51840b931e Add example: make space generate ctrl if held 13 years ago
  1. 10
      Makefile
  2. 69
      README.md
  3. 96
      xcape.1
  4. 187
      xcape.c

@ -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

@ -12,24 +12,38 @@ editor ;)
Minimal building instructions
-----------------------------
$ sudo apt-get install git gcc make libx11-dev libxtst-dev pkg-config
$ 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>`
@ -42,25 +56,44 @@ 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 "[").
You can also specify ModKey in decimal (prefix `#`), octal (`#0`), or
hexadecimal (`#0x`). It will be interpreted as a keycode unless no corresponding
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.
#### Examples
1. 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.
+ 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'
2. If your `s` key has the code `42` and your `l` key `43` and you have set both
to `AltGr` (a.k.a. ISO_Level3_Shift) with xmodmap, then this will generate the
ordinary letters when pressed and released on their own. But pressed together
with another key, the `s` or `l` key will produce `AltGr`. So, depending on your
keyboard layout, you can compose e.g. `@`, `{` or `³` easily when touch-typing.
xcape -e '#42=s;#43=l'
+ 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"
Note regarding xmodmap

@ -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)

@ -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
@ -44,7 +44,7 @@ typedef struct _Key_t
typedef struct _KeyMap_t
{
Bool UseKeyCode; // (for from) instead of KeySym; ignore latter
Bool UseKeyCode; /* (for from) instead of KeySym; ignore latter */
KeySym from_ks;
KeyCode from_kc;
Key_t *to_keys;
@ -62,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;
/************************************************************************
@ -78,29 +78,47 @@ void intercept (XPointer user_data, XRecordInterceptData *data);
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;
@ -110,24 +128,36 @@ 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);
@ -157,9 +187,12 @@ int main (int argc, char **argv)
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);
@ -170,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);
@ -193,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;
}
@ -221,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))
{
@ -230,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;
@ -267,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)
{
@ -281,13 +320,10 @@ 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)
{
@ -319,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];
@ -357,9 +395,13 @@ 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 ((km->UseKeyCode == False
&& XkbKeycodeToKeysym (self->ctrl_conn, key_code, 0, 0)
@ -369,7 +411,7 @@ void intercept (XPointer user_data, XRecordInterceptData *data)
{
handle_key (self, km, mouse_pressed, key_event);
}
else if (km->pressed && key_event == KeyPress)
else if (km->pressed && (key_event == KeyPress || key_event == ButtonPress))
{
/* We should check if the pressed key is a modifier before marking the key as used. */
if (key_code != 50) /* hack; try if it works */
@ -382,6 +424,7 @@ void intercept (XPointer user_data, XRecordInterceptData *data)
}
exit:
XUnlockDisplay (self->ctrl_conn);
XRecordFreeData (data);
}
@ -391,8 +434,8 @@ KeyMap_t *parse_token (Display *dpy, char *token, Bool debug)
KeyMap_t *km = NULL;
KeySym ks;
char *from, *to, *key;
KeyCode code; // keycode (to)
long fromcode; // keycode (from)
KeyCode code; /* keycode */
long parsed_code; /* parsed keycode value */
to = token;
from = strsep (&to, "=");
@ -404,17 +447,17 @@ KeyMap_t *parse_token (Display *dpy, char *token, Bool debug)
&& strsep (&from, "#") != NULL)
{
errno = 0;
fromcode = strtoul (from, NULL, 0); // dec, oct, hex automatically
parsed_code = strtoul (from, NULL, 0); /* dec, oct, hex automatically */
if (errno == 0
&& fromcode <=255
&& XkbKeycodeToKeysym (dpy, (KeyCode) fromcode, 0, 0) != NoSymbol)
&& parsed_code <=255
&& XkbKeycodeToKeysym (dpy, (KeyCode) parsed_code, 0, 0) != NoSymbol)
{
km->UseKeyCode = True;
km->from_kc = (KeyCode) fromcode;
km->from_kc = (KeyCode) parsed_code;
if (debug)
{
KeySym ks_temp = XkbKeycodeToKeysym (dpy, (KeyCode) fromcode, 0, 0);
fprintf(stderr, "Assigned mapping from from \"%s\" ( keysym 0x%x, "
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,
@ -455,28 +498,47 @@ KeyMap_t *parse_token (Display *dpy, char *token, Bool debug)
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 = XKeysymToKeycode (dpy, ks);
if (code == 0)
code = (KeyCode) parsed_code;
}
else
{
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;
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;
}
}
km->to_keys = key_add_key (km->to_keys, code);
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",
key,
(unsigned) XStringToKeysym (key),
(unsigned) code);
XKeysymToString(ks_temp),
(unsigned) ks_temp,
(unsigned) code);
}
}
}
@ -516,3 +578,28 @@ KeyMap_t *parse_mapping (Display *ctrl_conn, char *mapping, Bool debug)
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");
}

Loading…
Cancel
Save