[RFC] libinputmapper: Input device configuration for graphic-servers



[bcc to gnome-shell-list and kwin, to keep discussion on wayland-devel]

Hi

Without a generic graphics-server like xserver, compositors need to
handle input devices themselves if run as wayland compositors. To
avoid having several different conflicting implementations, I wrote up
a small proposal and library API to have a common configuration.

How is it currently handled?
A compositor uses udev to listen for input devices. For each
input-device, they use some heuristics (test for KEY_*, ABS_*, ..
event-bits) to figure out what kind of input is provided by the
device. Unknown devices and events are ignored, devices that look
useful are passed to the correct input-driver.
For keyboard input, libxkbcommon is used. For
mouse/touchpad/touchscreen input, every compositor has it's own simple
driver (I am not aware of an attempt to put xf86-input-synaptics into
an independent library). For other device types, applications handle
input themselves (gamepads, joysticks, and so on). And then there are
devices that have x11 drivers, but I am not aware of external drivers
for wayland-compositors (like wacom digitizers).

I am not interested in the device-drivers itself (for now in this
project). I think it would be nice to write a libsynapticscommon,
libmousecommon, .. which provide a libxkbcommon'ish interface for
other device types independent of the compositor implementation, but
that's another independent issue.
Instead, I am more interested in the device-detection and enumeration.
If I plug in a gamepad device, I don't want _any_ compositor to handle
it as a mouse, just because it provides REL_X/Y values. I don't want
compositors to erroneously handle accelerometers as mice because they
provide ABS_X/Y values. But on the other hand, I want users to be able
to tell compositors to handle ABS_X/Y input from their custom hardware
as mouse input, _iff_ they want to.
Furthermore, if a device has a buggy kernel driver and reports BTN_X
instead of BTN_A, I want all compositors to detect that and apply a
simple fixup button-mapping. Or allow users to remap
buttons/axis/LEDs/EV_WHATEVER arbitrarily.

udev provides some very basic heuristics with device-tags, but talking
to Kay Sievers, he'd like to avoid having huge detection-tables and
heuristics in udev (which is understandable).

Dmitry Torokhov is not averse to providing device-type properties in
the kernel input-subsystem, but on the other hand, it doesn't help us
much. Generic HID devices might still provide any arbitrary input that
we would have to write custom drivers/quirks for, if they don't match.
So if no-one steps up to do all that work, I recommend providing these
fixups in user-space. This also has the advantage, that users can
arbitrarily modify these rules if they want crazy setups (which users
normally want..). And we can ship fixup-rules for new devices, while
in the meantime writing kernel drivers for them and waiting for the
next kernel release. And don't forget the kernel drivers with broken
mappings, which we cannot fix due to backwards-compatibility, but
still want them to be correctly mapped in new
compositors/applications.


So what is the proposed solution?
My recommendation is, that compositors still search for devices via
udev and use device drivers like libxkbcommon. So linux evdev handling
is still controlled by the compositor. However, I'd like to see
something like my libinputmapper proposal being used for device
detection and classification.

libinputmapper provides an "inmap_evdev" object which reads device
information from an evdev-fd or sysfs /sys/class/input/input<num>
path, performs some heuristics to classify it and searches it's global
database for known fixups for broken devices.
It then provides "capabilities" to the caller, which allow them to see
what drivers to load on the device. And it provides a very simple
mapping table that allows to apply fixup mappings for broken devices.
These mappings are simple 1-to-1 mappings that are supposed to be
applied before drivers handle the input. This is to avoid
device-specific fixup in the drivers and move all this to the
inputmapper. An example would be a remapping for gamepads that report
BTN_A instead of BTN_NORTH, but we cannot fix them in the kernel for
backwards-compatibility reasons. The gamepad-driver can then assume
that if it receives BTN_NORTH, it is guaranteed to be BTN_NORTH and
doesn't need to special case xbox360/etc. controllers, because they're
broken.

