raster pushed a commit to branch master.

http://git.enlightenment.org/core/enlightenment.git/commit/?id=eab2a34ef3be828956b704bd5f7687cfa1748525

commit eab2a34ef3be828956b704bd5f7687cfa1748525
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
Date:   Thu Feb 6 13:53:27 2020 +0000

    backlight - add ddc support via libddcutil
    
    This adds ddc monitor control and glues it into the backlight system.
    A result of this is now backlgiht control gadgets work screen by
    screen and even on desktop monitors as well as on a laptop panel. If
    you now put a backlight gadget on a shelf on each screen... it will
    control THAT screen's backlight.
    
    This requires libddcutil to be installed. That will require i2c
    modules (i2c-dev specifically). This means that this is likely not
    going to do anything useful on bsd's... unless libddcutil happens to
    work there by chance.
    
    so install ddcutil/libddcutil. ensure it's in ld.so.conf so setuid
    root processes find it (as LD_LIBRARY_PATH won't help) and enjoy your
    new funcky per-screen backlight controls... :)
    
    @feature
---
 src/bin/e_backlight.c          | 116 ++++++--
 src/bin/e_system.c             |   3 +-
 src/bin/system/e_system.h      |   3 +
 src/bin/system/e_system_ddc.c  | 598 +++++++++++++++++++++++++++++++++++++++++
 src/bin/system/e_system_main.c |   2 +
 src/bin/system/meson.build     |   1 +
 6 files changed, 705 insertions(+), 18 deletions(-)

diff --git a/src/bin/e_backlight.c b/src/bin/e_backlight.c
index 8eb8faeda..5dd1daea5 100644
--- a/src/bin/e_backlight.c
+++ b/src/bin/e_backlight.c
@@ -39,11 +39,34 @@ _backlight_system_get_cb(void *data, const char *params)
      }
 }
 
+static void
+_backlight_system_ddc_get_cb(void *data, const char *params)
+{
+   char edid[257];
+   int id = -1, val = -1;
+   double fval;
+   Backlight_Device *bd = data;
+
+   if (!params) return;
+   if (sscanf(params, "%256s %i %i", edid, &id, &val) != 3) return;
+   if (!!strncmp(bd->edid, edid, strlen(edid))) return;
+   e_system_handler_del("ddc-val-get", _backlight_system_ddc_get_cb, bd);
+   fval = (double)val / 100.0;
+   if (fabs(fval - bd->val) >= DBL_EPSILON)
+     {
+        bd->val = fval;
+        ecore_event_add(E_EVENT_BACKLIGHT_CHANGE, NULL, NULL, NULL);
+     }
+}
+
 static void
 _backlight_devices_clear(void)
 {
+   const char *s;
    Backlight_Device *bd;
 
+   EINA_LIST_FREE(bl_devs, s)
+     eina_stringshare_del(s);
    EINA_LIST_FREE(_devices, bd)
      {
         eina_stringshare_del(bd->dev);
@@ -51,6 +74,7 @@ _backlight_devices_clear(void)
         eina_stringshare_del(bd->edid);
         if (bd->anim) ecore_animator_del(bd->anim);
         e_system_handler_del("bklight-val", _backlight_system_get_cb, bd);
+        e_system_handler_del("ddc-val-get", _backlight_system_ddc_get_cb, bd);
         free(bd);
      }
 }
