Linux kernels starting with version number 3.2 are supposed to have
the LED extensions included in the wacom driver. This commit provides
a first demonstration on how the new interface might be exposed by
means of xsetwacom.

The basic mechanism for passing the Button images between the
xsetwacom tool and the input driver is a new Property called
"Wacom Button Image". This property is a list of eight Pixmap IDs.
When XChangeProperty is called with an array of eight Pixmap IDs,
the Button Image for every ID which is not 0 is sent to the tablet.

The Pixmap properties are very restrictive:
* width 64 pixels
* height 32 pixels
* depth 32bit
If the restrictions are not met, the button image is not set.
E.g. when not using an X Server with TrueColor mode it will not
work.

Summary of changes:
* Added new src/wcmLED.c module which contains
        * init function: wcmLedStart
                called by wcmUSB.c:wcmLedStart()
                This tries to locate the wacom_led interface in sysfs using
                libudev. When located, the pathname ist stored in the
                sysfs_led member of WacomDevicePtr.
        * destroy function: wcmLedFree
                called by wcmConfig.c:wcmFree()
                Frees the sysfs_led string.
        * button image set function: wcmSetOLEDProperty
                called by wcmXCommand.c:wcmSetProperty()

* Added set_led_image function to xsetwacom.c.
        Contains a very simple text renderer using XDrawText.

* Added further fake symbols for "make check"

Usage Example:
Set Button text of Button 3 to 'example'
> xsetwacom --set 'Wacom Intuos4 8x13 pad' Image 3 'example'

TODO:
* Add function for setting luminance
* Add ability to read images from files
* Add ability for more sophisticated "text layout"
* Add support for non TrueColor mode X Server

Signed-off-by: Eduard Hasenleithner <edu...@hasenleithner.at>
---
 include/wacom-properties.h |    7 ++
 man/xsetwacom.man          |    4 +
 src/common.mk              |    1 +
 src/wcmConfig.c            |    1 +
 src/wcmLED.c               |  158 ++++++++++++++++++++++++++++++++++++++++++++
 src/wcmUSB.c               |    3 +
 src/wcmXCommand.c          |    6 ++
 src/xf86Wacom.h            |    6 ++
 src/xf86WacomDefs.h        |    8 ++
 test/fake-symbols.c        |   10 +++
 test/fake-symbols.h        |   14 ++++
 tools/xsetwacom.c          |   57 ++++++++++++++++-
 12 files changed, 274 insertions(+), 1 deletions(-)
 create mode 100644 src/wcmLED.c

diff --git a/include/wacom-properties.h b/include/wacom-properties.h
index 29d5056..8f88840 100644
--- a/include/wacom-properties.h
+++ b/include/wacom-properties.h
@@ -95,6 +95,13 @@
  */
 #define WACOM_PROP_BUTTON_ACTIONS "Wacom Button Actions"
 
+/* Pixmap, array of 8 Button Image IDs (PIXMAP IDs)
+   When calling XChangeProperty, only the Button Images to
+   be updated shall be contained in the array, the other values
+   shall be passed as 0 values.
+ */
+#define WACOM_PROP_BUTTON_IMAGE "Wacom Button Image"
+
 /* 8 bit, 2 values, priv->debugLevel and common->debugLevel. This property
  * is for use in the driver only and only enabled if --enable-debug is
  * given. No client may rely on this property being present or working.
diff --git a/man/xsetwacom.man b/man/xsetwacom.man
index 539f405..e43e7da 100644
--- a/man/xsetwacom.man
+++ b/man/xsetwacom.man
@@ -204,6 +204,10 @@ tip, eraser, or touch.  The pressure levels of all tablets 
are normalized to
 parameter is independent of the PressureCurve parameter.  Default:  27,
 range of 0 to 2047.
 .TP
+\fBImage\fR button-id text
+Set the button image for button-id (0-7) to the specified text.
+Only available on the Intuos4 M, L, or XL tablets.
+.TP
 \fBToolDebugLevel\fR level
 Set the debug level for this tool to the given level. This only affects
 code paths that are specific to a given tool. A higher level means more
diff --git a/src/common.mk b/src/common.mk
index fae8d9f..464e4bd 100644
--- a/src/common.mk
+++ b/src/common.mk
@@ -10,6 +10,7 @@ DRIVER_SOURCES= \
        $(top_srcdir)/src/wcmFilter.h \
        $(top_srcdir)/src/xf86WacomDefs.h \
        $(top_srcdir)/src/wcmUSB.c \
+       $(top_srcdir)/src/wcmLED.c \
        $(top_srcdir)/src/wcmXCommand.c \
        $(top_srcdir)/src/wcmValidateDevice.c \
        $(top_srcdir)/src/wcmTouchFilter.c
diff --git a/src/wcmConfig.c b/src/wcmConfig.c
index 22f162a..ff18b63 100644
--- a/src/wcmConfig.c
+++ b/src/wcmConfig.c
@@ -125,6 +125,7 @@ static void wcmFree(InputInfoPtr pInfo)
        TimerFree(priv->serial_timer);
        free(priv->tool);
        wcmFreeCommon(&priv->common);
+       wcmLedFree(priv);
        free(priv);
 
        pInfo->private = NULL;
diff --git a/src/wcmLED.c b/src/wcmLED.c
new file mode 100644
index 0000000..bb62d40
--- /dev/null
+++ b/src/wcmLED.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2011 by Eduard Hasenleithner <edu...@hasenleithner.at>
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <servermd.h>
+#include <libudev.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "xf86Wacom.h"
+
+static const char wacom_led_path[] = "/wacom_led";
+
+void wcmLedStart(WacomDevicePtr priv)
+{
+       struct stat s;
+       struct udev *udev;
+       struct udev_device *device, *parent;
+
+       if (fstat(priv->pInfo->fd, &s) < 0)
+               return;
+
+       udev = udev_new();
+       device = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
+       if (device)
+       {
+               parent = udev_device_get_parent_with_subsystem_devtype(
+                       device, "usb", NULL);
+               if (parent)
+               {
+                       const char *sysfs_usbdev = 
udev_device_get_syspath(parent);
+                       char *tmp;
+
+                       Xasprintf(&tmp, "%s%s", sysfs_usbdev, wacom_led_path);
+                       strcpy(tmp, sysfs_usbdev);
+                       strcat(tmp, wacom_led_path);
+                       if (stat(tmp, &s)>=0 && S_ISDIR(s.st_mode))
+                               priv->sysfs_led = tmp;
+                       else
+                               free(tmp);
+               }
+               udev_device_unref(device);
+       }
+       udev_unref(udev);
+}
+
+void wcmLedFree(WacomDevicePtr priv)
+{
+       free(priv->sysfs_led);
+       priv->sysfs_led = NULL;
+}
+
+static void led_surface_put(char *sysfs_path, int id,
+       unsigned char *src, int stride)
+{
+       char *str;
+       unsigned char img[1024];
+       int x, y, i = 0;
+       int fd;
+
+       for (y = 0; y < 16; y++)
+       {
+               unsigned char *pixel1 = src + (1+2*y)*stride;
+               unsigned char *pixel2 = pixel1 + stride;
+
+               for (x = 0; x < 64; x++)
+               {
+                       int lum1, lum2;
+                       pixel1 -= 4;
+                       pixel2 -= 4;
+                       lum1 = pixel1[0]+pixel1[1]+pixel1[2];
+                       lum2 = pixel2[0]+pixel2[1]+pixel2[2];
+                       lum1 /= 3*16; lum2 /= 3*16;
+                       img[i++] = (lum1&0xf) | (lum2<<4);
+               }
+       }
+
+       Xasprintf(&str, "%s/button%d_rawimg", sysfs_path, id);
+       fd = open(str, O_WRONLY);
+       if (fd >= 0)
+       {
+               write(fd, img, sizeof(img));
+               close(fd);
+       }
+       free(str);
+}
+
+int wcmSetOLEDProperty(DeviceIntPtr dev, Atom property,
+                      XIPropertyValuePtr prop, BOOL checkonly)
+{
+       InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+       WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+       DrawablePtr pDraw;
+       void *img;
+       int i, rc;
+       int stride;
+       int img_size;
+       XID *pixmap = prop->data;
+
+       if (!serverClient)
+               return BadImplementation;
+       if (!priv->sysfs_led)
+               return BadAtom;
+       if (prop->type != XA_PIXMAP || prop->format != 32)
+               return BadPixmap;
+       if (prop->size > 8)
+               return BadLength;
+
+       for (i = 0; i < prop->size; i++)
+       {
+               if (!pixmap[i])
+                       continue;
+               rc = dixLookupDrawable(&pDraw, pixmap[i], serverClient, 0, 0);
+               if (rc != Success)
+                       return rc;
+               if (pDraw->width != 64 || pDraw->height != 32)
+                       return BadValue;
+               if (BitsPerPixel(pDraw->depth) != 32)
+                       return BadValue;
+       }
+       if (checkonly)
+               return Success;
+
+       stride = PixmapBytePad(pDraw->width, pDraw->depth);
+       img_size = stride * pDraw->height;
+       img = malloc(img_size);
+       for (i = 0; i < prop->size; i++)
+       {
+               if (!pixmap[i])
+                       continue;
+
+               dixLookupDrawable(&pDraw, pixmap[i], serverClient, 0, 0);
+               pDraw->pScreen->GetImage(pDraw, 0, 0, 64, 32, ZPixmap, -1, img);
+               led_surface_put(priv->sysfs_led, i, img, stride);
+       }
+       free(img);
+       return Success;
+}
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmUSB.c b/src/wcmUSB.c
index 14de990..7dd2908 100644
--- a/src/wcmUSB.c
+++ b/src/wcmUSB.c
@@ -133,6 +133,7 @@ static int
 usbStart(InputInfoPtr pInfo)
 {
        int err;
+       WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
 
 #ifdef EVIOCGRAB
        /* Try to grab the event device so that data don't leak to 
/dev/input/mice */
@@ -144,6 +145,8 @@ usbStart(InputInfoPtr pInfo)
                xf86Msg(X_ERROR, "%s: Wacom X driver can't grab event device 
(%s)\n",
                                pInfo->name, strerror(errno));
 #endif
