[gnome-settings-daemon/gnome-3-12] wacom: add Bluetooth OLED handling for Intuos4 WL



commit a1f98e5a4bd86f663239a6d824deba70774d3a11
Author: Przemo Firszt <przemo firszt eu>
Date:   Tue Mar 11 21:32:00 2014 +0000

    wacom: add Bluetooth OLED handling for Intuos4 WL
    
    That patch implements OLED handling over bluetooth for Intuos4 Wireless
    tablets. Due to the nature of the device which accepts 4 bit colour images over
    USB and only 1 bit colour images we have to handle those 2 situations
    differently.
    
    On top of that kernel side has been implemented in such way that
    the images for USB connection have to be scrambled in userland before they are
    send to the device.
    
    The decision to move scrambling to the OLED helper and to
    have 4-bit to 1-bit conversion in the same location is the cleanest solution
    that I found to the problem of inconsistent image formatting. It changes
    the previous approach to keep scrambling out of the helper which is executed
    with elevated privileges.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=724955

 plugins/wacom/Makefile.am                |    3 +-
 plugins/wacom/gsd-wacom-oled-constants.h |   40 +++++++
 plugins/wacom/gsd-wacom-oled-helper.c    |  174 ++++++++++++++++++++++++++----
 plugins/wacom/gsd-wacom-oled.c           |   27 +-----
 plugins/wacom/gsd-wacom-oled.h           |   10 +--
 5 files changed, 199 insertions(+), 55 deletions(-)
---
diff --git a/plugins/wacom/Makefile.am b/plugins/wacom/Makefile.am
index 01ee14a..6274e43 100644
--- a/plugins/wacom/Makefile.am
+++ b/plugins/wacom/Makefile.am
@@ -14,6 +14,7 @@ libgsdwacom_la_SOURCES =      \
        gsd-wacom-osd-window.c  \
        gsd-wacom-oled.h        \
        gsd-wacom-oled.c        \
+       gsd-wacom-oled-constants.h \
        gsd-wacom-device.c      \
        gsd-wacom-device.h      \
        gsd-wacom-resources.c
@@ -59,7 +60,7 @@ polkit_policy_DATA = $(polkit_policy_in_files:.policy.in=.policy)
 
 # so it always gets included in the tarball
 gsd_wacom_led_helper_SOURCES = gsd-wacom-led-helper.c
-gsd_wacom_oled_helper_SOURCES =        gsd-wacom-oled-helper.c
+gsd_wacom_oled_helper_SOURCES =        gsd-wacom-oled-helper.c gsd-wacom-oled-constants.h
 
 EXTRA_DIST = $(gsd_wacom_led_helper_SOURCES) wacom.gresource.xml tablet-layout.css \
             $(gsd_wacom_oled_helper_SOURCES)
diff --git a/plugins/wacom/gsd-wacom-oled-constants.h b/plugins/wacom/gsd-wacom-oled-constants.h
new file mode 100644
index 0000000..b779cd2
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-oled-constants.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Przemo Firszt <przemo firszt eu>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GSD_WACOM_OLED_CONSTANTS_H
+#define __GSD_WACOM_OLED_CONSTANTS_H
+
+G_BEGIN_DECLS
+
+typedef enum {
+       GSD_WACOM_OLED_TYPE_USB,
+       GSD_WACOM_OLED_TYPE_BLUETOOTH
+} GsdWacomOledType;
+
+/* OLED parameters */
+#define OLED_WIDTH             64                      /*Width of OLED icon - hardware dependent*/
+#define OLED_HEIGHT            32                      /*Height of OLED icon - hardware dependent*/
+#define LABEL_SIZE             30                      /*Maximum length of text for OLED icon*/
+#define MAX_TOKEN              (LABEL_SIZE >> 1)       /*Maximum number of tokens equals half of maximum 
number of characters*/
+#define MAX_IMAGE_SIZE         1024                    /*Size of buffer for storing OLED image*/
+#define MAX_1ST_LINE_LEN       10                      /*Maximum number of characters in 1st line of OLED 
icon*/
+
+G_END_DECLS
+
+#endif /* __GSD_WACOM_OLED_CONSTANTS_H */
diff --git a/plugins/wacom/gsd-wacom-oled-helper.c b/plugins/wacom/gsd-wacom-oled-helper.c
index 99be48a..945286d 100644
--- a/plugins/wacom/gsd-wacom-oled-helper.c
+++ b/plugins/wacom/gsd-wacom-oled-helper.c
@@ -34,8 +34,73 @@
 #include <fcntl.h>
 #include <gudev/gudev.h>
 
+#include "gsd-wacom-oled-constants.h"
+
+#define USB_PIXELS_PER_BYTE 2
+#define BT_PIXELS_PER_BYTE 8
+#define USB_BUF_LEN OLED_HEIGHT * OLED_WIDTH / USB_PIXELS_PER_BYTE
+#define BT_BUF_LEN OLED_WIDTH * OLED_HEIGHT / BT_PIXELS_PER_BYTE
+
+static void
+oled_scramble_icon (guchar *image)
+{
+       guchar buf[USB_BUF_LEN];
+       int x, y, i;
+       guchar l1, l2, h1, h2;
+
+       for (i = 0; i < USB_BUF_LEN; i++)
+               buf[i] = image[i];
+
+       for (y = 0; y < (OLED_HEIGHT / 2); y++) {
+               for (x = 0; x < (OLED_WIDTH / 2); x++) {
+                       l1 = (0x0F & (buf[OLED_HEIGHT - 1 - x + OLED_WIDTH * y]));
+                       l2 = (0x0F & (buf[OLED_HEIGHT - 1 - x + OLED_WIDTH * y] >> 4));
+                       h1 = (0xF0 & (buf[OLED_WIDTH - 1 - x + OLED_WIDTH * y] << 4));
+                       h2 = (0xF0 & (buf[OLED_WIDTH - 1 - x + OLED_WIDTH * y]));
+
+                       image[2 * x + OLED_WIDTH * y] = h1 | l1;
+                       image[2 * x + 1 + OLED_WIDTH * y] = h2 | l2;
+               }
+       }
+}
+
+static int
+gsd_wacom_oled_prepare_buf (guchar *image, GsdWacomOledType type)
+{
+       guchar buf[BT_BUF_LEN];
+       guchar b0, b1, b2, b3, b4, b5, b6, b7;
+       int i;
+       int len = 0;
+
+       if (type == GSD_WACOM_OLED_TYPE_USB) {
+               /* Image has to be scrambled for devices connected over USB ... */
+               oled_scramble_icon (image);
+               len = USB_BUF_LEN;
+       } else if (type == GSD_WACOM_OLED_TYPE_BLUETOOTH) {
+               /* ... but for bluetooth it has to be converted to 1 bit colour instead of scrambling */
+               for (i = 0; i < BT_BUF_LEN; i++) {
+                       b0 = 0b10000000 & (image[(4 * i) + 0] >> 0);
+                       b1 = 0b01000000 & (image[(4 * i) + 0] << 3);
+                       b2 = 0b00100000 & (image[(4 * i) + 1] >> 2);
+                       b3 = 0b00010000 & (image[(4 * i) + 1] << 1);
+                       b4 = 0b00001000 & (image[(4 * i) + 2] >> 4);
+                       b5 = 0b00000100 & (image[(4 * i) + 2] >> 1);
+                       b6 = 0b00000010 & (image[(4 * i) + 3] >> 6);
+                       b7 = 0b00000001 & (image[(4 * i) + 3] >> 3);
+                       buf[i] = b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7;
+               }
+               for (i = 0; i < BT_BUF_LEN; i++)
+                       image[i] = buf[i];
+               len = BT_BUF_LEN;
+       } else {
+               g_assert_not_reached ();
+       }
+
+       return len;
+}
+
 static gboolean