libinputmapper would use some static heuristics for all this, but
additionally parse user-configuration. A configuration file contains
"[match]" entries, which specify device-configurations to load on
mtached devices. The device-configurations then contain different
blocks which can overwrite the auto-detected capabilities or provide
fixup-mappings.
But other configuration-sections can also be provided in the same
file. For instance an [xkb] group could be specified to set XKB
layout/variant/options. This won't be parsed by libinputmapper, but it
would provide the matching and forward the device-configuration to the
caller, which can pass it on to the driver.
Additionally, API users can provide separate (local)
configuration-files that allow users to change the configuration for
kmscon one way and another way for gnome-shell (or similar).

One can think of these configurations to replace the old
/etc/X11/xorg.conf.d/*.conf "InputClass" configuration items. Except
that the "matching" is offloaded to a separate file, and the "to be
applied configurations" are specified in "rule" files, that are linked
to from the "match" configurations. See below for syntax-examples.


I haven't implemented the library, yet. I am working on this. However,
I wrote up an API proposal that I attached inline below. kmscon input
handling is what I need it for, so I want something like this, anyway.
But I thought other compositors might be interested in this, too.

Comments are welcome!
Regards
David


Hint: The inmap_context_get_fd() and inmap_context_dispatch()
functions are used for file-monitors exclusively. Other than that, the
library doesn't provide any kind of file-descriptor handling or
dispatchers. Don't get confused by this.
I am also open to dropping this API and let callers monitor the files
themselves (optionally).

API proposal:
Attached inline so you can easily comment on it. It can also be found here:
  https://gist.github.com/dvdhrm/5563703
Please skip the code if you comment on the concept only!

/*
 * libinputmapper - Input Mapper Library
 *
 * Copyright (c) 2012-2013 David Herrmann <dh herrmann gmail com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * Input Mapper Library
 * libinputmapper detects capabilities of linux input devices and provides
 * optional mapping tables to overcome device incompatibilities. A huge set
 * of global and local configuration options is available to allow fast
 * updates of device tables and mappings.
 * The main users are graphic-servers, compositors and other applications
 * that work directly with linux evdev input devices. libinputmapper does
 * not hook into the evdev input event path. Instead, it provides a bunch
 * of small helpers that can detect device capabilities, provide fixup
 * button mappings or parse global and local user configurations. This gives
 * us global input device configuration, while still allowing graphics-servers
 * to perform custom input event handling and to apply local configurations.
 *
 * inmap_context is the main top-level object that is required for all other
 * objects. It keeps track of global configuration options, logging helpers
 * and more. Multiple contexts can be used independently from each other.
 * It reads global configuration options and optionally detects runtime
 * configuration changes.
 *
 * inmap_evdev wraps a single linux evdev input device and provides helpers
 * to read or write device attributes and features. It does not handle the
 * input event stream. This is still up to the applications. But it can read
 * device capabilities and provide fixup mapping tables which then can be
 * optionally applied by the application.
 *
 * inmap_mapping is an opaque mapping table. Each one is for a single device
 * and for a single purpose. It does nothing more than mapping linux input
 * events via a *_map() function.
 */

#ifndef INMAP_H
#define INMAP_H

#include <inttypes.h>
#include <linux/input.h>
#include <stdbool.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @file
 * Main libinputmapper API
 */

/**
 * @struct inmap_context
 * Opaque top level library context object
 *
 * Context objects contain shared state (eg., include paths, logging
 * functions, ..) for all other libinputmapper objects. Objects created on
 * different contexts do not share any memory and can be used independently.
 * However, a single context object (and all objects created on it) must not
 * be used simultaneously from multiple threads without synchronization, if
 * not otherwise noted.
 * Context objects also manage the system configuration and optionally
 * monitor them for runtime modifications.
 */
struct inmap_context;

/**
 * @struct inmap_evdev
 * Opaque linux event device object
 *
 * Evdev objects wrap the device-detection of any linux input event device.
 * It must be created by the caller either by fd or sysfs-path and then
 * performs the device detection. It uses a context object to apply any
 * system configuration and then assembles the capabilities of the detected
 * device. Depending on which capabilities are requested, it can allocate
 * the required mapping tables.
 */