@@ -170,7 +194,8 @@ _backlight_devices_device_set(Backlight_Device *bd, double 
val)
 #endif
    if (!strncmp(bd->dev, "ddc:", 4))
      {
-        // XXX: implement ddc support
+        e_system_send("ddc-val-set", "%s %i %i", bd->dev + 4, 0x10, 
(int)(bd->val * 100.0)); // backlight val in e_system_ddc.c
+        ecore_event_add(E_EVENT_BACKLIGHT_CHANGE, NULL, NULL, NULL);
      }
    else
      {
@@ -221,7 +246,8 @@ _backlight_devices_device_update(Backlight_Device *bd)
 #endif
    if (!strncmp(bd->dev, "ddc:", 4))
      {
-        // XXX: implement ddc support
+        e_system_handler_add("ddc-val-get", _backlight_system_ddc_get_cb, bd);
+        e_system_send("ddc-val-get", "%s %i", bd->dev + 4, 0x10); // backlight 
val in e_system_ddc.c
      }
    else
      {
@@ -262,6 +288,21 @@ _backlight_devices_screen_lid_get(void)
    return NULL;
 }
 
+static E_Randr2_Screen *
+_backlight_devices_screen_edid_get(const char *edid)
+{
+   Eina_List *l;
+   E_Randr2_Screen *sc;
+
+   if (!e_randr2) return NULL;
+   EINA_LIST_FOREACH(e_randr2->screens, l, sc)
+     {
+        if (!sc->info.edid) continue;
+        if (!strncmp(sc->info.edid, edid, strlen(edid))) return sc;
+     }
+   return NULL;
+}
+
 static Backlight_Device *
 _backlight_devices_edid_find(const char *edid)
 {
@@ -282,6 +323,7 @@ _backlight_devices_lid_register(const char *dev, Eina_Bool 
force)
    E_Randr2_Screen *sc = _backlight_devices_screen_lid_get();
    Backlight_Device *bd;
    if (!sc) return;
+   if (!sc->info.edid) return;
    bd = _backlight_devices_edid_find(sc->info.edid);
    if (!bd)
      {
@@ -302,23 +344,36 @@ _backlight_devices_lid_register(const char *dev, 
Eina_Bool force)
    eina_stringshare_replace(&(bd->dev), dev);
 }
 
+static void
+_backlight_devices_edid_register(const char *dev, const char *edid)
+{
+   E_Randr2_Screen *sc = _backlight_devices_screen_edid_get(edid);
+   Backlight_Device *bd;
+   if (!sc) return;
+   bd = _backlight_devices_edid_find(sc->info.edid);
+   if (!bd)
+     {
+        bd = calloc(1, sizeof(Backlight_Device));
+        if (!bd) return;
+        bd->edid = eina_stringshare_add(sc->info.edid);
+        bd->output = eina_stringshare_add(sc->info.name);
+        _devices = eina_list_append(_devices, bd);
+     }
+   if (bd->dev)
+     {
+        if (!strcmp(bd->dev, "randr")) return; // randr devices win
+     }
+   eina_stringshare_replace(&(bd->dev), dev);
+}
+
 static void
 _backlight_system_list_cb(void *data EINA_UNUSED, const char *params)
 {
    // params "dev flag dev flag ..."
-   const char *p = params, *s;
+   const char *p = params;
    char dev[1024], flag, devnum = 0;
 
    e_system_handler_del("bklight-list", _backlight_system_list_cb, NULL);
-   EINA_LIST_FREE(bl_devs, s)
-     eina_stringshare_del(s);
-#ifndef HAVE_WAYLAND_ONLY
-   if ((e_comp) && (e_comp->comp_type == E_PIXMAP_TYPE_X))
-     {
-        if (ecore_x_randr_output_backlight_available())
-          bl_devs = eina_list_append(bl_devs, eina_stringshare_add("randr"));
-     }
-#endif
    while ((p) && (*p))
      {
         if (sscanf(p, "%1023s", dev) == 1)
@@ -364,7 +419,30 @@ _backlight_system_list_cb(void *data EINA_UNUSED, const 
char *params)
 }
 
 static void
-_backlight_devices_probe(void)
+_backlight_system_ddc_list_cb(void *data EINA_UNUSED, const char *params)
+{
+   const char *p = params;
+   char dev[257], buf[343];
+
+   e_system_handler_del("ddc-list", _backlight_system_ddc_list_cb, NULL);
+   while ((p) && (*p))
+     {
+        if (sscanf(p, "%256s", dev) == 1)
+          {
+             p += strlen(dev);
+             snprintf(buf, sizeof(buf), "ddc:%s", dev);
+             bl_devs = eina_list_append
+               (bl_devs, eina_stringshare_add(buf));
+             _backlight_devices_edid_register(buf, dev);
+             if (*p != ' ') break;
+          }
+        else break;
+     }
+   _backlight_devices_pending_done();
+}
+
+static void
+_backlight_devices_probe(Eina_Bool initial)
 {
    _backlight_devices_clear();
 #ifndef HAVE_WAYLAND_ONLY
@@ -381,6 +459,7 @@ _backlight_devices_probe(void)
              Ecore_X_Randr_Output *out;
              int i, num = 0;
 
+             bl_devs = eina_list_append(bl_devs, 
eina_stringshare_add("randr"));
              out = ecore_x_randr_window_outputs_get(root, &num);
              if ((out) && (num > 0))
                {
@@ -417,8 +496,12 @@ _backlight_devices_probe(void)
    // to respond to the device listing later
    _devices_pending_ops++;
    e_system_handler_add("bklight-list", _backlight_system_list_cb, NULL);
-   e_system_send("bklight-refresh", NULL);
+   if (!initial) e_system_send("bklight-refresh", NULL);
    e_system_send("bklight-list", NULL);
+   _devices_pending_ops++;
+   e_system_handler_add("ddc-list", _backlight_system_ddc_list_cb, NULL);
+   if (!initial) e_system_send("ddc-refresh", NULL);
+   e_system_send("ddc-list", NULL);
    // XXXX: add ddc to e_syystem and query that too
 }
 
@@ -444,7 +527,7 @@ static void
 _cb_job_zone_change(void *data EINA_UNUSED)
 {
    zone_change_job = NULL;
-   _backlight_devices_probe();
+   _backlight_devices_probe(EINA_FALSE);
    e_backlight_update();
 }
 
@@ -460,7 +543,7 @@ EINTERN int
 e_backlight_init(void)
 {
    E_EVENT_BACKLIGHT_CHANGE = ecore_event_type_new();
-   _backlight_devices_probe();
+   _backlight_devices_probe(EINA_TRUE);
 #define H(ev, cb) \
    handlers = eina_list_append(handlers, \
                                ecore_event_handler_add(ev, cb, NULL));
@@ -546,7 +629,6 @@ e_backlight_level_set(E_Zone *zone, double val, double tim)
    if (zone->bl_mode == E_BACKLIGHT_MODE_NORMAL) tim = 0.5;
    else if (tim < 0.0) tim = e_config->backlight.transition;
 
-   // XXX: store in bl device
    E_FREE_FUNC(bd->anim, ecore_animator_del);
    bd->anim = ecore_animator_timeline_add(tim, _bl_anim, bd);
    bd->from_val = bl_now;
diff --git a/src/bin/e_system.c b/src/bin/e_system.c
index 1bb07268a..a3731090c 100644
--- a/src/bin/e_system.c
+++ b/src/bin/e_system.c
@@ -107,7 +107,8 @@ _system_message_read(void)
         if (del_count > 0)
           {
              eina_hash_del(_handlers, head->cmd, plist);
-             eina_hash_add(_handlers, head->cmd, list);
+             if (list)
+               eina_hash_add(_handlers, head->cmd, list);
           }
      }
    buf2 = eina_binbuf_new();
diff --git a/src/bin/system/e_system.h b/src/bin/system/e_system.h
index 5fc4b9639..be3ba4805 100644
--- a/src/bin/system/e_system.h
+++ b/src/bin/system/e_system.h
@@ -122,5 +122,8 @@ void e_system_l2ping_shutdown(void);
 void e_system_cpufreq_init(void);
 void e_system_cpufreq_shutdown(void);
 
+void e_system_ddc_init(void);
+void e_system_ddc_shutdown(void);
+
 #endif
 
diff --git a/src/bin/system/e_system_ddc.c b/src/bin/system/e_system_ddc.c
new file mode 100644
index 000000000..7290b7a2d
--- /dev/null
+++ b/src/bin/system/e_system_ddc.c
@@ -0,0 +1,598 @@
+#include "e_system.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct
+{
+   char *edid;
+   int screen;
+} Dev;
+
+typedef struct
+{
+   char *req, *params; // don't free - part of alloc for req struct at offset
+} Req;
+
+static Eina_Lock _devices_lock;
+static Eina_List *_devices = NULL;
+static Eina_List *_req = NULL;
+static Eina_Semaphore _worker_sem;
+
+//////////////////////////////////////////////////////////////////////////////
+// needed ddc types
+#define DDCA_EDID_MFG_ID_FIELD_SIZE 4
+#define DDCA_EDID_MODEL_NAME_FIELD_SIZE 14
+#define DDCA_EDID_SN_ASCII_FIELD_SIZE 14
+
+typedef int DDCA_Status;
+typedef void * DDCA_Display_Ref;
+typedef void * DDCA_Display_Handle;
+typedef uint8_t DDCA_Vcp_Feature_Code;
+
+typedef enum
+{
+   DDCA_IO_I2C,
+   DDCA_IO_ADL,
+   DDCA_IO_USB
+} DDCA_IO_Mode;
+
+typedef struct
+{
+   int iAdapterIndex;
+   int iDisplayIndex;
+} DDCA_Adlno;
+
+typedef struct
+{
+   DDCA_IO_Mode io_mode;
+   union {
+      int i2c_busno;
+      DDCA_Adlno adlno;
+      int hiddev_devno;
+   } path;
+} DDCA_IO_Path;
+
+typedef struct
+{
+   uint8_t major;
+   uint8_t minor;
+} DDCA_MCCS_Version_Spec;
+
+typedef struct
+{
+   char marker[4];
+   int dispno;
+   DDCA_IO_Path path;
+   int usb_bus;
+   int usb_device;
+   char mfg_id[DDCA_EDID_MFG_ID_FIELD_SIZE];
+   char model_name[DDCA_EDID_MODEL_NAME_FIELD_SIZE];
+   char sn[DDCA_EDID_SN_ASCII_FIELD_SIZE];
+   uint16_t product_code;
+   uint8_t edid_bytes[128];
+   DDCA_MCCS_Version_Spec vcp_version;
+   DDCA_Display_Ref dref;
+} DDCA_Display_Info;
+
+typedef struct
+{
+   int ct;
+   DDCA_Display_Info info[];
+} DDCA_Display_Info_List;
+
+typedef struct
+{
+   uint8_t mh;
+   uint8_t ml;
+   uint8_t sh;
+   uint8_t sl;
+} DDCA_Non_Table_Vcp_Value;
+
+//////////////////////////////////////////////////////////////////////////////
+// ddc feature codes we plan to support
+// 0x12 contrast (0->100, 75)
+// 0x16 video gain r (0->100, 50)
+// 0x18 video gain g (0->100, 50)
+// 0x1a video gain b (0->100, 50)
+// 0xaa screen rotation (read only)
+//  {0x01, "0 degrees"},
+//  {0x02, "90 degrees"},
+//  {0x03, "180 degrees"},
+//  {0x04, "270 degrees"},
+//  {0xff, "Display cannot supply orientation"},
+// 0x6c video black level r (0->255, 128)
+// 0x6e video black level g (0->255, 128)
+// 0x70 video black level b (0->255, 128)
+// 0x6b backlight level w
+// 0x6d backlight level r
+// 0x6f backlight level g
+// 0x71 backlight level b
+// 0xd6 power mode (10x = on, 0x4 = off)
+//  {0x01, "DPM: On,  DPMS: Off"},
+//  {0x02, "DPM: Off, DPMS: Standby"},
+//  {0x03, "DPM: Off, DPMS: Suspend"},
+//  {0x04, "DPM: Off, DPMS: Off" },
+// 0xb6 display tech (readonly) (0x03 = LCD active matrix)
+//  {0x01, "CRT (shadow mask)"},
+//  {0x02, "CRT (aperture grill)"},
+//  {0x03, "LCD (active matrix)"},   // TFT in 2.0
+//  {0x04, "LCos"},
+//  {0x05, "Plasma"},
+//  {0x06, "OLED"},
+//  {0x07, "EL"},
+//  {0x08, "Dynamic MEM"},     // MEM in 2.0
+//  {0x09, "Static MEM"},      // not in 2.0
+// 0x62 speaker volume (0-100)
+// 0x8d audio mute (1=mute, 2=unmute)
+// 0xca OSD (1=disabled, 2=enabled)
+// 0xda scan mode
+//  {0x00, "Normal operation"},
+//  {0x01, "Underscan"},
+//  {0x02, "Overscan"},
+//  {0x03, "Widescreen" }
+// 0xdb image mode
+//  {0x00, "No effect"},
+//  {0x01, "Full mode"},
+//  {0x02, "Zoom mode"},
+//  {0x03, "Squeeze mode" },
+//  {0x04, "Variable"},
+// 0x8d blank state (1=blank, 2 = unblack)
+// 0x82 horiz mirror (0=normal, 1 = mirror)
+// 0x84 vert  mirror (0=normal, 1 = mirror)
+// 0x63 speaker sel (0=front l/r, 1=side l/r, 2=rear l/r, 3=subwoofer)
+// 0x86 scaling values
+//  {0x01, "No scaling"},
+//  {0x02, "Max image, no aspect ration distortion"},
+//  {0x03, "Max vertical image, no aspect ratio distortion"},
+//  {0x04, "Max horizontal image, no aspect ratio distortion"},
+//  {0x05, "Max vertical image with aspect ratio distortion"},
+//  {0x06, "Max horizontal image with aspect ratio distortion"},
+//  {0x07, "Linear expansion (compression) on horizontal axis"},   // Full mode
+//  {0x08, "Linear expansion (compression) on h and v axes"},      // Zoom mode
+//  {0x09, "Squeeze mode"},
+//  {0x0a, "Non-linear expansion"},                                // Variable
+// 0x94 stereo mode
+//  {0x00,  "Speaker off/Audio not supported"},
+//  {0x01,  "Mono"},
+//  {0x02,  "Stereo"},
+//  {0x03,  "Stereo expanded"},
+//  {0x11,  "SRS 2.0"},
+//  {0x12,  "SRS 2.1"},
+//  {0x13,  "SRS 3.1"},
+//  {0x14,  "SRS 4.1"},
+//  {0x15,  "SRS 5.1"},
+//  {0x16,  "SRS 6.1"},
+//  {0x17,  "SRS 7.1"},
+//  {0x21,  "Dolby 2.0"},
+//  {0x22,  "Dolby 2.1"},
+//  {0x23,  "Dolby 3.1"},
+//  {0x24,  "Dolby 4.1"},
+//  {0x25,  "Dolby 5.1"},
+//  {0x26,  "Dolby 6.1"},
+//  {0x27,  "Dolby 7.1"},
+//  {0x31,  "THX 2.0"},
+//  {0x32,  "THX 2.1"},
+//  {0x33,  "THX 3.1"},
+//  {0x34,  "THX 4.1"},
+//  {0x35,  "THX 5.1"},
+//  {0x36,  "THX 6.1"},
+//  {0x37,  "THX 7.1"},
+
+//////////////////////////////////////////////////////////////////////////////
+
+// ddc lib handle and func symbols
+static void *ddc_lib = NULL;
+struct {
+   DDCA_Status (*ddca_get_display_info_list2)
+     (bool include_invalid_displays, DDCA_Display_Info_List **dlist_loc);
+   void (*ddca_free_display_info_list)
+     (DDCA_Display_Info_List *dlist);
+   DDCA_Status (*ddca_open_display2)
+     (DDCA_Display_Ref ddca_dref, bool wait, DDCA_Display_Handle *ddca_dh_loc);
+   DDCA_Status (*ddca_get_non_table_vcp_value)
+     (DDCA_Display_Handle ddca_dh, DDCA_Vcp_Feature_Code feature_code, 
DDCA_Non_Table_Vcp_Value *valrec);
+   DDCA_Status (*ddca_set_non_table_vcp_value)
+     (DDCA_Display_Handle ddca_dh, DDCA_Vcp_Feature_Code feature_code, uint8_t 
hi_byte, uint8_t lo_byte);
+   DDCA_Status (*ddca_close_display)
+     (DDCA_Display_Handle ddca_dh);
+} ddc_func;
+
+static DDCA_Display_Info_List *ddc_dlist = NULL;
+static DDCA_Display_Handle *ddc_dh = NULL;
+
+static void
+_ddc_clean(void)
+{
+   int i;
+
+   if (!ddc_lib) return;
+   if (ddc_dlist)
+     {
+        if (ddc_dh)
+          {
+             for (i = 0; i < ddc_dlist->ct; i++)
+               {
+                  ddc_func.ddca_close_display(ddc_dh[i]);
+               }
+              free(ddc_dh);
+             ddc_dh = NULL;
+          }
+        ddc_func.ddca_free_display_info_list(ddc_dlist);
+        ddc_dlist = NULL;
+     }
+}
+
+static Eina_Bool
+_ddc_probe(void)
+{
+   int i;
+
+   if (!ddc_lib) return EINA_FALSE;
+   _ddc_clean();
+
+   // the below can be quite sluggish, so we don't want to do this
+   // often, even though this is isolated in a worker thread. it will
+   // block the ddc worker thread while this is done.
+   if (ddc_func.ddca_get_display_info_list2(false, &ddc_dlist) != 0)
+     return EINA_FALSE;
+   if (!ddc_dlist) return EINA_FALSE;
+   ddc_dh = calloc(ddc_dlist->ct, sizeof(DDCA_Display_Handle));
+   if (!ddc_dh)
+     {
+        ddc_func.ddca_free_display_info_list(ddc_dlist);
+        ddc_dlist = NULL;
+        free(ddc_dh);
+     }
+   for (i = 0; i < ddc_dlist->ct; i++)
+     {
+        DDCA_Display_Info *dinfo = &(ddc_dlist->info[i]);
+        DDCA_Display_Ref dref = dinfo->dref;
+        int j;
+        Dev *d;
+
+        if (ddc_func.ddca_open_display2(dref, false, &(ddc_dh[i])) != 0)
+          {
+             for (i = i - 1; i >= 0; i--)
+               {
+                  ddc_func.ddca_close_display(ddc_dh[i]);
+               }
+             free(ddc_dh);
+             ddc_dh = NULL;
+             ddc_func.ddca_free_display_info_list(ddc_dlist);
+             ddc_dlist = NULL;
+             return EINA_FALSE;
+          }
+        d = calloc(1, sizeof(Dev));
+        d->edid = malloc((128 * 2) + 1);
+        if (d->edid)
+          {
+             for (j = 0; j < 128; j++)
+               snprintf(&(d->edid[j * 2]), 3, "%02x", dinfo->edid_bytes[j]);
+             d->edid[j * 2] = 0;
+             d->screen = i;
+             eina_lock_take(&_devices_lock);
+             _devices = eina_list_append(_devices, d);
+             eina_lock_release(&_devices_lock);
+          }
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_ddc_init(void)
+{
+   ddc_lib = dlopen("libddcutil.so.2", RTLD_NOW | RTLD_LOCAL);
+   if (!ddc_lib) return EINA_FALSE;
+#define SYM(_x) \
+   do { \
+      ddc_func._x = dlsym(ddc_lib, #_x); \
+      if (!ddc_func._x) return EINA_FALSE; \
+   } while (0)
+   SYM(ddca_get_display_info_list2);
+   SYM(ddca_free_display_info_list);
+   SYM(ddca_open_display2);
+   SYM(ddca_get_non_table_vcp_value);
+   SYM(ddca_set_non_table_vcp_value);
+   SYM(ddca_close_display);
+
+   // brute force modprobe this as it likely is needed - probe will fail
+   // if this doesn't work or find devices anyway
+   system("modprobe i2c-dev");
+   if (!_ddc_probe()) return EINA_FALSE;
+
+   return EINA_TRUE;
+}
+
+static Req *
+_req_alloc(const char *req, const char *params)
+{
+   Req *r;
+
+   if (!params) return NULL;
+   r = calloc(1, sizeof(Req) + strlen(req) + 1 + strlen(params) + 1);
+   if (!r) return NULL;
+   r->req = ((char *)r) + sizeof(Req);
+   strcpy(r->req, req);
+   r->params = r->req + strlen(r->req) + 1;
+   strcpy(r->params, params);
+   return r;
+}
+
+static void
+_request(const char *req, const char *params)
+{
+   Eina_List *l;
+   Req *r2, *r = _req_alloc(req, params);
+   if (!r) return;
+
+   eina_lock_take(&_devices_lock);
+   EINA_LIST_FOREACH(_req, l, r2)
+     {
+        if (!strcmp(r2->req, r->req))
+          {
+             if (!strcmp(req, "refresh"))
+               {
+                  _req = eina_list_remove_list(_req, l);
+                  free(r2);
+                  break;
+               }
+             else if ((!strcmp(req, "val-set")) ||
+                      (!strcmp(req, "val-get")))
+               {
+                  if ((!strncmp(params, r2->params, 256)) &&
+                      (params[256] == ' '))
+                    {
+                       const char *space = strchr(params + 256 + 1, ' ');
+                       if (!strncmp(params, r2->params, (space - params)))
+                         {
+                            _req = eina_list_remove_list(_req, l);
+                            free(r2);
+                            break;
+                         }
+                    }
+               }
+          }
+     }
+   _req = eina_list_append(_req, r);
+   eina_semaphore_release(&_worker_sem, 1);
+   eina_lock_release(&_devices_lock);
+}
+
+static void
+_do_list(Ecore_Thread *th)
+{
+   Eina_List *l;
+   Dev *d;
+   Req *r;
+   Eina_Strbuf *sbuf;
+
+   sbuf = eina_strbuf_new();
+   if (sbuf)
+     {
+        eina_lock_take(&_devices_lock);
+        EINA_LIST_FOREACH(_devices, l, d)
+          {
+             eina_strbuf_append(sbuf, d->edid);
+             if (l->next) eina_strbuf_append(sbuf, " ");
+          }
+        eina_lock_release(&_devices_lock);
+
+        r = _req_alloc("ddc-list", eina_strbuf_string_get(sbuf));
+        if (r) ecore_thread_feedback(th, r);
+        eina_strbuf_free(sbuf);
+     }
+}
+
+static Eina_Bool
+_id_ok(int id)
+{
+   if (id == 0x10) return EINA_TRUE; // backlight allowed
+   return EINA_FALSE;
+}
+
+static Dev *
+_dev_find(const char *edid)
+{
+   Eina_List *l;
+   Dev *d;
+
+   EINA_LIST_FOREACH(_devices, l, d)
+     {
+        if (!strcmp(d->edid, edid))
+          {
+             return d;
+          }
+     }
+   return NULL;
+}
+
+static void
+_do_val_set(Ecore_Thread *th, const char *edid, int id, int val)
+{
+   Dev *d;
+   Req *r;
+   int screen;
+   char buf[512];
+
+   if (!ddc_lib) goto err;
+   if (!edid) goto err;
+   if (!_id_ok(id)) goto err;
+   if ((val < 0) || (val >= 65536)) goto err;
+
+   eina_lock_take(&_devices_lock);
+   d = _dev_find(edid);
+   if (!d)
+     {
+        eina_lock_release(&_devices_lock);
+        goto err;
+     }
+   screen = d->screen;
+   eina_lock_release(&_devices_lock);
+
+   if (ddc_func.ddca_set_non_table_vcp_value
+       (ddc_dh[screen], id, (val >> 8) & 0xff, val & 0xff) == 0)
+     {
+        snprintf(buf, sizeof(buf), "%s %i %i ok", edid, id, val);
+     }
+   else
+     {
+err:
+        snprintf(buf, sizeof(buf), "%s %i %i err", edid, id, val);
+     }
+   r = _req_alloc("ddc-val-set", buf);
+   if (r) ecore_thread_feedback(th, r);
+}
+
+static void
+_do_val_get(Ecore_Thread *th, const char *edid, int id)
+{
+   Dev *d;
+   Req *r;
+   int screen, val;
+   char buf[512];
+   DDCA_Non_Table_Vcp_Value valrec;
+
+   if (!ddc_lib) goto err;
+   if (!edid) goto err;
+   if (!_id_ok(id)) goto err;
+
+   eina_lock_take(&_devices_lock);
+   d = _dev_find(edid);
+   if (!d)
+     {
+        eina_lock_release(&_devices_lock);
+        goto err;
+     }
+   screen = d->screen;
+   eina_lock_release(&_devices_lock);
+
+   if (ddc_func.ddca_get_non_table_vcp_value
+       (ddc_dh[screen], id, &valrec) == 0)
+     {
+        val = valrec.sl | (valrec.sh << 8);
+        snprintf(buf, sizeof(buf), "%s %i %i", edid, id, val);
+     }
+   else
+     {
+err:
+        snprintf(buf, sizeof(buf), "%s %i -1", edid, id);
+     }
+   r = _req_alloc("ddc-val-get", buf);
+   if (r) ecore_thread_feedback(th, r);
+}
+
+static void
+_cb_worker(void *data EINA_UNUSED, Ecore_Thread *th)
+{
+   Req *r;
+
+   _ddc_init();
+   _do_list(th);
+
+   for (;;)
+     {
+        // wait for requests
+        eina_semaphore_lock(&_worker_sem);
+        eina_lock_take(&_devices_lock);
+        if (_req)
+          {
+             r = _req->data;
+             if (r)
+               {
+                  _req = eina_list_remove_list(_req, _req);
+                  eina_lock_release(&_devices_lock);
+                  if (!strcmp(r->req, "list"))
+                    {
+                       _do_list(th);
+                    }
+                  else if (!strcmp(r->req, "refresh"))
+                    {
+                       _ddc_probe();
+                       _do_list(th);
+                    }
+                  else if (!strcmp(r->req, "val-set"))
+                    {
+                       int id, val;
+                       char edid[257];
+                       if (sscanf(r->params, "%256s %i %i", edid, &id, &val) 
== 3)
+                         _do_val_set(th, edid, id, val);
+                    }
+                  else if (!strcmp(r->req, "val-get"))
+                    {
+                       int id;
+                       char edid[257];
+                       if (sscanf(r->params, "%256s %i", edid, &id) == 2)
+                         _do_val_get(th, edid, id);
+                    }
+                  free(r);
+               }
+             else eina_lock_release(&_devices_lock);
+          }
+        else eina_lock_release(&_devices_lock);
+     }
+}
+
+static void
+_cb_worker_message(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED, void 
*msg_data)
+{
+   Req *r = msg_data;
+
+   if (!r) return;
+   e_system_inout_command_send(r->req, "%s", r->params);
+   free(r);
+}
+
+static void
+_cb_worker_end(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED)
+{
+}
+
+static void
+_cb_worker_cancel(void *data EINA_UNUSED, Ecore_Thread *th EINA_UNUSED)
+{
+}
+
+static void
+_cb_ddc_list(void *data EINA_UNUSED, const char *params)
+{
+   _request("list", params);
+}
+
+static void
+_cb_ddc_refresh(void *data EINA_UNUSED, const char *params)
+{
+   _request("refresh", params);
+}
+
+static void
+_cb_ddc_val_set(void *data EINA_UNUSED, const char *params)
+{
+   _request("val-set", params);
+}
+
+static void
+_cb_ddc_val_get(void *data EINA_UNUSED, const char *params)
+{
+   _request("val-get", params);
+}
+
+void
+e_system_ddc_init(void)
+{
+   eina_lock_new(&_devices_lock);
+   eina_semaphore_new(&_worker_sem, 0);
+   ecore_thread_feedback_run(_cb_worker, _cb_worker_message,
+                             _cb_worker_end, _cb_worker_cancel,
+                             NULL, EINA_TRUE);
+   e_system_inout_command_register("ddc-list",    _cb_ddc_list, NULL);
+   e_system_inout_command_register("ddc-refresh", _cb_ddc_refresh, NULL);
+   e_system_inout_command_register("ddc-val-set", _cb_ddc_val_set, NULL);
+   e_system_inout_command_register("ddc-val-get", _cb_ddc_val_get, NULL);
+}
+
+void
+e_system_ddc_shutdown(void)
+{
+   // only shutdown things we really have to - no need to free mem etc.
+}
diff --git a/src/bin/system/e_system_main.c b/src/bin/system/e_system_main.c
index de7873ade..fd7eb9d2c 100644
--- a/src/bin/system/e_system_main.c
+++ b/src/bin/system/e_system_main.c
@@ -287,6 +287,7 @@ main(int argc EINA_UNUSED, const char **argv EINA_UNUSED)
 
    e_system_inout_init();
    e_system_backlight_init();
+   e_system_ddc_init();
    e_system_storage_init();
    e_system_power_init();
    e_system_rfkill_init();
@@ -302,6 +303,7 @@ main(int argc EINA_UNUSED, const char **argv EINA_UNUSED)
    e_system_rfkill_shutdown();
    e_system_power_shutdown();
    e_system_storage_shutdown();
+   e_system_ddc_shutdown();
    e_system_backlight_shutdown();
    e_system_inout_shutdown();
 
diff --git a/src/bin/system/meson.build b/src/bin/system/meson.build
index d570fa4d6..64683b96d 100644
--- a/src/bin/system/meson.build
+++ b/src/bin/system/meson.build
@@ -2,6 +2,7 @@ src = [
   'e_system_main.c',
   'e_system_inout.c',
   'e_system_backlight.c',
+  'e_system_ddc.c',
   'e_system_storage.c',
   'e_system_power.c',
   'e_system_rfkill.c',

-- 


Reply via email to