The LED extensions in the wacom kernel driver are scheduled for
inclusion into linux-3.2. This commit provides a first
demonstration on how the OLED part of new interface might be
exposed by new wacom xinput device properties.
The basic mechanism for passing the Button images between the
xsetwacom tool and the input driver is a new Property called
"Wacom OLED Pixmap". This property is a list of Pixmap IDs,
one for each OLED display on the tablet. Upon call of
XChangeDeviceProperty the OLED displays are updated according
to the contents of the Pixmaps.
Summary of changes:
* Added new src/wcmLED.c module which contains
* init function: wcmLedInit
called by wcmXCommand.c:InitWcmDeviceProperties()
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_oled_image function to xsetwacom.c.
Contains a very simple text renderer using XDrawString.
* 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 ability to read images from files
* Add ability for more sophisticated "text layout"
* Add support for non TrueColor mode X Server
* Add function for setting luminance
Signed-off-by: Eduard Hasenleithner <[email protected]>
---
include/wacom-properties.h | 14 +++
man/xsetwacom.man | 4 +
src/common.mk | 1 +
src/wcmConfig.c | 1 +
src/wcmLED.c | 235 ++++++++++++++++++++++++++++++++++++++++++++
src/wcmXCommand.c | 14 +++
src/xf86Wacom.h | 6 +
src/xf86WacomDefs.h | 13 +++
test/fake-symbols.c | 23 +++++
tools/xsetwacom.c | 78 ++++++++++++++-
10 files changed, 388 insertions(+), 1 deletions(-)
create mode 100644 src/wcmLED.c
diff --git a/include/wacom-properties.h b/include/wacom-properties.h
index 29d5056..5d8ad1c 100644
--- a/include/wacom-properties.h
+++ b/include/wacom-properties.h
@@ -95,6 +95,20 @@
*/
#define WACOM_PROP_BUTTON_ACTIONS "Wacom Button Actions"
+/* Pixmap, array of Button Pixmap IDs
+ * This property has one Pixmap ID per OLED display on the tablet
+ * The image size is fixed to 64x32 pixels.
+ * Initially, all IDs are zero and their image is blank.
+ * Writing this property causes the OLED displays to be updated with
+ * the content of the pixmaps.
+ * To enable reading of pixmaps set by previous property modifications,
+ * applications which want to change a not yet allocated pixmap
+ * shall:
+ * * allocate said pixmap with 64x32 pixels, and
+ * * invoke XSetCloseDownMode with close_mode=RetainPermanent.
+ */
+#define WACOM_PROP_OLED_PIXMAP "Wacom OLED Pixmap"
+
/* 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 dc0995f..9d2c9d2 100644
--- a/man/xsetwacom.man
+++ b/man/xsetwacom.man
@@ -208,6 +208,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 94b188d..bd101dc 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..5dd944a
--- /dev/null
+++ b/src/wcmLED.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2011 by Eduard Hasenleithner <[email protected]>
+ *
+ * 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";
+
+/**
+ * Check the presence of the 'wacom_led' sysfs attribute group,
+ * and store the path in priv->sysfs_led if it is present
+ */
+static void wcmLedSyspathInit(WacomDevicePtr priv)
+{
+ struct udev *udev;
+ struct udev_device *device, *parent;
+
+ udev = udev_new();
+ device = udev_device_new_from_devnum(udev, 'c', priv->common->min_maj);
+ if (device)
+ {
+ parent = udev_device_get_parent_with_subsystem_devtype(device,
"usb", NULL);
+ if (parent)
+ {
+ struct stat s;
+ 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);
+}
+
+/**
+ * Check the presence of the sysfs attribute for a single OLED display
+ * This is function is used by wcmLedInit to count the number of
+ * available OLED displays
+ * @param priv pointer to device structure
+ * @param id id of oled display to check presence
+ * @return presence status (True or False)
+ */
+static int oled_presence(WacomDevicePtr priv, int id)
+{
+ struct stat s;
+ int r;
+ char *tmp;
+
+ r = Xasprintf(&tmp, "%s/button%d_rawimg", priv->sysfs_led, id);
+ if (r >= 0)
+ {
+ r = stat(tmp, &s);
+ free(tmp);
+ }
+ return r >= 0 && S_ISREG(s.st_mode);
+}
+
+static int oled_raw_put(WacomDevicePtr priv, uint8_t *rawimg, int id)
+{
+ int r = -1;
+ int fd;
+
+ if (!priv->oled || id >= priv->oled->count)
+ return -1;
+
+ if (memcmp(priv->oled->buf[id], rawimg, WCM_OLED_RAW_SIZE))
+ {
+ char *str = NULL;
+ Xasprintf(&str, "%s/button%d_rawimg", priv->sysfs_led, id);
+ fd = open(str, O_WRONLY);
+ free(str);
+ if (fd >= 0)
+ {
+ r = write(fd, rawimg, WCM_OLED_RAW_SIZE);
+ close(fd);
+ if (r >= 0)
+ memcpy(priv->oled->buf[id], rawimg,
WCM_OLED_RAW_SIZE);
+ }
+ } else
+ {
+ /* Pixmap did not visibly change */
+ r = 0;
+ }
+
+ return r;
+}
+
+/**
+ * Convert a 64x32 24 bit image to a 4 bit OLED image
+ * @param dst 4 bit OLED destination image
+ * @param src 24 bit source image with 32 bit/pixel
+ * @param stride number of bytes for each line
+ */
+static void oled_image_convert(uint8_t *dst, unsigned char *src, int stride)
+{
+ int x, y, i = 0;
+
+ 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;
+ dst[i++] = (lum1&0xf) | (lum2<<4);
+ }
+ }
+}
+
+int wcmSetOLEDProperty(DeviceIntPtr dev, Atom property,
+ XIPropertyValuePtr pixmaps, 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 = pixmaps->data;
+
+ if (!serverClient || !priv->sysfs_led || !priv->oled)
+ return BadImplementation;
+ if (pixmaps->type != XA_PIXMAP || pixmaps->format != 32)
+ return BadPixmap;
+ if (pixmaps->size != priv->oled->count)
+ return BadLength;
+
+ for (i = 0; i < pixmaps->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 BadPixmap;
+ if (BitsPerPixel(pDraw->depth) != 32)
+ return BadPixmap;
+ }
+ if (checkonly)
+ return Success;
+
+ stride = PixmapBytePad(pDraw->width, pDraw->depth);
+ img_size = stride * pDraw->height;
+ img = malloc(img_size);
+ for (i = 0; i < pixmaps->size; i++)
+ {
+ uint8_t rawimg[WCM_OLED_RAW_SIZE];
+
+ if (pixmap[i])
+ {
+ dixLookupDrawable(&pDraw, pixmap[i], serverClient, 0,
0);
+ pDraw->pScreen->GetImage(pDraw, 0, 0, 64, 32, ZPixmap,
-1, img);
+ oled_image_convert(rawimg, img, stride);
+ } else
+ memset(rawimg, 0, sizeof(rawimg));
+ oled_raw_put(priv, rawimg, i);
+ }
+ free(img);
+ return Success;
+}
+
+void wcmLedInit(WacomDevicePtr priv)
+{
+ wcmLedSyspathInit(priv);
+ if (priv->sysfs_led)
+ {
+ int count = 0;
+ while (oled_presence(priv, count))
+ count++;
+ if (count > 0)
+ {
+ /* initialize OLED displays */
+ int i;
+ uint8_t blank_raw[WCM_OLED_RAW_SIZE];
+
+ priv->oled = malloc(sizeof(*priv->oled) +
count*sizeof(*priv->oled->buf));
+ priv->oled->count = count;
+
+ memset(blank_raw, 0, sizeof(blank_raw));
+ for (i = 0; i < priv->oled->count; i++)
+ {
+ priv->oled->buf[i][0] = 1; /* force update */
+ oled_raw_put(priv, blank_raw, i);
+ }
+ }
+ }
+}
+
+void wcmLedFree(WacomDevicePtr priv)
+{
+ free(priv->sysfs_led);
+ priv->sysfs_led = NULL;
+ free(priv->oled);
+ priv->oled = NULL;
+}
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmXCommand.c b/src/wcmXCommand.c
index 82f9b80..537a2c0 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_oled_pixmap;
Atom prop_product_id;
#ifdef DEBUG
Atom prop_debuglevels;
@@ -229,6 +230,16 @@ void InitWcmDeviceProperties(InputInfoPtr pInfo)
memset(values, 0, sizeof(values));
prop_btnactions = InitWcmAtom(pInfo->dev, WACOM_PROP_BUTTON_ACTIONS,
XA_ATOM, 32, WCM_MAX_MOUSE_BUTTONS, values);
+ if (IsPad(priv))
+ {
+ wcmLedInit(priv);
+ if (priv->oled)
+ {
+ memset(values, 0, sizeof(values));
+ prop_oled_pixmap = InitWcmAtom(pInfo->dev,
WACOM_PROP_OLED_PIXMAP, XA_PIXMAP, 32, priv->oled->count, values);
+ }
+ }
+
if (IsPad(priv)) {
memset(values, 0, sizeof(values));
prop_strip_buttons = InitWcmAtom(pInfo->dev,
WACOM_PROP_STRIPBUTTONS, XA_ATOM, 32, 4, values);
@@ -836,6 +847,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_oled_pixmap)
+ {
+ wcmSetOLEDProperty(dev, property, prop, checkonly);
} else
wcmSetActionProperties(dev, property, prop, checkonly);
diff --git a/src/xf86Wacom.h b/src/xf86Wacom.h
index c1b55c9..ab02fb5 100644
--- a/src/xf86Wacom.h
+++ b/src/xf86Wacom.h
@@ -184,6 +184,12 @@ enum WacomSuppressMode {
SUPPRESS_NON_MOTION /* Supress all events but x/y motion */
};
+/* LED functions */
+extern void wcmLedInit(WacomDevicePtr priv);
+extern void wcmLedFree(WacomDevicePtr priv);
+extern int wcmSetOLEDProperty(DeviceIntPtr dev, Atom property,
+ XIPropertyValuePtr pixmaps, BOOL checkonly);
+
/****************************************************************************/
#endif /* __XF86WACOM_H */
diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h
index cf16648..29890f7 100644
--- a/src/xf86WacomDefs.h
+++ b/src/xf86WacomDefs.h
@@ -189,6 +189,8 @@ struct _WacomModel
* tablet buttons besides the strips are
* treated as buttons */
+#define WCM_OLED_RAW_SIZE 1024 /* size of raw oled image */
+
/* get/set/property */
typedef struct _PROPINFO PROPINFO;
@@ -296,6 +298,17 @@ 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;
+ struct oled_buffer
+ {
+ int count;
+ uint8_t buf[0][WCM_OLED_RAW_SIZE];
+ } *oled;
};
/******************************************************************************
diff --git a/test/fake-symbols.c b/test/fake-symbols.c
index bba10e4..4e354a2 100644
--- a/test/fake-symbols.c
+++ b/test/fake-symbols.c
@@ -1,4 +1,5 @@
#include "fake-symbols.h"
+#include <servermd.h>
_X_EXPORT
int xf86ReadSerial (int fd, void *buf, int count)
@@ -461,3 +462,25 @@ void
xf86UnblockSIGIO (int wasset)
{
}
+
+int
+dixLookupDrawable(DrawablePtr *pDraw, XID id, ClientPtr client,
+ Mask type, Mask access)
+{
+ return 0;
+}
+
+XID
+FakeClientID(int client)
+{
+ return 0;
+}
+
+Bool
+AddResource(XID id, RESTYPE type, pointer value)
+{
+ return FALSE;
+}
+
+PaddingInfo PixmapWidthPaddingInfo[33];
+ScreenInfo screenInfo;
diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c
index 99ca974..35037e8 100644
--- a/tools/xsetwacom.c
+++ b/tools/xsetwacom.c
@@ -110,6 +110,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_oled_image(Display *dpy, XDevice *dev, param_t *param, int
argc, char **argv);
/* NOTE: When removing or changing a parameter name, add to
* deprecated_parameters.
@@ -411,6 +412,12 @@ static param_t parameters[] =
.prop_flags = PROP_FLAG_WRITEONLY | PROP_FLAG_OUTPUT,
},
{
+ .name = "Image",
+ .desc = "Set the button image. ",
+ .set_func = set_oled_image,
+ .prop_flags = PROP_FLAG_WRITEONLY,
+ },
+ {
.name = "all",
.desc = "Get value for all parameters. ",
.get_func = get_all,
@@ -2350,6 +2357,75 @@ static void set_output(Display *dpy, XDevice *dev,
param_t *param, int argc, cha
fprintf(stderr, "Unable to find an output '%s'.\n", argv[0]);
}
+static void set_oled_image(Display *dpy, XDevice *dev, param_t *param, int
argc, char **argv)
+{
+ int id, w, h, range, i;
+ Atom oled_pixmap = None;
+ Atom type;
+ GC gc;
+ XGCValues values;
+ int index;
+ int format;
+ int r;
+ long pixmap_nitems;
+ long after;
+ long *pixmap_array;
+ Pixmap pixmap;
+ Bool success;
+
+ oled_pixmap = XInternAtom(dpy, WACOM_PROP_OLED_PIXMAP, True);
+ if (!oled_pixmap)
+ {
+ fprintf(stderr, "property '%s' not available\n",
WACOM_PROP_OLED_PIXMAP);
+ return;
+ }
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "too few arguments\n");
+ return;
+ }
+
+ /* Retrieve Pixmap array */
+ r = XGetDeviceProperty(dpy, dev, oled_pixmap, 0, 8, False,
+ XA_PIXMAP, &type, &format,
+ &pixmap_nitems, &after,
+ (unsigned char**)&pixmap_array);
+ if (r != Success || type != XA_PIXMAP || format != 32)
+ {
+ fprintf(stderr, "failed to set image\n");
+ return;
+ }
+
+ success = convert_value_from_user(param, argv[0], &index);
+ if (!success || index < 0 || index >= pixmap_nitems)
+ {
+ fprintf(stderr, "'%s' is not a valid image index\n", argv[0]);
+ return;
+ }
+
+ pixmap = pixmap_array[index];
+ if (!pixmap)
+ {
+ pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), 64, 32, 24);
+ XSetCloseDownMode(dpy, RetainPermanent);
+ pixmap_array[index] = pixmap;
+ }
+
+ values.foreground = BlackPixel(dpy, 0);
+ values.background = BlackPixel(dpy, 0);
+ gc = XCreateGC(dpy, pixmap, (GCForeground|GCBackground), &values);
+
+ 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]));
+
+ /* Write back Pixmap array */
+ XChangeDeviceProperty(dpy, dev, oled_pixmap, XA_PIXMAP, 32,
+ PropModeReplace,
+ (unsigned char*)pixmap_array, pixmap_nitems);
+}
+
static void get_all(Display *dpy, XDevice *dev, param_t *param, int argc, char
**argv)
{
@@ -2669,7 +2745,7 @@ static void test_parameter_number(void)
* deprecated them.
* Numbers include trailing NULL entry.
*/
- assert(ARRAY_SIZE(parameters) == 34);
+ assert(ARRAY_SIZE(parameters) == 35);
assert(ARRAY_SIZE(deprecated_parameters) == 17);
}
--
1.7.5.4
------------------------------------------------------------------------------
All of the data generated in your IT infrastructure is seriously valuable.
Why? It contains a definitive record of application performance, security
threats, fraudulent activity, and more. Splunk takes this data and makes
sense of it. IT sense. And common sense.
http://p.sf.net/sfu/splunk-d2dcopy2
_______________________________________________
Linuxwacom-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel