[PATCH evdev 3/4] Add a property to toggle fnmode on Apple keyboards

Peter Hutterer peter.hutterer at who-t.net
Tue May 17 22:00:57 PDT 2011


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
    1 .. keyboard sends multimedia keys

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 include/evdev-properties.h |    3 +
 src/Makefile.am            |    3 +-
 src/apple.c                |  168 ++++++++++++++++++++++++++++++++++++++++++++
 src/evdev.c                |    1 +
 src/evdev.h                |    1 +
 5 files changed, 175 insertions(+), 1 deletions(-)
 create mode 100644 src/apple.c

diff --git a/include/evdev-properties.h b/include/evdev-properties.h
index 16f2af7..8887cd1 100644
--- a/include/evdev-properties.h
+++ b/include/evdev-properties.h
@@ -75,4 +75,7 @@
 /* CARD32 */
 #define EVDEV_PROP_THIRDBUTTON_THRESHOLD "Evdev Third Button Emulation Threshold"
 
+/* BOOL, 1 value, true → send function keys, false → send multimedia keys */
+#define EVDEV_PROP_APPLE_FNMODE "Evdev Apple Function Keys"
+
 #endif
diff --git a/src/Makefile.am b/src/Makefile.am
index d1efe53..b3a5671 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,5 +37,6 @@ AM_CPPFLAGS =-I$(top_srcdir)/include
                                emuMB.c \
                                emuThird.c \
                                emuWheel.c \
-                               draglock.c
+                               draglock.c \
+                               apple.c
 
diff --git a/src/apple.c b/src/apple.c
new file mode 100644
index 0000000..1a82e2a
--- /dev/null
+++ b/src/apple.c
@@ -0,0 +1,168 @@
+/*
+ * 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 at 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
+ *     1 .. keyboard sends multimedia keys
+ */
+
+#define FNMODE_PATH "/sys/module/hid_apple/parameters/fnmode"
+#define APPLE_VENDOR 0x5ac
+#define APPLE_KEYBOARD 0x220
+
+static Atom prop_fnmode;
+static Bool fnmode_readonly; /* set if we can only read fnmode */
+
+
+/**
+ * @retval 0 fnmode is set to function keys
+ * @retval 1 fnmode is set to multimedia keys
+ * @retval -1 Error, see errno
+ */
+static int
+get_fnmode(void)
+{
+    int fd;
+    char retvalue;
+
+    fd = open(FNMODE_PATH, O_RDWR);
+    if (fd < 0)
+    {
+        if (errno == EACCES)
+        {
+            fnmode_readonly = TRUE;
+            fd = open(FNMODE_PATH, O_RDONLY);
+        }
+    }
+
+    if (fd < 0)
+        return -1;
+
+    if (read(fd, &retvalue, 1) != 1)
+        return -1;
+
+    close(fd);
+
+    if (retvalue != '0' && retvalue != '1')
+    {
+        xf86Msg(X_ERROR, "Invalid fnmode value: %c\n", retvalue);
+        errno = EINVAL;
+        return -1;
+    }
+
+    return retvalue == '1';
+}
+
+/**
+ * @param fnmode 0 for function keys,  1 for multimedia keys
+ * @return 0 on success, -1 otherwise (check errno)
+ */
+static int
+set_fnmode(Bool fnmode)
+{
+    int fd;
+    char mode;
+
+    fd = open(FNMODE_PATH, O_WRONLY);
+    if (fd < 0)
+        return -1;
+
+    mode = fnmode ? '1' : '0';
+
+    if (write(fd, &mode, 1) != 1)
+        return -1;
+
+    close(fd);
+
+    return 0;
+}
+
+static int
+EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom,
+                      XIPropertyValuePtr val, BOOL checkonly)
+{
+    if (atom == prop_fnmode)
+    {
+        if (val->format != 8 || val->type != XA_INTEGER)
+            return BadMatch;
+
+        if (fnmode_readonly)
+            return BadAccess;
+
+        if (!checkonly)
+            set_fnmode(*(CARD8*)val->data);
+    }
+
+    return Success;
+}
+
+void
+EvdevAppleInitProperty(DeviceIntPtr dev)
+{
+    InputInfoPtr pInfo  = dev->public.devicePrivate;
+    EvdevPtr     pEvdev = pInfo->private;
+    char fnmode;
+
+    if (pEvdev->id_vendor != APPLE_VENDOR ||
+        pEvdev->id_product != APPLE_KEYBOARD)
+        return;
+
+    fnmode = get_fnmode();
+    if (fnmode == -1)
+    {
+        xf86IDrvMsg(pInfo, X_ERROR, "Failed to get fnmode (%s)\n", strerror(errno));
+        return;
+    }
+
+    prop_fnmode = MakeAtom(EVDEV_PROP_APPLE_FNMODE, strlen(EVDEV_PROP_APPLE_FNMODE), TRUE);
+    XIChangeDeviceProperty(dev, prop_fnmode, XA_INTEGER, 8,
+                           PropModeReplace, 1, &fnmode, FALSE);
+    XISetDevicePropertyDeletable(dev, prop_fnmode, FALSE);
+    XIRegisterPropertyHandler(dev, EvdevAppleSetProperty, NULL, NULL);
+}
diff --git a/src/evdev.c b/src/evdev.c
index d93adb4..38868c4 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1359,6 +1359,7 @@ EvdevInit(DeviceIntPtr device)
     Evdev3BEmuInitProperty(device);
     EvdevWheelEmuInitProperty(device);
     EvdevDragLockInitProperty(device);
+    EvdevAppleInitProperty(device);
 
     return Success;
 }
diff --git a/src/evdev.h b/src/evdev.h
index 1741e59..6434c3f 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -243,4 +243,5 @@ void EvdevMBEmuInitProperty(DeviceIntPtr);
 void Evdev3BEmuInitProperty(DeviceIntPtr);
 void EvdevWheelEmuInitProperty(DeviceIntPtr);
 void EvdevDragLockInitProperty(DeviceIntPtr);
+void EvdevAppleInitProperty(DeviceIntPtr);
 #endif
-- 
1.7.4.4



More information about the xorg-devel mailing list