struct inmap_evdev;

/**
 * @struct inmap_mapping
 * Opaque mapping table for a single device and purpose
 *
 * After being allocated, a mapping table is constant and serves only to
 * map input events to the correct output event. It takes the full linux
 * input_event object and performs the transformation inline. So you should
 * apply it directly after reading the event from the kernel and before
 * processing it.
 * Mapping tables are opaque so they can be shared between different devices
 * on a single inmap_context. Furthermore, they can be extended to perform
 * more complicated mappings than simple KEY_* and BTN_* 1-to-1 mappings.
 */
struct inmap_mapping;

/**
 * @enum inmap_capability
 * Generic device capabilities
 *
 * Device capabilities describe what generic interface is provided by the
 * detected device. A device can support any combination of them (or none).
 * Each interface follows specific rules. If a device does not follow these
 * rules, we must provide a mapping table so the correct data is reported.
 *
 * Capabilities can be added for all kinds of generic devices. However, custom
 * devices with model-specific features do not belong here, as their drivers
 * need to perform device-detection themselves, anyway.
 *
 * Furthermore, each capability needs a well-defined protocol. They are mostly
 * already provided by the kernel. But there are many devices and drivers
 * which provide inconsistent behavior. That's why each capability has to
 * be consistent and coherent and misbehaving devices get fixup mappings, if
 * we cannot fix it in the kernel device driver itself.
 */
enum inmap_capability {
/**
* @INMAP_CAP_KEYBOARD
* Keyboard interface
*
* The device is a real keyboard that is used for normal input.
* Power-buttons and similar devices which report KEY_* events for
* non-keyboard buttons are not considered a keyboard in this sense.
* Only real keyboards and small extension devices (like external
* NumPads) belong here.
* The defined KEY_* events are not listed here as normally no
* mapping is applied. Libraries like libxkbcommon should be used
* for keyboard handling and mapping.
*/
INMAP_CAP_KEYBOARD = (1 << 0),

/**
* @INMAP_CAP_MOUSE
* Mouse interface
*
* The device is a real mouse with relative motion events. Motion
* is reported as REL_X/Y. Buttons are reported as BTN_0-BTN_9 or
* BTN_LEFT-BTN_TASK.
* Other mouse-like devices (especially those with absolute motion
* events) do not belong here.
*/
INMAP_CAP_MOUSE = (1 << 1),

/**
* @INMAP_CAP_TOUCHPAD
* Touchpad interface
*
* Touchpad devices belong here. But only those that are used as
* pointing devices and are intended to drive a mouse device. That is,
* the input is interpreted as relative motion, even though the
* reported data is normally absolute.
* Hence, touchscreens do not belong here. They are normally
* interpreted as absolute position input.
*
* For multitouch devices, libmtdev defines the protocol.
*
* @TODO define the protocol (see the synaptics descriptions)
*/
INMAP_CAP_TOUCHPAD = (1 << 2),

/**
* @INMAP_CAP_TOUCHSCREEN
* Touchscreen interface
*
* Touchscreens are exactly the same as @INMAP_CAP_TOUCHPAD, but they
* are normally attached to a single monitor and interpreted as
* absolute positioning input.
* Therefore, they are not included in @INMAP_CAP_TOUCHPAD, but are
* a separate group instead. The reported protocol is the same, though.
*
* @sa INMAP_CAP_TOUCHPAD
*/
INMAP_CAP_TOUCHSCREEN = (1 << 3),

/**
* @INMAP_CAP_ACCELEROMETER
* Accelerometer interface
*
* Accelerometer devices report linear acceleration data as ABS_X/Y/Z
* and rotational acceleration as ABS_RX/Y/Z.
*
* @TODO this collides with ABS_X/Y of absolute pointing devices
*       introduce ABS_ACCELX/Y/Z
*/
INMAP_CAP_ACCELEROMETER = (1 << 4),

/**
* @INMAP_CAP_GAMEPAD
* Gamepad interface
*
* All standard gamepad devices belong here.
*
* @TODO define gamepad mapping (see linux-input vger discussion)
*/
INMAP_CAP_GAMEPAD = (1 << 5),

/**
* @INMAP_CAP_JOYSTICK
* Joystick interface
*
* All standard joystick devices belong here.
*
* @TODO define joystick mapping
*/
INMAP_CAP_JOYSTICK = (1 << 6),
};