+
+       wcmLedStart(priv);
        return Success;
 }
 
diff --git a/src/wcmXCommand.c b/src/wcmXCommand.c
index fe71bd1..af13e6c 100644
--- a/src/wcmXCommand.c
+++ b/src/wcmXCommand.c
@@ -97,6 +97,7 @@ Atom prop_gesture_param;
 Atom prop_hover;
 Atom prop_tooltype;
 Atom prop_btnactions;
+Atom prop_btn_img;
 Atom prop_product_id;
 #ifdef DEBUG
 Atom prop_debuglevels;
@@ -220,6 +221,8 @@ void InitWcmDeviceProperties(InputInfoPtr pInfo)
        /* default to no actions */
        memset(values, 0, sizeof(values));
        prop_btnactions = InitWcmAtom(pInfo->dev, WACOM_PROP_BUTTON_ACTIONS, 
-32, WCM_MAX_MOUSE_BUTTONS, values);
+       prop_btn_img = InitWcmAtom(pInfo->dev, WACOM_PROP_BUTTON_IMAGE, -32,
+               WCM_MAX_LED_IMAGES, values);
 
        if (IsPad(priv)) {
                memset(values, 0, sizeof(values));
@@ -828,6 +831,9 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, 
XIPropertyValuePtr prop,
                if (prop->size != WCM_MAX_MOUSE_BUTTONS)
                        return BadMatch;
                wcmSetPropertyButtonActions(dev, property, prop, checkonly);
+       } else if (property == prop_btn_img)
+       {
+               wcmSetOLEDProperty(dev, property, prop, checkonly);
        } else
                wcmSetActionProperties(dev, property, prop, checkonly);
 
