
/* 
 *  gcc -Wall -Os `pkg-config libxml-2.0 --cflags` -I/usr/include/ddccontrol mcontrol.c -o mcontrol -lddccontrol
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <libxml/xmlstring.h>

#include <ddcci.h>
#include <monitor_db.h>


#define DATADIR "/usr/share/ddccontrol-db"  /* adjust it if needed */

#define RETRYS 3 /* number of retrys */

static void dumpctrl(struct monitor* mon, unsigned char ctrl, int force)
{
    unsigned short value, maximum;
    int retry, result;

    struct monitor_db* monitor = mon->db;
    struct group_db* group;
    struct subgroup_db* subgroup;
    struct control_db* control;
    struct value_db* valued;
    xmlChar* controlname = NULL;
    xmlChar* valuename = NULL;
    
    for (retry = RETRYS; retry; retry--) {
        if ((result = ddcci_readctrl(mon, ctrl, &value, &maximum)) >= 0) 
        {
            if ((result > 0) || force)
            {
                if (monitor) 
                {
                    /* loop through groups */
                    for (group = monitor->group_list; (group != NULL) && 
                            (controlname == NULL); group = group->next) 
                    {
                        /* loop through subgroups inside group */
                        for (subgroup = group->subgroup_list; (subgroup != NULL); subgroup = subgroup->next) 
                        {
                            /* loop through controls inside subgroup */
                            for (control = subgroup->control_list; (control != NULL); control = control->next) 
                            {
                                /* check for control id */
                                if (control->address == ctrl) 
                                {
                                    controlname = control->name;
                                    /* look for the value */
                                    for (valued = control->value_list; (valued != NULL); valued = valued->next) {
                                        if (valued->value == value) {
                                            valuename = valued->name;
                                            break;
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
                if (controlname == NULL) {
                    fprintf(stdout, "%s 0x%02x: %c/%d/%d %c [???]\n", "Control",
                        ctrl, (result > 0) ? '+' : '-', value, maximum, (mon->caps.vcp[ctrl]) ? 'C' : ' ');
                }
                else if (valuename == NULL) {
                    fprintf(stdout, "%s 0x%02x: %c/%d/%d %c [%s]\n", "Control",
                        ctrl, (result > 0) ? '+' : '-',  value, maximum, (mon->caps.vcp[ctrl]) ? 'C' : ' ', controlname);
                }
                else {
                    fprintf(stdout, "%s 0x%02x: %c/%d/%d %c [%s - %s]\n", "Control",
                        ctrl, (result > 0) ? '+' : '-',  value, maximum, (mon->caps.vcp[ctrl]) ? 'C' : ' ', controlname, valuename);
                }
            }
            break;
        }
    }
}

/* Find the delay we must respect after writing to an address in the database. */
static int find_write_delay(struct monitor* mon, char ctrl) {
    struct monitor_db* monitor = mon->db;
    struct group_db* group;
    struct subgroup_db* subgroup;
    struct control_db* control;
    
    if (monitor)
    {
        /* loop through groups */
        for (group = monitor->group_list; group != NULL; group = group->next)
        {
            /* loop through subgroups inside group */
            for (subgroup = group->subgroup_list; subgroup != NULL; subgroup = subgroup->next)
            {
                /* loop through controls inside subgroup */
                for (control = subgroup->control_list; control != NULL; control = control->next)
                {
                    /* check for control id */
                    if (control->address == ctrl) 
                    {
                        return control->delay;                  }
                }
            }
        }
    }
    return -1;
}

void write_value(struct monitor* mon, int ctrl, int value) {
    int delay;

    delay = find_write_delay(mon, ctrl);
    if (delay >= 0) {
        fprintf(stdout, "\nWriting 0x%02x, 0x%02x(%d) (%dms delay)...\n",
            ctrl, value, value, delay);
    }
    else {
        fprintf(stdout, "\nWriting 0x%02x, 0x%02x(%d)...\n",
            ctrl, value, value);
    }
    ddcci_writectrl(mon, ctrl, value, delay);

    dumpctrl(mon, ctrl, 1);
}

int main(int argc, char **argv)
{
    int ret;
        
    /* filedescriptor and name of device */
    struct monitor mon;
    char *fn;
    
    char *datadir = NULL;
    
    int ctrl = -1;
    int value = -1;
    
    if (!ddcci_init(datadir)) {
        printf("Unable to initialize ddcci library.\n");
        exit(1);
    }

        fn = NULL;
        
        struct monitorlist* monlist;
        struct monitorlist* current;
        
        monlist = ddcci_probe();
        
        printf("Detected monitors :\n");
        
        current = monlist;
        while (current != NULL)
        {
            printf(" - Device: %s\n", current->filename);
            printf("   DDC/CI supported: %s\n", current->supported ? "Yes" : "No");
            printf("   Monitor Name: %s\n", current->name);
            printf("   Input type: %s\n", current->digital ? "Digital" : "Analog");
            
            if ((!fn) && (current->supported))
            {
                printf("  (Automatically selected)\n");
                fn = malloc(strlen(current->filename)+1);
                strcpy(fn, current->filename);
            }
            
            current = current->next;
        }
        
        if (fn == NULL) {
            fprintf(stderr, 
                "No monitor supporting DDC/CI available.\n"
                "If your graphics card need it, please check all the required kernel modules are loaded (i2c-dev, and your framebuffer driver).\n"
                );
            ddcci_release();
            exit(0);
        }
        
        ddcci_free_list(monlist);
  
        if ((ret = ddcci_open(&mon, fn, 0)) < 0) {
            fprintf(stderr, 
                "\nDDC/CI at %s is unusable (%d).\n"
                "If your graphics card need it, please check all the required kernel modules are loaded (i2c-dev, and your framebuffer driver).\n"
                , fn, ret);

        } else {

            ctrl = 0xdc;    /* magic bright mode */
            value = 6;
            write_value(&mon, ctrl, value);

            ctrl = 0x10;    /* brightness */
            value = 42;
            write_value(&mon, ctrl, value);

            ctrl = 0x12;    /* contrast */
            value = 49;
            write_value(&mon, ctrl, value);

            ctrl = 0xf2;    /* gamma */
            value = 6;
            write_value(&mon, ctrl, value);

            ddcci_save(&mon);
            ddcci_close(&mon);
        }

    free(fn);
    ddcci_release();

    return 0;
}