/**
 * @defgroup context Library Context
 * Managing library contexts
 *
 * A context object contains all top-level information. It can be shared
 * between multiple device objects and provides global infrastructure to them.
 *
 * @{
 */

/**
 * @enum inmap_context_flags
 * Context creation and operation flags
 *
 * Different flags that control how a context is created and how it behaves.
 *
 * @memberof inmap_context
 * @sa inmap_context_new()
 */
enum inmap_context_flags {
/**
* @INMAP_CONTEXT_IGNORE_ENVIRONMENT
* Ignore environment variables
*
* The context object will not use any environment variables as
* default values if they are not explicitly set.
*
* @memberof inmap_context
*/
INMAP_CONTEXT_IGNORE_ENVIRONMENT = (1 << 0),

/**
* @INMAP_CONTEXT_MONITOR_CONF_FILES
* Monitor configuration files
*
* If set, the context will monitor configuration files for runtime
* modifications. This allows adapting to changes without restarting
* the application.
* Changes are not automatically applied. The application has to
* react to these events and retrigger device detection for all
* active devices.
*
* @memberof inmap_context
* @sa inmap_context_dispatch()
*/
INMAP_CONTEXT_MONITOR_CONF_FILES = (1 << 1),

/**
* @INMAP_CONTEXT_NO_DEFAULT_CONF
* Skip default configuration
*
* If set, the global default configuration is not parsed.
*
* @memberof inmap_context
*/
INMAP_CONTEXT_NO_DEFAULT_CONF = (1 << 2),
};

/**
 * Create new context
 *
 * @param[out] out   Pointer to the new context is stored here
 * @param[in] caps   Capabilities that are used with this context
 * @param[in] flags  Optional flags or 0
 *
 * Create a new context and store a pointer to it in @out. You must specify
 * all capabilities that you want to use with this context in @caps. This
 * causes the context to ignore and skip all configuration options for
 * other capabilities to save memory.
 * You cannot modify the capabilities afterwards.
 *
 * You can change the behavior of the context with additional flags. See the
 * @inmap_context_flags for a description of flags.
 *
 * Context objects are reference counted. Initial ref-count is 1. You can
 * modify it via @inmap_context_ref() and @inmap_context_unref().
 *
 * @returns 0 on success, negative error code on failure.
 *
 * @memberof inmap_context
 * @sa inmap_context_flags
 * @sa inmap_context_ref()
 * @sa inmap_context_unref()
 */
int inmap_context_new(struct inmap_context **out,
     enum inmap_capability caps,
     enum inmap_context_flags flags);

/**
 * Increase context ref-count
 *
 * @param[in] ctx  Valid context object or NULL
 *
 * Increase ref-count of the given context by 1. If NULL is passed as
 * context, then nothing is done.
 *
 * @memberof inmap_context
 * @sa inmap_context_new()
 * @sa inmap_context_unref()
 */
void inmap_context_ref(struct inmap_context *ctx);

/**
 * Decrease context ref-count
 *
 * @param[in] ctx  Valid context object or NULL
 *
 * Decrease ref-count of @ctx by 1. You must not access @ctx afterwards if
 * you do not own another reference. This function destroys @ctx if the
 * ref-count drops to 0.
 * If @ctx is NULL, nothing is done.
 *
 * @memberof inmap_context
 * @sa inmap_context_new()
 * @sa inmap_context_ref()
 */
void inmap_context_unref(struct inmap_context *ctx);

/**
 * Set user-data
 *
 * @param[in] ctx        Valid context object
 * @param[in] user_data  User-data pointer
 *
 * Set a user-controlled pointer field in @ctx. This allows the user to
 * associate any data with this context. It can be retrieved via
 * @inmap_context_get_user_data() again.
 * This field is set to NULL during context initialization and then never
 * modified by the library code itself. It is a fully user controlled field.
 *
 * @memberof inmap_context
 * @sa inmap_context_get_user_data()
 */