diff --git a/src/xf86Wacom.h b/src/xf86Wacom.h
index 60353dc..15d260b 100644
--- a/src/xf86Wacom.h
+++ b/src/xf86Wacom.h
@@ -185,6 +185,12 @@ enum WacomSuppressMode {
        SUPPRESS_NON_MOTION     /* Supress all events but x/y motion */
 };
 
+/* LED functions */
+extern void wcmLedStart(WacomDevicePtr priv);
+extern void wcmLedFree(WacomDevicePtr priv);
+int wcmSetOLEDProperty(DeviceIntPtr dev, Atom property,
+                      XIPropertyValuePtr prop, BOOL checkonly);
+
 /****************************************************************************/
 #endif /* __XF86WACOM_H */
 
diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h
index 94eee2e..51cde9c 100644
--- a/src/xf86WacomDefs.h
+++ b/src/xf86WacomDefs.h
@@ -213,6 +213,8 @@ struct _WacomModel
                                         * tablet buttons besides the strips are
                                         * treated as buttons */
 
+#define WCM_MAX_LED_IMAGES 8
+
 /* get/set/property */
 typedef struct _PROPINFO PROPINFO;
 
@@ -320,6 +322,12 @@ struct _WacomDeviceRec
        Atom strip_actions[4];
 
        OsTimerPtr serial_timer; /* timer used for serial number property 
update */
+
+       /* path to the sysfs directory for (O)LED control;
+        * NULL means that the sysfs interface is not present
+        * (either due to missing driver,
+        *   or device has no (O)LEDs) */
+       char *sysfs_led;
 };
 
 /******************************************************************************
diff --git a/test/fake-symbols.c b/test/fake-symbols.c
index bba10e4..b33632f 100644
--- a/test/fake-symbols.c
+++ b/test/fake-symbols.c
@@ -461,3 +461,13 @@ void
 xf86UnblockSIGIO (int wasset)
 {
 }
+
+int
+dixLookupDrawable(DrawablePtr *pDraw, XID id, ClientPtr client,
+                 Mask type, Mask access)
+{
+       return 0;
+}
+
+extern PaddingInfo PixmapWidthPaddingInfo[];
+PaddingInfo PixmapWidthPaddingInfo[33];
diff --git a/test/fake-symbols.h b/test/fake-symbols.h
index dd9500a..b922548 100644
--- a/test/fake-symbols.h
+++ b/test/fake-symbols.h
@@ -184,3 +184,17 @@ extern void TimerFree(OsTimerPtr timer);
 
 extern int xf86BlockSIGIO (void);
 extern void xf86UnblockSIGIO (int wasset);
+
+extern int
+dixLookupDrawable(DrawablePtr *pDraw, XID id, ClientPtr client,
+                 Mask type, Mask access);
+
+typedef struct _PaddingInfo {
+       int     padRoundUp;     /* pixels per pad unit - 1 */
+       int     padPixelsLog2;  /* log 2 (pixels per pad unit) */
+       int     padBytesLog2;   /* log 2 (bytes per pad unit) */
+       int     notPower2;      /* bitsPerPixel not a power of 2 */
+       int     bytesPerPixel;  /* only set when notPower2 is TRUE */
+       int     bitsPerPixel;   /* bits per pixel */
+} PaddingInfo;
+extern PaddingInfo PixmapWidthPaddingInfo[];
diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c
index 9d47ea6..7e8284b 100644
--- a/tools/xsetwacom.c
+++ b/tools/xsetwacom.c
@@ -111,6 +111,7 @@ static void set_xydefault(Display *dpy, XDevice *dev, 
param_t *param, int argc,
 static void get_all(Display *dpy, XDevice *dev, param_t *param, int argc, char 
**argv);
 static void get_param(Display *dpy, XDevice *dev, param_t *param, int argc, 
char **argv);
 static void set_output(Display *dpy, XDevice *dev, param_t *param, int argc, 
char **argv);
+static void set_led_image(Display *dpy, XDevice *dev, param_t *param, int 
argc, char **argv);
 
 /* NOTE: When removing or changing a parameter name, add to
  * deprecated_parameters.
@@ -412,6 +413,12 @@ static param_t parameters[] =
                .prop_flags = PROP_FLAG_WRITEONLY | PROP_FLAG_OUTPUT,
        },
        {
+               .name = "Image",
+               .desc = "Set the button image. ",
+               .set_func = set_led_image,
+               .prop_flags = PROP_FLAG_WRITEONLY,
+       },
+       {
                .name = "all",
                .desc = "Get value for all parameters. ",
                .get_func = get_all,
@@ -2129,6 +2136,54 @@ static void set_output(Display *dpy, XDevice *dev, 
param_t *param, int argc, cha
                set_output_xrandr(dpy, dev, param, argc, argv);
 }
 
+static void set_led_image(Display *dpy, XDevice *dev, param_t *param, int 
argc, char **argv)
+{
+       int id, w, h, range, i;
+       Atom prop = None;
+       GC gc;
+       XGCValues values;
+       int index;
+       long array[8];
+       Pixmap pixmap;
+       Bool success;
+
+       prop = XInternAtom(dpy, WACOM_PROP_BUTTON_IMAGE, True);
+       if (!prop)
+       {
+               fprintf(stderr, "WACOM_PROP_BUTTON_IMAGE not available\n");
+               return;
+       }
+
+       if (argc < 2)
+       {
+               fprintf(stderr, "too few arguments\n");
+               return;
+       }
+
+       success = convert_value_from_user(param, argv[0], &index);
+       if (!success || index < 0 || index >= 8)
+       {
+               fprintf(stderr, "'%s' is not a valid image index\n", argv[0]);
+               return;
+       }
+
+       values.foreground = BlackPixel(dpy, 0);
+       values.background = BlackPixel(dpy, 0);
+       gc = XCreateGC(dpy, DefaultRootWindow(dpy),
+               (GCForeground|GCBackground), &values);
+
+       pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
+                              64, 32, DefaultDepth(dpy, 0));
+       XFillRectangle(dpy, pixmap, gc, 0, 0, 64, 32);
+       XSetForeground(dpy, gc, WhitePixel(dpy, 0));
+       XDrawString(dpy, pixmap, gc, 0, 16, argv[1], strlen(argv[1]));
+
+       memset(array, 0, sizeof array);
+       array[index] = pixmap;
+       XChangeDeviceProperty(dpy, dev, prop, XA_PIXMAP, 32, PropModeReplace,
+                             (char*)array, 8);
+}
+
 
 static void get_all(Display *dpy, XDevice *dev, param_t *param, int argc, char 
**argv)
 {
@@ -2448,7 +2503,7 @@ static void test_parameter_number(void)
         * deprecated them.
         * Numbers include trailing NULL entry.
         */
-       assert(ArrayLength(parameters) == 34);
+       assert(ArrayLength(parameters) == 35);
        assert(ArrayLength(deprecated_parameters) == 17);
 }
 
-- 
1.7.5.4


------------------------------------------------------------------------------
All the data continuously generated in your IT infrastructure contains a
definitive record of customers, application performance, security
threats, fraudulent activity and more. Splunk takes this data and makes
sense of it. Business sense. IT sense. Common sense.
http://p.sf.net/sfu/splunk-d2dcopy1
_______________________________________________
Linuxwacom-devel mailing list
Linuxwacom-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel

Reply via email to