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
This commit is contained in:
Carsten Haitzler 2020-02-06 13:53:27 +00:00
parent 19fe001f4f
commit eab2a34ef3
6 changed files with 705 additions and 18 deletions

View File

@ -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 static void
_backlight_devices_clear(void) _backlight_devices_clear(void)
{ {
const char *s;
Backlight_Device *bd; Backlight_Device *bd;
EINA_LIST_FREE(bl_devs, s)
eina_stringshare_del(s);
EINA_LIST_FREE(_devices, bd) EINA_LIST_FREE(_devices, bd)
{ {
eina_stringshare_del(bd->dev); eina_stringshare_del(bd->dev);
@ -51,6 +74,7 @@ _backlight_devices_clear(void)
eina_stringshare_del(bd->edid); eina_stringshare_del(bd->edid);
if (bd->anim) ecore_animator_del(bd->anim); if (bd->anim) ecore_animator_del(bd->anim);
e_system_handler_del("bklight-val", _backlight_system_get_cb, bd); 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); free(bd);
} }
} }
@ -170,7 +194,8 @@ _backlight_devices_device_set(Backlight_Device *bd, double val)
#endif #endif
if (!strncmp(bd->dev, "ddc:", 4)) 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 else
{ {
@ -221,7 +246,8 @@ _backlight_devices_device_update(Backlight_Device *bd)
#endif #endif
if (!strncmp(bd->dev, "ddc:", 4)) 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 else
{ {
@ -262,6 +288,21 @@ _backlight_devices_screen_lid_get(void)
return NULL; 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 * static Backlight_Device *
_backlight_devices_edid_find(const char *edid) _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(); E_Randr2_Screen *sc = _backlight_devices_screen_lid_get();
Backlight_Device *bd; Backlight_Device *bd;
if (!sc) return; if (!sc) return;
if (!sc->info.edid) return;
bd = _backlight_devices_edid_find(sc->info.edid); bd = _backlight_devices_edid_find(sc->info.edid);
if (!bd) if (!bd)
{ {
@ -302,23 +344,36 @@ _backlight_devices_lid_register(const char *dev, Eina_Bool force)
eina_stringshare_replace(&(bd->dev), dev); 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 static void
_backlight_system_list_cb(void *data EINA_UNUSED, const char *params) _backlight_system_list_cb(void *data EINA_UNUSED, const char *params)
{ {
// params "dev flag dev flag ..." // params "dev flag dev flag ..."
const char *p = params, *s; const char *p = params;
char dev[1024], flag, devnum = 0; char dev[1024], flag, devnum = 0;
e_system_handler_del("bklight-list", _backlight_system_list_cb, NULL); 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)) while ((p) && (*p))
{ {
if (sscanf(p, "%1023s", dev) == 1) if (sscanf(p, "%1023s", dev) == 1)
@ -364,7 +419,30 @@ _backlight_system_list_cb(void *data EINA_UNUSED, const char *params)
} }
static void 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(); _backlight_devices_clear();
#ifndef HAVE_WAYLAND_ONLY #ifndef HAVE_WAYLAND_ONLY
@ -381,6 +459,7 @@ _backlight_devices_probe(void)
Ecore_X_Randr_Output *out; Ecore_X_Randr_Output *out;
int i, num = 0; int i, num = 0;
bl_devs = eina_list_append(bl_devs, eina_stringshare_add("randr"));
out = ecore_x_randr_window_outputs_get(root, &num); out = ecore_x_randr_window_outputs_get(root, &num);
if ((out) && (num > 0)) if ((out) && (num > 0))
{ {
@ -417,8 +496,12 @@ _backlight_devices_probe(void)
// to respond to the device listing later // to respond to the device listing later
_devices_pending_ops++; _devices_pending_ops++;
e_system_handler_add("bklight-list", _backlight_system_list_cb, NULL); 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); 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 // XXXX: add ddc to e_syystem and query that too
} }
@ -444,7 +527,7 @@ static void
_cb_job_zone_change(void *data EINA_UNUSED) _cb_job_zone_change(void *data EINA_UNUSED)
{ {
zone_change_job = NULL; zone_change_job = NULL;
_backlight_devices_probe(); _backlight_devices_probe(EINA_FALSE);
e_backlight_update(); e_backlight_update();
} }
@ -460,7 +543,7 @@ EINTERN int
e_backlight_init(void) e_backlight_init(void)
{ {
E_EVENT_BACKLIGHT_CHANGE = ecore_event_type_new(); E_EVENT_BACKLIGHT_CHANGE = ecore_event_type_new();
_backlight_devices_probe(); _backlight_devices_probe(EINA_TRUE);
#define H(ev, cb) \ #define H(ev, cb) \
handlers = eina_list_append(handlers, \ handlers = eina_list_append(handlers, \
ecore_event_handler_add(ev, cb, NULL)); 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; if (zone->bl_mode == E_BACKLIGHT_MODE_NORMAL) tim = 0.5;
else if (tim < 0.0) tim = e_config->backlight.transition; else if (tim < 0.0) tim = e_config->backlight.transition;
// XXX: store in bl device
E_FREE_FUNC(bd->anim, ecore_animator_del); E_FREE_FUNC(bd->anim, ecore_animator_del);
bd->anim = ecore_animator_timeline_add(tim, _bl_anim, bd); bd->anim = ecore_animator_timeline_add(tim, _bl_anim, bd);
bd->from_val = bl_now; bd->from_val = bl_now;

View File

@ -107,7 +107,8 @@ _system_message_read(void)
if (del_count > 0) if (del_count > 0)
{ {
eina_hash_del(_handlers, head->cmd, plist); 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(); buf2 = eina_binbuf_new();

View File

@ -122,5 +122,8 @@ void e_system_l2ping_shutdown(void);
void e_system_cpufreq_init(void); void e_system_cpufreq_init(void);
void e_system_cpufreq_shutdown(void); void e_system_cpufreq_shutdown(void);
void e_system_ddc_init(void);
void e_system_ddc_shutdown(void);
#endif #endif

View File

@ -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.
}

View File

@ -287,6 +287,7 @@ main(int argc EINA_UNUSED, const char **argv EINA_UNUSED)
e_system_inout_init(); e_system_inout_init();
e_system_backlight_init(); e_system_backlight_init();
e_system_ddc_init();
e_system_storage_init(); e_system_storage_init();
e_system_power_init(); e_system_power_init();
e_system_rfkill_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_rfkill_shutdown();
e_system_power_shutdown(); e_system_power_shutdown();
e_system_storage_shutdown(); e_system_storage_shutdown();
e_system_ddc_shutdown();
e_system_backlight_shutdown(); e_system_backlight_shutdown();
e_system_inout_shutdown(); e_system_inout_shutdown();

View File

@ -2,6 +2,7 @@ src = [
'e_system_main.c', 'e_system_main.c',
'e_system_inout.c', 'e_system_inout.c',
'e_system_backlight.c', 'e_system_backlight.c',
'e_system_ddc.c',
'e_system_storage.c', 'e_system_storage.c',
'e_system_power.c', 'e_system_power.c',
'e_system_rfkill.c', 'e_system_rfkill.c',