void inmap_context_set_user_data(struct inmap_context *ctx, void *user_data);

/**
 * Get user-data
 *
 * @param[in] ctx  Valid context object
 *
 * @returns user-data field previously set via @inmap_context_set_user_data()
 *
 * @memberof inmap_context
 * @sa inmap_context_set_user_data()
 */
void *inmap_context_get_user_data(struct inmap_context *ctx);

/**
 * @enum inmap_context_log_level
 * Log-level of context objects
 *
 * The log-levels are exactly the same as the kernel log-levels.
 *
 * @memberof inmap_context
 */
enum inmap_context_log_level {
INMAP_CONTEXT_LOG_LEVEL_EMERGENCY,
INMAP_CONTEXT_LOG_LEVEL_ALERT,
INMAP_CONTEXT_LOG_LEVEL_CRITICAL,
INMAP_CONTEXT_LOG_LEVEL_ERROR,
INMAP_CONTEXT_LOG_LEVEL_WARNING,
INMAP_CONTEXT_LOG_LEVEL_NOTICE,
INMAP_CONTEXT_LOG_LEVEL_INFO,
INMAP_CONTEXT_LOG_LEVEL_DEBUG,
};

/**
 * Set log level
 *
 * @param[in] ctx    Valid context object
 * @param[in] level  New log level
 *
 * Change the default log-level to @level. Only log-messages with equal or
 * higher priority are passed to the log-function. Default log-level is
 * @INMAP_CONTEXT_LOG_LEVEL_ERROR.
 *
 * During context creation, the log-level is set to the value of the
 * environment variable INMAP_LOG_LEVEL if set. It may be specified as a
 * number or name.
 *
 * @memberof inmap_context
 * @sa inmap_context_log_level
 * @sa inmap_context_get_log_level()
 * @sa inmap_context_set_log_fn()
 */
void inmap_context_set_log_level(struct inmap_context *ctx, unsigned int level);

/**
 * Get log level
 *
 * @param[in] ctx  Valid context object
 *
 * @returns log-level previously set via inmap_context_log_leve()
 *
 * @memberof inmap_context
 * @sa inmap_context_log_level
 * @sa inmap_context_set_log_level()
 * @sa inmap_context_set_log_fn()
 */
unsigned int inmap_context_get_log_level(struct inmap_context *ctx);

/**
 * Set log function
 *
 * @param[in] ctx     Valid context object
 * @param[in] log_fn  Log-function or NULL
 *
 * Change the default log-function to @log_fn. If @log_fn is NULL, no
 * logging will be done.
 * This log-function is called for every log-message that has a higher
 * or equal priority as the currently set log-level.
 *
 * The first argument is the context object, followed by the user_data field
 * which can also be retrieved via inmap_context_get_user_data(). Then the
 * log-level of the message, the file/line/function information where the
 * message originated and the format/args combination with the actual
 * log message.
 *
 * @memberof inmap_context
 * @sa inmap_context_log_level
 * @sa inmap_context_set_log_level()
 */
void inmap_context_set_log_fn(struct inmap_context *ctx,
     void (*log_fn) (struct inmap_context *ctx,
                     void *user_data,
                     unsigned int level,
                     const char *file,
                     int line,
                     const char *fn,
                     const char *format,
                     va_list args));

/**
 * Return the context file-descriptor
 *
 * @param[in] ctx  Valid context
 *
 * If the context was created with the @INMAP_CONTEXT_MONITOR_CONF_FILES
 * flag, it will allocate kernel resources to monitor configuration files.
 * These report changes via file-descriptors. You can track these via this
 * function.
 * Whenever the file-descriptor is readable, you must call
 * inmap_context_dispatch() to let the library handle the events.
 *
 * If you didn't use config-file tracking, this will always return -1 and
 * there is no reason to call this or inmap_context_dispatch().
 *
 * @returns file-descriptor or -1 if not used
 *
 * @memberof inmap_context
 * @sa inmap_context_dispatch()
 */
int inmap_context_get_fd(struct inmap_context *ctx);

/**
 * @enum inmap_context_dispatch_event
 * Dispatch events
 *
 * Events that are handled by the context object and returned to the caller
 * so they can react to it.
 *
 * @memberof inmap_context
 * @sa inmap_context_dispatch()
 */
enum inmap_context_dispatch_event {
/**
* @INMAP_CONTEXT_CONF_CHANGED
* Configuration changed
*
* This is returned whenever a configuration file was modified and
* @INMAP_CONTEXT_MONITOR_CONF_FILES was used. The library itself
* does not react to this event, so you need to reread the
* configuration files yourself via inmap_context_conf_refresh() and
* recreate the inmap_evdev objects that might have changed.
*
* @memberof inmap_context
*/
INMAP_CONTEXT_CONF_CHANGED = (1 << 0),
};

/**
 * Dispatch outstanding work
 *
 * @param[in] ctx  Valid context object
 *
 * Whenever the internal file-descriptor of this context is readable (see
 * also @inmap_context_get_fd()), you must call this function to dispatch
 * any outstanding work.
 * This is currently only used for the @INMAP_CONTEXT_MONITOR_CONF_FILES
 * implementation. If @inmap_context_get_fd() does not return a valid
 * file-descriptor, you can skip calling this function.
 * All events that were detected are returned as bitmask so the caller can
 * react on them.
 *
 * @returns @inmap_context_dispatch_event event-flags on success, negative
 * error code on failure.
 *
 * @memberof inmap_context
 * @sa inmap_context_get_fd()
 */
int inmap_context_dispatch(struct inmap_context *ctx);

/** @} */

/**
 * @defgroup context-conf Library Context Configuration Files
 * Managing configuration files
 *
 * Users can supply configuration files to extend the detection logic
 * without modifying the source code. Default configurations are always
 * used (except if INMAP_CONTEXT_NO_DEFAULT_CONF is used) but you can
 * supply additional files for local configurations.
 *
 * If a configuration file is given by path or filename it can be monitored
 * for runtime modifications if INMAP_CONTEXT_MONITOR_CONF_FILES is used.
 * Configurations given via memory cannot be monitored.
 *
 * Configuration files contain matching-rules and filenames of
 * device-configuration-files to apply for matched devices. So the main
 * configuration file simply contains a list of names and IDs for devices
 * that need special fixups.
 *
 * Syntax of configuration files is ini-style like this:
 * @code
 *   [match <optional-name>]
 *   name = *name*wild*mask
 *   bus = <bus-name>,<another-bus-name>,..
 *   vid = <vendor-id>,<another-vendor-id>,..
 *   pid = <product-id>,..
 *   rules = <relative-path-to-device-conf>
 * @endcode
 * You can put as many of these [match] groups into a configuration file. All
 * fields except for "rules" are optional. If a device matches a [match] block,
 * the rules that are found in "rules" are applied to the device. Multiple
 * blocks may match and are applied in the order as specified.
 * Fields are matched with a logical "AND". So if "bus" and "name" are
 * specified, the device must match both. Multiple entries in the same
 * category are matched with a logical "OR". So if multiple "vid" entries
 * are given, only one of them must match.
 * Unknown rules are silently ignored. This allows to easily extend this file
 * with further entries in the future.
 *
 * Rule-files contain rules that are to be applied to a device. Unknown entries
 * are silently ignored. This allows to store additional information in
 * these files for other libraries to use.
 * Supported entries are:
 * @code
 *   [capabilities <optional-name>]
 *   add = <cap1>,<cap2>,..
 *   remove = <cap1>,<cap2>,..
 *   set = <cap1>,<cap2>,..
 * @endcode
 * This overwrites the heuristics used to detect device capabilities. "add"
 * causes the library to add the given capabilities to the auto-detected caps.
 * "remove" causes the library to remove them and "set" overwrites all of
 * them. "set" is mutually exclusive to "add" and "remove", obviously.
 * @code
 *   [mapping <optional-name>]
 *   map = <type>:<code> => [<type>:]<code>,
 *         [<type>:]<code> => [<type>:]<code>,
 *         ..
 * @endcode
 * This specifies a simple mapping table. <type> must only be specified once
 * and will automatically be kept for following entries that have no explicit
 * <type> prefix.
 * inmap_evdev will create a mapping table from these rules and map the
 * specified type+code combinations to the target type+code. The "value"
 * field cannot be modified for now.
 *
 * @{
 */

