mirror of
https://github.com/X11Libre/xf86-input-evdev.git
synced 2026-03-24 01:34:04 +00:00
Add a property to toggle function key mode
On some keyboards, the multimedia function keys are overlaid with the F
keys. This property enables clients to switch the primary mode of these F
keys between function keys and multimedia keys.
Some keyboards provide an Fn key to toggle between the modes. This is
hardware-specific and may or may not work on any given keyboard device.
The current imlementation is only hooked up to apple keyboards.
The kernel provides a tweak to enable/disable.
/sys/module/hid_apple/parameters/fnmode
0 .. keyboard sends Fx keys, Fn disabled
1 .. keyboard sends multimedia keys, Fn toggles to function keys
2 .. keyboard sends function keys, Fn toggles to multimedia keys
If fnmode is on 0, we force it to 2.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Michel Dänzer <michel@daenzer.net>
This commit is contained in:
@@ -75,4 +75,16 @@
|
||||
/* CARD32 */
|
||||
#define EVDEV_PROP_THIRDBUTTON_THRESHOLD "Evdev Third Button Emulation Threshold"
|
||||
|
||||
/* CARD8, 1 value,
|
||||
This property is initialized on devices that have multimedia keys on the
|
||||
function keys. The value of the property selects the default behaviour
|
||||
for the function keys. The behaviour of the fn key (if any exists) is
|
||||
hardware specific. On some hardware, fn may toggle the other set of
|
||||
functions available on the keys.
|
||||
|
||||
0 send functions keys by default, fn may toggle to multimedia keys
|
||||
1 send multimedia keys by default, fn may toggle to function keys
|
||||
*/
|
||||
#define EVDEV_PROP_FUNCTION_KEYS "Evdev Function Keys"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,5 +37,6 @@ AM_CPPFLAGS =-I$(top_srcdir)/include
|
||||
emuMB.c \
|
||||
emuThird.c \
|
||||
emuWheel.c \
|
||||
draglock.c
|
||||
draglock.c \
|
||||
apple.c
|
||||
|
||||
|
||||
312
src/apple.c
Normal file
312
src/apple.c
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright © 2011 Red Hat, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without
|
||||
* fee, provided that the above copyright notice appear in all copies
|
||||
* and that both that copyright notice and this permission notice
|
||||
* appear in supporting documentation, and that the name of Red Hat
|
||||
* not be used in advertising or publicity pertaining to distribution
|
||||
* of the software without specific, written prior permission. Red
|
||||
* Hat makes no representations about the suitability of this software
|
||||
* for any purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
|
||||
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Peter Hutterer (peter.hutterer@redhat.com)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <evdev.h>
|
||||
#include <evdev-properties.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <exevents.h>
|
||||
#include <xf86.h>
|
||||
#include <xf86Xinput.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
/* Apple-specific controls.
|
||||
*
|
||||
* On Apple keyboards, the multimedia function keys are overlaid with the F
|
||||
* keys. F1 is also BrightnessDown, F10 is Mute, etc. The kernel provides a
|
||||
* tweak to enable/disable this.
|
||||
*
|
||||
* /sys/module/hid_apple/parameters/fnmode
|
||||
* 0 .. keyboard sends Fx keys, fn is disabled
|
||||
* 1 .. keyboard sends multimedia keys, fn sends Fx keys
|
||||
* 2 .. keyboard sends Fx keys, fn sends multimedia keys
|
||||
*
|
||||
* We only handle 1 and 2, don't care about 0. If fnmode is found to be on
|
||||
* 0, we force it to 2 instead.
|
||||
*/
|
||||
|
||||
/* In this file: fkeymode refers to the evdev-specific enums and parameters,
|
||||
* fnmode refers to the fnmode parameter exposed by the kernel. fnmode is
|
||||
* apple-specific */
|
||||
#define FNMODE_PATH "/sys/module/hid_apple/parameters/fnmode"
|
||||
|
||||
/* Taken from the kernel */
|
||||
#define USB_VENDOR_ID_APPLE 0x05ac
|
||||
#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d
|
||||
#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e
|
||||
#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f
|
||||
#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220
|
||||
#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221
|
||||
#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a
|
||||
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
|
||||
|
||||
static int EvdevAppleGetProperty (DeviceIntPtr dev, Atom property);
|
||||
static int EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom,
|
||||
XIPropertyValuePtr val, BOOL checkonly);
|
||||
|
||||
static Atom prop_fkeymode;
|
||||
static Bool fnmode_readonly; /* set if we can only read fnmode */
|
||||
|
||||
struct product_table
|
||||
{
|
||||
unsigned int vendor;
|
||||
unsigned int product;
|
||||
} apple_keyboard_table[] = {
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS},
|
||||
{ 0, 0}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return TRUE if the device matches a product in the given product table,
|
||||
* FALSE otherwise
|
||||
*/
|
||||
static Bool product_check(const struct product_table *t, int vendor, int product)
|
||||
{
|
||||
while (t->vendor)
|
||||
{
|
||||
if (vendor == t->vendor && product == t->product)
|
||||
return TRUE;
|
||||
t++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 0 on success, -1 otherwise (check errno)
|
||||
*/
|
||||
static int
|
||||
set_fnmode(enum fkeymode fkeymode)
|
||||
{
|
||||
int fd;
|
||||
char mode;
|
||||
int bytes_written;
|
||||
|
||||
if (fkeymode == FKEYMODE_UNKNOWN)
|
||||
{
|
||||
errno = EINVAL; /* silly you */
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open(FNMODE_PATH, O_WRONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
mode = (fkeymode == FKEYMODE_FKEYS) ? '2' : '1';
|
||||
|
||||
bytes_written = write(fd, &mode, 1);
|
||||
close(fd);
|
||||
|
||||
return (bytes_written == 1) ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value of fnmode. If fnmode is found to be on 0, we set it
|
||||
* to 2 in the process. Yes, quite daring, I know. I live on the edge.
|
||||
*
|
||||
* @return The current setting of fnmode or FKEYMODE_UNKNOWN on error (check
|
||||
* errno)
|
||||
*/
|
||||
static enum fkeymode
|
||||
get_fnmode(void)
|
||||
{
|
||||
int fd;
|
||||
char retvalue;
|
||||
|
||||
fd = open(FNMODE_PATH, O_RDWR);
|
||||
if (fd < 0 && errno == EACCES)
|
||||
{
|
||||
fnmode_readonly = TRUE;
|
||||
fd = open(FNMODE_PATH, O_RDONLY);
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
goto err;
|
||||
|
||||
if (read(fd, &retvalue, 1) != 1)
|
||||
goto err;
|
||||
|
||||
if (retvalue != '0' && retvalue != '1' && retvalue != '2')
|
||||
{
|
||||
xf86Msg(X_ERROR, "Invalid fnmode value: %c\n", retvalue);
|
||||
errno = EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
/* we don't want 0, switch to 2 */
|
||||
if (retvalue == '0')
|
||||
{
|
||||
if (fnmode_readonly)
|
||||
xf86Msg(X_WARNING, "fnmode is disabled and read-only. Fn key will"
|
||||
"not toggle to multimedia keys.\n");
|
||||
else
|
||||
set_fnmode(FKEYMODE_FKEYS);
|
||||
}
|
||||
|
||||
|
||||
return retvalue == '1' ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS;
|
||||
|
||||
err:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
return FKEYMODE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property value to fkeymode. If the property doesn't exist,
|
||||
* initialize it.
|
||||
*/
|
||||
static void set_fkeymode_property(InputInfoPtr pInfo, enum fkeymode fkeymode)
|
||||
{
|
||||
DeviceIntPtr dev = pInfo->dev;
|
||||
BOOL init = FALSE;
|
||||
char data;
|
||||
|
||||
switch(fkeymode)
|
||||
{
|
||||
case FKEYMODE_FKEYS: data = 0; break;
|
||||
case FKEYMODE_MMKEYS: data = 1; break;
|
||||
case FKEYMODE_UNKNOWN:
|
||||
xf86IDrvMsg(pInfo, X_ERROR, "Failed to get fnmode (%s)\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prop_fkeymode) {
|
||||
init = TRUE;
|
||||
prop_fkeymode = MakeAtom(EVDEV_PROP_FUNCTION_KEYS, strlen(EVDEV_PROP_FUNCTION_KEYS), TRUE);
|
||||
}
|
||||
|
||||
/* Don't send an event if we're initializing the property */
|
||||
XIChangeDeviceProperty(dev, prop_fkeymode, XA_INTEGER, 8,
|
||||
PropModeReplace, 1, &data, !init);
|
||||
|
||||
if (init)
|
||||
{
|
||||
XISetDevicePropertyDeletable(dev, prop_fkeymode, FALSE);
|
||||
XIRegisterPropertyHandler(dev, EvdevAppleSetProperty, EvdevAppleGetProperty, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when a client reads the property state.
|
||||
* Update with current kernel state, it may have changed behind our back.
|
||||
*/
|
||||
static int
|
||||
EvdevAppleGetProperty (DeviceIntPtr dev, Atom property)
|
||||
{
|
||||
if (property == prop_fkeymode)
|
||||
{
|
||||
InputInfoPtr pInfo = dev->public.devicePrivate;
|
||||
EvdevPtr pEvdev = pInfo->private;
|
||||
enum fkeymode fkeymode;
|
||||
|
||||
fkeymode = get_fnmode();
|
||||
if (fkeymode != pEvdev->fkeymode) {
|
||||
/* set internal copy first, so we don't write to the file in
|
||||
* SetProperty handler */
|
||||
pEvdev->fkeymode = fkeymode;
|
||||
set_fkeymode_property(pInfo, fkeymode);
|
||||
}
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
static int
|
||||
EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom,
|
||||
XIPropertyValuePtr val, BOOL checkonly)
|
||||
{
|
||||
InputInfoPtr pInfo = dev->public.devicePrivate;
|
||||
EvdevPtr pEvdev = pInfo->private;
|
||||
|
||||
if (atom == prop_fkeymode)
|
||||
{
|
||||
CARD8 v = *(CARD8*)val->data;
|
||||
|
||||
if (val->format != 8 || val->type != XA_INTEGER)
|
||||
return BadMatch;
|
||||
|
||||
if (fnmode_readonly)
|
||||
return BadAccess;
|
||||
|
||||
if (v > 1)
|
||||
return BadValue;
|
||||
|
||||
if (!checkonly)
|
||||
{
|
||||
if ((!v && pEvdev->fkeymode != FKEYMODE_FKEYS) ||
|
||||
(v && pEvdev->fkeymode != FKEYMODE_MMKEYS))
|
||||
{
|
||||
pEvdev->fkeymode = v ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS;
|
||||
set_fnmode(pEvdev->fkeymode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
void
|
||||
EvdevAppleInitProperty(DeviceIntPtr dev)
|
||||
{
|
||||
InputInfoPtr pInfo = dev->public.devicePrivate;
|
||||
EvdevPtr pEvdev = pInfo->private;
|
||||
enum fkeymode fkeymode;
|
||||
|
||||
if (!product_check(apple_keyboard_table,
|
||||
pEvdev->id_vendor, pEvdev->id_product))
|
||||
return;
|
||||
|
||||
fkeymode = get_fnmode();
|
||||
pEvdev->fkeymode = fkeymode;
|
||||
set_fkeymode_property(pInfo, fkeymode);
|
||||
}
|
||||
@@ -1359,6 +1359,7 @@ EvdevInit(DeviceIntPtr device)
|
||||
Evdev3BEmuInitProperty(device);
|
||||
EvdevWheelEmuInitProperty(device);
|
||||
EvdevDragLockInitProperty(device);
|
||||
EvdevAppleInitProperty(device);
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
10
src/evdev.h
10
src/evdev.h
@@ -85,6 +85,13 @@
|
||||
/* Number of longs needed to hold the given number of bits */
|
||||
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
|
||||
|
||||
/* Function key mode */
|
||||
enum fkeymode {
|
||||
FKEYMODE_UNKNOWN = 0,
|
||||
FKEYMODE_FKEYS, /* function keys send function keys */
|
||||
FKEYMODE_MMKEYS, /* function keys send multimedia keys */
|
||||
};
|
||||
|
||||
/* axis specific data for wheel emulation */
|
||||
typedef struct {
|
||||
int up_button;
|
||||
@@ -197,6 +204,8 @@ typedef struct {
|
||||
/* Event queue used to defer keyboard/button events until EV_SYN time. */
|
||||
int num_queue;
|
||||
EventQueueRec queue[EVDEV_MAXQUEUE];
|
||||
|
||||
enum fkeymode fkeymode;
|
||||
} EvdevRec, *EvdevPtr;
|
||||
|
||||
/* Event posting functions */
|
||||
@@ -243,4 +252,5 @@ void EvdevMBEmuInitProperty(DeviceIntPtr);
|
||||
void Evdev3BEmuInitProperty(DeviceIntPtr);
|
||||
void EvdevWheelEmuInitProperty(DeviceIntPtr);
|
||||
void EvdevDragLockInitProperty(DeviceIntPtr);
|
||||
void EvdevAppleInitProperty(DeviceIntPtr);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user