-gsd_wacom_oled_helper_write (const gchar *filename, gchar *buffer, GError **error)
+gsd_wacom_oled_helper_write (const gchar *filename, gchar *buffer, GsdWacomOledType type, GError **error)
 {
        guchar *image;
        gint retval;
@@ -46,19 +111,33 @@ gsd_wacom_oled_helper_write (const gchar *filename, gchar *buffer, GError **erro
        fd = open (filename, O_WRONLY);
        if (fd < 0) {
                ret = FALSE;
-               g_set_error (error, 1, 0, "failed to open filename: %s", filename);
+               g_set_error (error, 1, 0, "Failed to open filename: %s", filename);
                goto out;
        }
 
        image = g_base64_decode (buffer, &length);
-       if (!image)
+       if (length != USB_BUF_LEN) {
+               ret = FALSE;
+               g_set_error (error, 1, 0, "Base64 buffer has length of %" G_GSIZE_FORMAT " (expected %i)", 
length, USB_BUF_LEN);
                goto out;
+       }
+       if (!image) {
+               ret = FALSE;
+               g_set_error (error, 1, 0, "Decoding base64 buffer failed");
+               goto out;
+       }
+
+       length = gsd_wacom_oled_prepare_buf (image, type);
+       if (!length) {
+               ret = FALSE;
+               g_set_error (error, 1, 0, "Invalid image buffer length");
+               goto out;
+       }
 
-       /* write to device file */
        retval = write (fd, image, length);
        if (retval != length) {
                ret = FALSE;
-               g_set_error (error, 1, 0, "writing to %s failed", filename);
+               g_set_error (error, 1, 0, "Writing to %s failed", filename);
        }
 
        g_free (image);
@@ -82,6 +161,48 @@ get_oled_sysfs_path (GUdevDevice *device,
        return filename;
 }
 
+static char *
+get_bt_oled_sysfs_path (GUdevDevice *device, int button_num)
+{
+       char *status;
+       char *filename;
+
+       status = g_strdup_printf ("/oled%i_img", button_num);
+       filename = g_build_filename (g_udev_device_get_sysfs_path (device), status, NULL);
+       g_free (status);
+       return filename;
+}
+
+static char *
+get_bt_oled_filename (GUdevClient *client, GUdevDevice *device, int button_num)
+{
+       GUdevDevice *hid_dev;
+       const char *dev_uniq;
+       GList *hid_list;
+       GList *element;
+       const char *dev_hid_uniq;
+       char *filename = NULL;
+
+       dev_uniq = g_udev_device_get_property (device, "UNIQ");
+
+       hid_list =  g_udev_client_query_by_subsystem (client, "hid");
+       element = g_list_first(hid_list);
+       while (element) {
+               hid_dev = (GUdevDevice*)element->data;
+               dev_hid_uniq = g_udev_device_get_property (hid_dev, "HID_UNIQ");
+               if (g_strrstr (dev_uniq, dev_hid_uniq)){
+                       filename = get_bt_oled_sysfs_path (hid_dev, button_num);
+                       g_object_unref (hid_dev);
+                       break;
+               }
+               g_object_unref (hid_dev);
+               element = g_list_next(element);
+       }
+       g_list_free(hid_list);
+       return filename;
+}
+
+
 int main (int argc, char **argv)
 {
        GOptionContext *context;
@@ -92,6 +213,7 @@ int main (int argc, char **argv)
        GError *error = NULL;
        const char * const subsystems[] = { "input", NULL };
        int ret = 1;
+       GsdWacomOledType type;
 
        char *path = NULL;
        char *buffer = "";
@@ -134,33 +256,45 @@ int main (int argc, char **argv)
        client = g_udev_client_new (subsystems);
        device = g_udev_client_query_by_device_file (client, path);
        if (device == NULL) {
-               g_debug ("Could not find device '%s' in udev database", path);
+               g_critical ("Could not find device '%s' in udev database", path);
                goto out;
        }
 
        if (g_udev_device_get_property_as_boolean (device, "ID_INPUT_TABLET") == FALSE &&
            g_udev_device_get_property_as_boolean (device, "ID_INPUT_TOUCHPAD") == FALSE) {
-               g_debug ("Device '%s' is not a Wacom tablet", path);
+               g_critical ("Device '%s' is not a Wacom tablet", path);
                goto out;
        }
 
-       if (g_strcmp0 (g_udev_device_get_property (device, "ID_BUS"), "usb") != 0) {
-               /* FIXME handle Bluetooth OLEDs too */
-               g_debug ("Non-USB OLEDs setting is not (yet) supported");
-               goto out;
-       }
+       if (g_strcmp0 (g_udev_device_get_property (device, "ID_BUS"), "usb") == 0) {
+               parent = g_udev_device_get_parent_with_subsystem (device, "usb", "usb_interface");
+               if (parent == NULL) {
+                       g_critical ("Could not find parent USB device for '%s'", path);
+                       goto out;
+               }
+               g_object_unref (device);
+               device = parent;
+
+               filename = get_oled_sysfs_path (device, button_num);
+               type = GSD_WACOM_OLED_TYPE_USB;
+       } else if (g_strrstr( g_udev_device_get_property (device, "DEVPATH"), "bluetooth")) {
+               parent = g_udev_device_get_parent (device);
+               if (parent == NULL) {
+                       g_critical ("Could not find parent device for '%s'", path);
+                       goto out;
+               }
+               g_object_unref (device);
+               device = parent;
 
-       parent = g_udev_device_get_parent_with_subsystem (device, "usb", "usb_interface");
-       if (parent == NULL) {
-               g_debug ("Could not find parent USB device for '%s'", path);
+               filename = get_bt_oled_filename (client, device, button_num);
+               type = GSD_WACOM_OLED_TYPE_BLUETOOTH;
+       } else {
+               g_critical ("Not an expected device: %s", path);
                goto out;
        }
-       g_object_unref (device);
-       device = parent;
 
-       filename = get_oled_sysfs_path (device, button_num);
-       if (gsd_wacom_oled_helper_write (filename, buffer, &error) == FALSE) {
-               g_debug ("Could not set OLED icon for '%s': %s", path, error->message);
+       if (gsd_wacom_oled_helper_write (filename, buffer, type, &error) == FALSE) {
+               g_critical ("Could not set OLED icon for '%s': %s", path, error->message);
                g_error_free (error);
                g_free (filename);
                goto out;
diff --git a/plugins/wacom/gsd-wacom-oled.c b/plugins/wacom/gsd-wacom-oled.c
index 5163568..004edcf 100644
--- a/plugins/wacom/gsd-wacom-oled.c
+++ b/plugins/wacom/gsd-wacom-oled.c
@@ -35,29 +35,6 @@
 #define ROTATION_KEY           "rotation"
 
 static void
-oled_scramble_icon (guchar* image)
-{
-       unsigned char buf[MAX_IMAGE_SIZE];
-       int x, y, i;
-       unsigned char l1, l2, h1, h2;
-
-       for (i = 0; i < MAX_IMAGE_SIZE; i++)
-               buf[i] = image[i];
-
-       for (y = 0; y < (OLED_HEIGHT / 2); y++) {
-               for (x = 0; x < (OLED_WIDTH / 2); x++) {
-                       l1 = (0x0F & (buf[OLED_HEIGHT - 1 - x + OLED_WIDTH * y]));
-                       l2 = (0x0F & (buf[OLED_HEIGHT - 1 - x + OLED_WIDTH * y] >> 4));
-                       h1 = (0xF0 & (buf[OLED_WIDTH - 1 - x + OLED_WIDTH * y] << 4));
-                       h2 = (0xF0 & (buf[OLED_WIDTH - 1 - x + OLED_WIDTH * y]));
-
-                       image[2 * x + OLED_WIDTH * y] = h1 | l1;
-                       image[2 * x + 1 + OLED_WIDTH * y] = h2 | l2;
-               }
-       }
-}
-
-static void
 oled_surface_to_image (guchar          *image,
                       cairo_surface_t *surface)
 {
@@ -220,7 +197,6 @@ gsd_wacom_oled_gdkpixbuf_to_base64 (GdkPixbuf *pixbuf)
                }
        }
 
-       oled_scramble_icon (image);
        base_string = g_base64_encode (image, MAX_IMAGE_SIZE);
        base64 = g_strconcat (MAGIC_BASE64, base_string, NULL);
        g_free (base_string);
@@ -233,13 +209,12 @@ static char *
 oled_encode_image (char             *label,
                    GsdWacomRotation  rotation)
 {
-       guchar *image;
+       unsigned char *image;
 
        image = g_malloc (MAX_IMAGE_SIZE);
 
        /* convert label to image */
        oled_render_text (label, image, rotation);
-       oled_scramble_icon (image);
 
        return (g_base64_encode (image, MAX_IMAGE_SIZE));
 }
diff --git a/plugins/wacom/gsd-wacom-oled.h b/plugins/wacom/gsd-wacom-oled.h
index 933355a..f71176a 100644
--- a/plugins/wacom/gsd-wacom-oled.h
+++ b/plugins/wacom/gsd-wacom-oled.h
@@ -17,19 +17,13 @@
  *
  */
 
+#include "gsd-wacom-oled-constants.h"
+
 #ifndef __GSD_WACOM_OLED_H
 #define __GSD_WACOM_OLED_H
 
 G_BEGIN_DECLS
 
-/* OLED parameters */
-#define OLED_WIDTH             64                      /*Width of OLED icon - hardware dependent*/
-#define OLED_HEIGHT            32                      /*Height of OLED icon - hardware dependent*/
-#define LABEL_SIZE             30                      /*Maximum length of text for OLED icon*/
-#define MAX_TOKEN              (LABEL_SIZE >> 1)       /*Maximum number of tokens equals half of maximum 
number of characters*/
-#define MAX_IMAGE_SIZE         1024                    /*Size of buffer for storing OLED image*/
-#define MAX_1ST_LINE_LEN       10                      /*Maximum number of characters in 1st line of OLED 
icon*/
-
 void set_oled (GsdWacomDevice *device, char *button_id, char *label);
 char *gsd_wacom_oled_gdkpixbuf_to_base64 (GdkPixbuf *pixbuf);
 


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