/** configuration file parser flags */
enum inmap_context_conf_flags {
/** Allow any file type, not only regular files. */
INMAP_CONTEXT_CONF_ALLOW_ANY = (1 << 0),
/** Do not monitor this file. */
INMAP_CONTEXT_CONF_DONT_MONITOR = (1 << 1),
};

/**
 * Add configuration file from memory buffer
 *
 * @param[in] ctx     Valid context object
 * @param[in] buffer  Pointer to the memory buffer to read from. Does not
 *                    have to be zero terminated.
 * @param[in] length  Length of @buffer not including any possible
 *                    terminating zero character.
 * @param[in] flags   Optional flags for the parser or 0.
 *
 * Parses the buffer given in @buffer as configuration file and updates the
 * context accordingly.
 *
 * @INMAP_CONTEXT_CONF_ALLOW_ANY has no effect if set and is ignored.
 *
 * Obviously, @INMAP_CONTEXT_MONITOR_CONF_FILES has no effect on
 * configurations that are parsed with this function. If you want to track
 * configuration files for changes, you must use
 * @inmap_context_conf_add_from_path.
 * @INMAP_CONTEXT_CONF_DONT_MONITOR is ignored, too.
 *
 * @returns 0 on success, negative error code on failure.
 *
 * @memberof inmap_context
 * @sa inmap_context_conf_add_from_path()
 */
int inmap_context_conf_add_from_buffer(struct inmap_context *ctx,
      const void *buffer, size_t length,
      enum inmap_context_conf_flags flags);

/**
 * Add configuration file from file path
 *
 * @param[in] ctx   Valid context object
 * @param[in] path  Path to the configuration file. Must be a zero-
 *                  terminated string.
 *
 * Parses the configuration file at @path and updates the context
 * accordingly.
 *
 * @returns 0 on success, negative error code on failure.
 *
 * @memberof inmap_context
 * @sa inmap_context_conf_add_from_buffer()
 */
int inmap_context_conf_add_from_path(struct inmap_context *ctx,
    const char *path);

/**
 * Parse configuration files
 *
 * @param[in] ctx  Valid context object
 *
 * This resets the configuration-file cache and re-reads all files. Use
 * this whenever a configuration file changed. You also must call this
 * after creating the context to read the default configurations.
 *
 * Files added via @inmap_context_conf_add_from_path() are automatically
 * parsed while added, but can be re-parsed by calling this function.
 *
 * @returns 0 on success, negative error code on failure
 *
 * @memberof inmap_context
 */
int inmap_context_conf_parse(struct inmap_context *ctx);

/** @} */

/**
 * @defgroup evdev Evdev input devices
 * Managing evdev input device detection and rules
 *
 * @{
 */

/**
 * Create new evdev device from fd
 *
 * @param[in] ctx   Valid context object
 * @param[out] out  Pointer to new evdev object is stored here
 * @param[in] fd    File-descriptor of input evdev device
 *
 * Create a new inmap_evdev context and retrieve information via a normal
 * file-descriptor. The file-descriptor is not kept by this function but
 * only used during initialization to retrieve device-name and so on.
 *
 * The new evdev object will be associated with the given context. All
 * matching rules are immediately applied and device detection will be
 * performed.
 *
 * inmap_evdev objects are ref-counted. Initial ref-count will be 1.
 *
 * @returns 0 on success, negative error code on failure
 *
 * @memberof inmap_evdev
 */
int inmap_evdev_new_from_fd(struct inmap_context *ctx,
   struct inmap_evdev **out,
   int fd);

/**
 * Create new evdev device from sysfs-path
 *
 * @param[in] ctx   Valid context object
 * @param[out] out  Pointer to new evdev object is stored here
 * @param[in] path  Path to sysfs-device directory
 *
 * Create a new inmap_evdev context and retrieve information via sysfs. No
 * file-descriptor to the event-device is needed.
 * The sysfs path @path normally points to a directory like
 *   /sys/class/input/input<num>/
 * Note that this must be the input-device directory, not the event/evdev
 * device directory!
 *
 * The new evdev object will be associated with the given context. All
 * matching rules are immediately applied and device detection will be
 * performed.
 *
 * inmap_evdev objects are ref-counted. Initial ref-count will be 1.
 *
 * @returns 0 on success, negative error code on failure
 *
 * @memberof inmap_evdev
 */
int inmap_evdev_new_from_syspath(struct inmap_context *ctx,
struct inmap_evdev **out,
const char *path);

/**
 * Increase inmap_evdev ref-count
 *
 * @param[in] evdev  Valid evdev object, or NULL
 *
 * Increase ref-count of @evdev by 1. Does nothing if @evdev is NULL.
 *
 * @memberof inmap_evdev
 */
void inmap_evdev_ref(struct inmap_evdev *evdev);

/**
 * Decrease inmap_evdev ref-count
 *
 * @param[in] evdev  Valid evdev object, or NULL
 *
 * Decrease ref-count of @evdev by 1. This destroys the object immediately,
 * iff the ref-count drops to 0. Does nothing if @evdev is NULL.
 *
 * @memberof inmap_evdev
 */
void inmap_evdev_unref(struct inmap_evdev *evdev);

/**
 * Get device capabilities
 *
 * @param[in] evdev  Valid evdev object
 *
 * Return a bitmask of capabilities of the given device. You can use these
 * to decide which device drivers to load on this input device.
 *
 * @returns bitmask of capabilities
 *
 * @memberof inmap_evdev
 */
enum inmap_capabilities inmap_evdev_get_capabilities(struct inmap_evdev *evdev);

/**
 * Create mapping table
 *
 * @param[in] evdev  Valid evdev object
 * @param[out] out   Pointer to new mapping table is stored here
 * @param[in] caps   Capability bitmask
 *
 * Create new mapping tables for the capabilities given in @caps. The new
 * table is stored in @out. If no table is available, @out will be NULL
 * afterwards.
 * This combines all required tables for the given capabilities in one
 * single mapping-table. You should apply these mappings before processing
 * the read input events.
 *
 * @returns 0 on success, negative error code on failure
 *
 * @memberof inmap_evdev
 */
int inmap_evdev_get_mapping(struct inmap_evdev *evdev,
   struct inmap_mapping **out,
   unsigned int caps);

/** @} */

/**
 * @defgroup mapping Event mapping
 * Mapping linux input-events
 *
 * @{
 */

/**
 * Increase ref-count of mapping table
 *
 * @param[in] map  Valid mapping table, or NULL
 *
 * Increase ref-count of @map by 1. Does nothing if @map is NULL.
 *
 * @memberof inmap_mapping
 */
void inmap_mapping_ref(struct inmap_mapping *map);

/**
 * Decrease ref-count of mapping table
 *
 * @param[in] map  Valid mapping table, or NULL
 *
 * Decrease ref-count of @map by 1. Destroys the object iff the ref-count
 * drops to 0. Does nothing if @map is NULL.
 *
 * @memberof inmap_mapping
 */
void inmap_mapping_unref(struct inmap_mapping *map);

/**
 * Map linux input-event
 *
 * @param[in] map        Valid mapping table, or NULL
 * @param[in,out] event  Linux input event
 *
 * Map the input-event in @event according to the rules in @map. If @map is
 * NULL no mapping is applied.
 * The mapping is performed inline, so @event will be modified by this
 * function.
 *
 * @memberof inmap_mapping
 */
void inmap_mapping_map(struct inmap_mapping *map,
      struct input_event *event);

/** @} */

#ifdef __cplusplus
}
#endif

#endif /* INMAP_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]