diff options
| -rw-r--r-- | CMakeLists.txt | 5 | ||||
| -rw-r--r-- | deps/sysobj_early/data/edid.ids | 98 | ||||
| -rw-r--r-- | deps/sysobj_early/include/util_edid.h | 68 | ||||
| -rw-r--r-- | deps/sysobj_early/src/appf.c | 8 | ||||
| -rw-r--r-- | deps/sysobj_early/src/util_edid.c | 319 | ||||
| -rw-r--r-- | hardinfo/x_util.c | 3 | ||||
| -rw-r--r-- | modules/devices.c | 23 | ||||
| -rw-r--r-- | modules/devices/dmi_memory.c | 2 | ||||
| -rw-r--r-- | modules/devices/monitors.c | 282 | 
9 files changed, 804 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4326e892..e41cbf4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,7 @@ set(HARDINFO_RESOURCES  	"deps/sysobj_early/data/sdcard.ids"  	"deps/sysobj_early/data/usb.ids"  	"deps/sysobj_early/data/arm.ids" +	"deps/sysobj_early/data/edid.ids"  )  set(HARDINFO_MANPAGES @@ -152,6 +153,7 @@ set(MODULE_devices_SOURCES  	modules/devices.c  	modules/devices/${HARDINFO_ARCH}/processor.c  	modules/devices/gpu.c +	modules/devices/monitors.c  	modules/devices/battery.c  	modules/devices/dmi.c  	modules/devices/dmi_memory.c @@ -209,6 +211,7 @@ set_source_files_properties(  )  set_source_files_properties( +	modules/devices/monitors.c  	modules/devices/dmi_memory.c  #	modules/devices/spd-decode.c  	hardinfo/problem_marker.c @@ -230,8 +233,10 @@ add_library(sysobj_early STATIC  	deps/sysobj_early/src/appf.c  	deps/sysobj_early/src/nice_name.c  	deps/sysobj_early/gui/uri_handler.c +	deps/sysobj_early/src/util_edid.c  )  set_target_properties(sysobj_early PROPERTIES COMPILE_FLAGS "-std=c99 -Wall -Wextra -Wno-parentheses -Wno-unused-function") +target_link_libraries(sysobj_early m)  if (HARDINFO_GTK3)  add_library(uber-graph STATIC diff --git a/deps/sysobj_early/data/edid.ids b/deps/sysobj_early/data/edid.ids new file mode 100644 index 00000000..575287b6 --- /dev/null +++ b/deps/sysobj_early/data/edid.ids @@ -0,0 +1,98 @@ +# +# https://github.com/bp0/verbose-spork/blob/master/data/edid.ids +# +# List of known/observed EDID three-letter vendor codes +# See http://edid.tv/manufacturer +# These codes are also the three-letter "EISA" or "PNP" codes. +# See http://uefi.org/sites/default/files/resources/PNPID_List.pdf +# +# Syntax: +# VEN Vendor Name +# +# Please keep sorted. +# + +AAA Avolites +ACI Ancor Communications +ACR Acer Technologies +ADA Addi-Data GmbH +AGO AlgolTek +AOC AOC International +APP Apple Computer +ATO ASTRO DESIGN +AUO AU Optronics +AVT Avtek +BMD Blackmagic Design +BNO Bang & Olufsen +BNQ BenQ +BOE BOE +CMN Chimei Innolux Corporation +CMO Chi Mei Optoelectronics +CRO Extraordinary Technologies +CUK Calibre UK +DEL Dell +DGC Data General Corporation +DON DENON +ENC Eizo Nanao Corporation +EPH Epiphan Systems +EXP Data Export Corporation +FNI Funai Electric +FUS Fujitsu Siemens Computers +GSM Goldstar Company +HIQ Kaohsiung Opto Electronics Americas +HPN Hewlett-Packard Company +HSD HannStar Display Corporation +HTC Hitachi +HWP Hewlett-Packard Company +IBM International Business Machines +INO Innolab Pte Ltd +INT Interphase Corporation +INX Communications Supply Corporation (A division of WESCO) +ITE Integrated Tech Express +IVM Iiyama North America +JVC Victor Company of Japan (JVC) +KTC Kingston Tech Corporation +LEN Lenovo Group +LGD LG Display +LNX The Linux Foundation +LWR Lightware Visual Engineering +MAX Rogen Tech Distribution +MEG Abeam Tech +MEI Panasonic Industry Company +MGW Magewell +MST MS Telematica +MTC Mars-Tech Corporation +MTX Matrox +NEC NEC Corporation +NEX Nexgen Mediatech +ONK ONKYO Corporation +ORN ORION ELECTRIC +OTM Optoma Corporation +OVR Oculus VR +PHL Koninklijke Philips +PIO Pioneer Electronic Corporation +PLN Planar +PNR Planar Systems +QDS Quanta Display +RAT Rent-A-Tech +REN Renesas Technology Corporation +SAM Samsung Electric Company +SAN Sanyo Electric Company +SEC Seiko Epson Corporation +SHP Sharp Corporation +SII Silicon Image +SNY Sony +STD STD Computer +SVS AMX (Harman, SVSI) +SYN Synaptics +TCL Technical Concepts +TDC Teradici +TOP Orion Communications Company +TSB Toshiba Corporation +TST Transtream +VES Vestel Elektronik Sanayi ve Ticaret A. S. +VIT Visitech +VIZ Vizio +VSC ViewSonic Corporation +WDE Westinghouse Digital Electronics +YMH Yamaha Corporation diff --git a/deps/sysobj_early/include/util_edid.h b/deps/sysobj_early/include/util_edid.h new file mode 100644 index 00000000..3d54fa0d --- /dev/null +++ b/deps/sysobj_early/include/util_edid.h @@ -0,0 +1,68 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * Copyright (C) 2018  Burt P. <pburt0@gmail.com> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef __UTIL_EDID_H__ +#define __UTIL_EDID_H__ + +#include <stdint.h>  /* for *int*_t types */ + +#define EDID_MAX_EXT_BLOCKS 254 + +/* just enough edid decoding */ +struct edid { +    int ver_major, ver_minor; +    char ven[4]; + +    int d_type[4]; +    char d_text[4][14]; + +    /* point into d_text */ +    char *name; +    char *serial; +    char *ut1; +    char *ut2; + +    int a_or_d; /* 0 = analog, 1 = digital */ +    int bpc; + +    uint16_t product; +    uint32_t n_serial; +    int week, year; + +    int horiz_cm, vert_cm; +    float diag_cm, diag_in; + +    int size; /* bytes */ +    int checksum_ok; /* block 0 */ + +    int ext_blocks, ext_blocks_ok, ext_blocks_fail; +    uint8_t ext[EDID_MAX_EXT_BLOCKS][2]; /* block type, checksum_ok */ +}; + +int edid_fill(struct edid *id_out, const void *edid_bytes, int edid_len); +int edid_fill_xrandr(struct edid *id_out, const char *xrandr_edid_dump); + +int edid_hex_to_bin(void **edid_bytes, int *edid_len, const char *hex_string); +char *edid_bin_to_hex(const void *edid_bytes, int edid_len); + +const char *edid_descriptor_type(int type); +char *edid_dump(struct edid *id); + +#endif diff --git a/deps/sysobj_early/src/appf.c b/deps/sysobj_early/src/appf.c index 432e0f30..5969d945 100644 --- a/deps/sysobj_early/src/appf.c +++ b/deps/sysobj_early/src/appf.c @@ -18,12 +18,18 @@   *   */ -#include "appf.h"  #define _GNU_SOURCE /* for vasprintf() */  #include <stdlib.h>  #include <stdarg.h>  #include <stdio.h>  #include <string.h> +#include "appf.h" + +/* FIXME: if this isn't here, hardinfo will crash, + * I don't have the slightest idea why */ +void wtf() { +    edid_fill(NULL, NULL, 0); +}  char *appf(char *str, const char *sep, const char *fmt, ...) {      char *buf = NULL; diff --git a/deps/sysobj_early/src/util_edid.c b/deps/sysobj_early/src/util_edid.c new file mode 100644 index 00000000..3f953191 --- /dev/null +++ b/deps/sysobj_early/src/util_edid.c @@ -0,0 +1,319 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * Copyright (C) 2018  Burt P. <pburt0@gmail.com> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#define _GNU_SOURCE +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <endian.h> +#include <stdio.h> +#include "util_edid.h" +#include "util_sysobj.h" +#include "gettext.h" + +/* block must be 128 bytes */ +int block_check(const void *edid_block_bytes) { +    if (!edid_block_bytes) return 0; +    uint8_t sum = 0; +    uint8_t *data = (uint8_t*)edid_block_bytes; +    int i; +    for(i=0; i<128; i++) +        sum += data[i]; +    return sum == 0 ? 1 : 0; +} + +int edid_fill(struct edid *id_out, const void *edid_bytes, int edid_len) { +    int i; + +    if (!id_out) return 0; + +    memset(id_out, 0, sizeof(struct edid)); +    id_out->size = edid_len; + +    if (edid_len >= 128) { +        uint8_t *u8 = (uint8_t*)edid_bytes; +        uint16_t *u16 = (uint16_t*)edid_bytes; +        uint32_t *u32 = (uint32_t*)edid_bytes; + +        id_out->ver_major = u8[18];        /* byte 18 */ +        id_out->ver_minor = u8[19];        /* byte 19 */ + +        /* checksum first block */ +        id_out->checksum_ok = block_check(edid_bytes); +        if (edid_len > 128) { +            int blocks = edid_len / 128; +            if (blocks > EDID_MAX_EXT_BLOCKS) blocks = EDID_MAX_EXT_BLOCKS; +            blocks--; +            id_out->ext_blocks = blocks; +            for(; blocks; blocks--) { +                id_out->ext[blocks-1][0] = *(u8 + (blocks * 128)); +                int r = block_check(u8 + (blocks * 128)); +                id_out->ext[blocks-1][1] = (uint8_t)r; +                if (r) id_out->ext_blocks_ok++; +                else id_out->ext_blocks_fail++; +            } +        } + +        /* expect 1.3 */ + +        uint16_t vid = be16toh(u16[4]); /* bytes 8-9 */ +        id_out->ven[2] = 64 + (vid & 0x1f); +        id_out->ven[1] = 64 + ((vid >> 5) & 0x1f); +        id_out->ven[0] = 64 + ((vid >> 10) & 0x1f); + +        id_out->product = le16toh(u16[5]); /* bytes 10-11 */ +        id_out->n_serial = le32toh(u32[3]);/* bytes 12-15 */ +        id_out->week = u8[16];             /* byte 16 */ +        id_out->year = u8[17] + 1990;      /* byte 17 */ + +        if (id_out->week >= 52) +            id_out->week = 0; + +        id_out->a_or_d = (u8[20] & 0x80) ? 1 : 0; +        if (id_out->a_or_d == 1) { +            /* digital */ +            switch((u8[20] >> 4) & 0x7) { +                case 0x1: id_out->bpc = 6; break; +                case 0x2: id_out->bpc = 8; break; +                case 0x3: id_out->bpc = 10; break; +                case 0x4: id_out->bpc = 12; break; +                case 0x5: id_out->bpc = 14; break; +                case 0x6: id_out->bpc = 16; break; +            } +        } + +        if (u8[21] && u8[22]) { +            id_out->horiz_cm = u8[21]; +            id_out->vert_cm = u8[22]; +            id_out->diag_cm = +                sqrt( (id_out->horiz_cm * id_out->horiz_cm) +                 + (id_out->vert_cm * id_out->vert_cm) ); +            id_out->diag_in = id_out->diag_cm / 2.54; +        } + +        uint16_t dh, dl; + +        /* descriptor at byte 54 */ +        dh = be16toh(u16[54/2]); +        dl = be16toh(u16[54/2+1]); +        id_out->d_type[0] = (dh << 16) + dl; +        switch(id_out->d_type[0]) { +            case 0xfc: case 0xff: case 0xfe: +                strncpy(id_out->d_text[0], (char*)u8+54+5, 13); +        } + +        /* descriptor at byte 72 */ +        dh = be16toh(u16[72/2]); +        dl = be16toh(u16[72/2+1]); +        id_out->d_type[1] = (dh << 16) + dl; +        switch(id_out->d_type[1]) { +            case 0xfc: case 0xff: case 0xfe: +                strncpy(id_out->d_text[1], (char*)u8+72+5, 13); +        } + +        /* descriptor at byte 90 */ +        dh = be16toh(u16[90/2]); +        dl = be16toh(u16[90/2+1]); +        id_out->d_type[2] = (dh << 16) + dl; +        switch(id_out->d_type[2]) { +            case 0xfc: case 0xff: case 0xfe: +                strncpy(id_out->d_text[2], (char*)u8+90+5, 13); +        } + +        /* descriptor at byte 108 */ +        dh = be16toh(u16[108/2]); +        dl = be16toh(u16[108/2+1]); +        id_out->d_type[3] = (dh << 16) + dl; +        switch(id_out->d_type[3]) { +            case 0xfc: case 0xff: case 0xfe: +                strncpy(id_out->d_text[3], (char*)u8+108+5, 13); +        } + +        for(i = 0; i < 4; i++) { +            g_strstrip(id_out->d_text[i]); +            switch(id_out->d_type[i]) { +                case 0xfc: +                    id_out->name = id_out->d_text[i]; +                    break; +                case 0xff: +                    id_out->serial = id_out->d_text[i]; +                    break; +                case 0xfe: +                    if (id_out->ut1) +                        id_out->ut2 = id_out->d_text[i]; +                    else +                        id_out->ut1 = id_out->d_text[i]; +                    break; +            } +        } + +        if (!id_out->name) { +            if (SEQ(id_out->ut1, "LG Display") && id_out->ut2) +                /* LG may use "uspecified text" for name and model */ +                id_out->name = id_out->ut2; +            else if (SEQ(id_out->ut1, "AUO") && id_out->ut2) +                /* Same with AUO */ +                id_out->name = id_out->ut2; +            else { +                if (id_out->ut1) id_out->name = id_out->ut1; +                if (id_out->ut2 && !id_out->serial) id_out->serial = id_out->ut2; +            } +        } +    } +    return 1; +} + +int edid_hex_to_bin(void **edid_bytes, int *edid_len, const char *hex_string) { +    int blen = strlen(hex_string) / 2; +    uint8_t *buffer = malloc(blen), *n = buffer; +    memset(buffer, 0, blen); +    int len = 0; + +    const char *p = hex_string; +    char byte[3] = ".."; + +    while(p && *p) { +        if (isxdigit(p[0]) && isxdigit(p[1])) { +            byte[0] = p[0]; +            byte[1] = p[1]; +            *n = strtol(byte, NULL, 16); +            n++; +            len++; +            p += 2; +        } else +            p++; +    } + +    *edid_bytes = (void *)buffer; +    *edid_len = len; +    return 1; +} + +int edid_fill_xrandr(struct edid *id_out, const char *xrandr_edid_dump) { +    void *buffer = NULL; +    int len = 0; +    edid_hex_to_bin(&buffer, &len, xrandr_edid_dump); +    return edid_fill(id_out, buffer, len); +} + +const char *edid_ext_block_type(int type) { +    switch(type) { +        case 0xf0: +            return N_("extension block map"); +        case 0x02: +            return N_("EIA/CEA-861 extension block"); +    } +    return N_("unknown block type"); +} + +const char *edid_descriptor_type(int type) { +    switch(type) { +        case 0xff: +            return N_("display serial number"); +        case 0xfe: +            return N_("unspecified text"); +        case 0xfd: +            return N_("display range limits"); +        case 0xfc: +            return N_("display name"); +        case 0xfb: +            return N_("additional white point"); +        case 0xfa: +            return N_("additional standard timing identifiers"); +        case 0xf9: +            return N_("Display Color Management"); +        case 0xf8: +            return N_("CVT 3-byte timing codes"); +        case 0xf7: +            return N_("additional standard timing"); +        case 0x10: +            return N_("dummy"); +    } +    if (type < 0x0f) return N_("manufacturer reserved descriptor"); +    return N_("detailed timing descriptor"); +} + +char *edid_dump(struct edid *id) { +    char *ret = NULL; +    int i; + +    if (!id) return NULL; +    ret = appfnl(ret, "edid_version: %d.%d (%d bytes)", id->ver_major, id->ver_minor, id->size); + +    ret = appfnl(ret, "mfg: %s, model: %u, n_serial: %u", id->ven, id->product, id->n_serial); +    if (id->week && id->year) +        ret = appf(ret, "", ", dom: week %d of %d", id->week, id->year); +    else if (id->year) +        ret = appf(ret, "", ", dom: %d", id->year); + +    ret = appfnl(ret, "type: %s", id->a_or_d ? "digital" : "analog"); +    if (id->bpc) +        ret = appfnl(ret, "bits per color channel: %d", id->bpc); + +    if (id->horiz_cm && id->vert_cm) +        ret = appfnl(ret, "size: %d cm × %d cm", id->horiz_cm, id->vert_cm); +    if (id->diag_cm) +        ret = appfnl(ret, "diagonal: %0.2f cm (%0.2f in)", id->diag_cm, id->diag_in); + +    ret = appfnl(ret, "checksum %s", id->checksum_ok ? "ok" : "failed!"); +    if (id->ext_blocks_ok || id->ext_blocks_fail) +        ret = appf(ret, "", ", extension blocks: %d of %d ok", id->ext_blocks_ok, id->ext_blocks_ok + id->ext_blocks_fail); + +    for(i = 0; i < 4; i++) +        ret = appfnl(ret, "descriptor[%d] (%s): %s", i, _(edid_descriptor_type(id->d_type[i])), *id->d_text[i] ? id->d_text[i] : "{...}"); + +    for(i = 0; i < EDID_MAX_EXT_BLOCKS; i++) { +        if (id->ext[i][0]) { +            ret = appfnl(ret, "ext_block[%d]: (%s): %s", i, _(edid_ext_block_type(id->ext[i][0])), id->ext[i][1] ? "ok" : "fail"); +        } +    } + +    return ret; +} + +char *edid_bin_to_hex(const void *edid_bytes, int edid_len) { +    int lines = edid_len / 16; +    int blen = lines * 35 + 1; +    int pc = 0; +    char *ret = malloc(blen); +    memset(ret, 0, blen); +    uint8_t *u8 = (uint8_t*)edid_bytes; +    char *p = ret; +    for(; lines; lines--) { +        sprintf(p, "\t\t"); +        p+=2; +        int i; +        for(i = 0; i < 16; i++) { +            sprintf(p, "%02x", (unsigned int)*u8); +            p+=2; +            u8++; +            pc++; +            if (pc == blen-1) { +                sprintf(p, "\n"); +                goto edid_print_done; +            } +        } +        sprintf(p, "\n"); +        p++; +    } +edid_print_done: +    return ret; +} diff --git a/hardinfo/x_util.c b/hardinfo/x_util.c index 52888a8c..07d5226a 100644 --- a/hardinfo/x_util.c +++ b/hardinfo/x_util.c @@ -151,7 +151,7 @@ gboolean fill_xinfo(xinfo *xi) {  gboolean fill_xrr_info(xrr_info *xrr) {      gboolean spawned;      gchar *out, *err, *p, *next_nl; -    gchar *xrr_cmd = g_strdup("xrandr"); +    gchar *xrr_cmd = g_strdup("xrandr --prop");      int ec;      x_screen ts; @@ -192,7 +192,6 @@ gboolean fill_xrr_info(xrr_info *xrr) {                  goto xrr_next_line;              } -              /* looking for:               * <output_id> (connected|disconnected|unknown connection) (primary|?) <%dx%d+%d+%d> (attribute_list) mm x mm               */ diff --git a/modules/devices.c b/modules/devices.c index 02597e65..14b3708c 100644 --- a/modules/devices.c +++ b/modules/devices.c @@ -42,6 +42,7 @@  gchar *callback_processors();  gchar *callback_gpu(); +gchar *callback_monitors();  gchar *callback_battery();  gchar *callback_pci();  gchar *callback_sensors(); @@ -57,6 +58,7 @@ gchar *callback_device_resources();  void scan_processors(gboolean reload);  void scan_gpu(gboolean reload); +void scan_monitors(gboolean reload);  void scan_battery(gboolean reload);  void scan_pci(gboolean reload);  void scan_sensors(gboolean reload); @@ -80,6 +82,7 @@ enum {      ENTRY_DMI,      ENTRY_PROCESSOR,      ENTRY_GPU, +    ENTRY_MONITORS,      ENTRY_DMI_MEM,      ENTRY_PCI,      ENTRY_USB, @@ -95,6 +98,7 @@ enum {  static ModuleEntry entries[] = {      [ENTRY_PROCESSOR] = {N_("Processor"), "processor.png", callback_processors, scan_processors, MODULE_FLAG_NONE},      [ENTRY_GPU] = {N_("Graphics Processors"), "devices.png", callback_gpu, scan_gpu, MODULE_FLAG_NONE}, +    [ENTRY_MONITORS] = {N_("Monitors"), "monitor.png", callback_monitors, scan_monitors, MODULE_FLAG_NONE},      [ENTRY_PCI] = {N_("PCI Devices"), "devices.png", callback_pci, scan_pci, MODULE_FLAG_NONE},      [ENTRY_USB] = {N_("USB Devices"), "usb.png", callback_usb, scan_usb, MODULE_FLAG_NONE},      [ENTRY_FW] = {N_("Firmware"), "processor.png", callback_firmware, scan_firmware, MODULE_FLAG_NONE}, @@ -134,6 +138,11 @@ gchar *firmware_get_info();  gboolean firmware_hinote(const char **msg);  gchar *firmware_info = NULL; +/* in monitors.c */ +gchar *monitors_get_info(); +gboolean monitors_hinote(const char **msg); +gchar *monitors_info = NULL; +  #include <vendor.h>  extern gchar *gpu_summary; @@ -551,6 +560,15 @@ void scan_dmi_mem(gboolean reload)      SCAN_END();  } +void scan_monitors(gboolean reload) +{ +    SCAN_START(); +    if (monitors_info) +        g_free(monitors_info); +    monitors_info = monitors_get_info(); +    SCAN_END(); +} +  void scan_firmware(gboolean reload)  {      SCAN_START(); @@ -656,6 +674,11 @@ gchar *callback_dmi_mem()      return g_strdup(memory_devices_info);  } +gchar *callback_monitors() +{ +    return g_strdup(monitors_info); +} +  gchar *callback_firmware()  {      return g_strdup(firmware_info); diff --git a/modules/devices/dmi_memory.c b/modules/devices/dmi_memory.c index 753c4c74..f266193c 100644 --- a/modules/devices/dmi_memory.c +++ b/modules/devices/dmi_memory.c @@ -618,7 +618,7 @@ gchar *make_spd_section(spd_data *spd) {      return ret;  } -gchar *tag_make_safe_inplace(gchar *tag) { +static gchar *tag_make_safe_inplace(gchar *tag) {      if (!tag)          return tag;      if (!g_utf8_validate(tag, -1, NULL)) diff --git a/modules/devices/monitors.c b/modules/devices/monitors.c new file mode 100644 index 00000000..4e8ba915 --- /dev/null +++ b/modules/devices/monitors.c @@ -0,0 +1,282 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2019 Leandro A. F. Pereira <leandro@hardinfo.org> + *    Copyright (C) 2019 Burt P. <pburt0@gmail.com> + * + *    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, version 2. + * + *    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA + */ + +#include "devices.h" +#include "util_sysobj.h" +#include "util_edid.h" +#include "util_ids.h" + +static const char monitor_icon[] = "monitor.png"; + +#define UNKIFNULL2(f) ((f) ? f : _("(Unknown)")) +#define UNKIFEMPTY2(f) ((*f) ? f : _("(Unknown)")) + +gboolean no_monitors = FALSE; + +gchar *edid_ids_file = NULL; + +void find_edid_ids_file() { +    if (edid_ids_file) return; +    char *file_search_order[] = { +        g_build_filename(g_get_user_config_dir(), "hardinfo", "edid.ids", NULL), +        g_build_filename(params.path_data, "edid.ids", NULL), +        NULL +    }; +    int n; +    for(n = 0; file_search_order[n]; n++) { +        if (!edid_ids_file && !access(file_search_order[n], R_OK)) +            edid_ids_file = file_search_order[n]; +        else +            g_free(file_search_order[n]); +    } +    auto_free(edid_ids_file); +} + +typedef struct { +    gchar *drm_connection; +    uint8_t *edid_bin; +    gsize edid_len; +    struct edid id; +    gchar *_vstr; /* use monitor_vendor_str() */ +} monitor; +#define monitor_new() g_new0(monitor, 1) + +monitor *monitor_new_from_sysfs(const gchar *sysfs_edid_file) { +    monitor *m = monitor_new(); +    g_file_get_contents(sysfs_edid_file, (gchar**)&(m->edid_bin), &m->edid_len, NULL); +    if (m->edid_len) { +        edid_fill(&m->id, (void*)(m->edid_bin), m->edid_len); +    } + +    gchar *pd = g_path_get_dirname(sysfs_edid_file); +    m->drm_connection = g_path_get_basename(pd); +    g_free(pd); + +    return m; +} + +void monitor_free(monitor *m) { +    if (m) { +        g_free(m->_vstr); +        g_free(m->drm_connection); +        g_free(m->edid_bin); +        g_free(m); +    } +} + +const gchar *monitor_vendor_str(monitor *m) { +    if (m->_vstr) +        return m->_vstr; + +    ids_query_result result = {}; + +    if (!edid_ids_file) +        find_edid_ids_file(); + +    scan_ids_file(edid_ids_file, m->id.ven, &result, -1); +    if (result.results[0]) { +        m->_vstr = g_strdup(result.results[0]); +        return m->_vstr; +    } +    return g_strdup(m->id.ven); +} + +gchar *monitor_name(monitor *m, gboolean include_vendor) { +    if (!m) return NULL; +    gchar *desc = NULL; + +    if (include_vendor) { +        if (*m->id.ven) +            desc = appfsp(desc, "%s", vendor_get_shortest_name(monitor_vendor_str(m))); +        else +            desc = appfsp(desc, "%s", "Unknown"); +    } + +    if (m->id.diag_in) { +        gchar *din = util_strchomp_float(g_strdup_printf("%0.1f", m->id.diag_in)); +        desc = appfsp(desc, "%s\"", din); +        g_free(din); +    } + +    if (m->id.name) +        desc = appfsp(desc, "%s", m->id.name); +    else +        desc = appfsp(desc, "%s %s", m->id.a_or_d ? "Digital" : "Analog", "Display"); + +    return desc; +} + +gchar **get_output_lines(const char *cmd_line) { +    gboolean spawned; +    gchar *out, *err; +    gchar **ret = NULL; + +    spawned = g_spawn_command_line_sync(cmd_line, +            &out, &err, NULL, NULL); +    if (spawned) { +        ret = g_strsplit(out, "\n", -1); +        g_free(out); +        g_free(err); +    } +    return ret; +} + +static gchar *tag_make_safe_inplace(gchar *tag) { +    if (!tag) +        return tag; +    if (!g_utf8_validate(tag, -1, NULL)) +        return tag; //TODO: reconsider +    gchar *p = tag, *pd = tag; +    while(*p) { +        gchar *np = g_utf8_next_char(p); +        gunichar c = g_utf8_get_char_validated(p, -1); +        int l = g_unichar_to_utf8(c, NULL); +        if (l == 1 && g_unichar_isalnum(c)) { +            g_unichar_to_utf8(c, pd); +        } else { +            *pd = '_'; +        } +        p = np; +        pd++; +    } +    return tag; +} + +static gchar *make_edid_section(monitor *m) { +    if (m->edid_len) { +        const gchar *vstr = monitor_vendor_str(m); + +        gchar *dom = NULL; +        if (m->id.week && m->id.year) +            dom = g_strdup_printf(_("Week %d of %d"), m->id.week, m->id.year); +        else if (m->id.year) +            dom = g_strdup_printf("%d", m->id.year); + +        gchar *bpcc = NULL; +        if (m->id.bpc) +            bpcc = g_strdup_printf("%d", m->id.bpc); + +        gchar *scr_size = NULL; +        if (m->id.horiz_cm && m->id.vert_cm) +            scr_size = g_strdup_printf("%d cm × %d cm", m->id.horiz_cm, m->id.vert_cm); + +        int aok = m->id.checksum_ok; +        if (m->id.ext_blocks_fail) aok = 0; +        gchar *csum = aok ? _("Ok") : _("Fail"); + +        gchar *ret = g_strdup_printf("[%s]\n" +            "%s=%d.%d\n" /* version */ +            "%s=%d %s\n" /* size */ +            "%s=%d\n" /* ext block */ +            "%s=%s %s\n" /* checksum */ +            "[%s]\n" +            "%s=%s\n" /* vendor */ +            "%s=%s\n" /* name */ +            "%s=%s\n" /* dom */ +            "%s=%s\n" /* size */ +            "%s=%s\n" /* sig type */ +            "%s=%s\n" /* bpcc */ +            , +            _("EDID Meta"), +            _("Version"), (int)m->id.ver_major, (int)m->id.ver_minor, +            _("Data Size"), m->id.size, _("bytes"), +            _("Extension Blocks"), m->id.ext_blocks, +            _("Checksum"), csum, aok ? "" : problem_marker(), +            _("EDID Device"), +            _("Vendor"), vstr, +            _("Name"), m->id.name, +            _("Manufacture Date"), UNKIFNULL2(dom), +            _("Screen Size"), UNKIFNULL2(scr_size), +            _("Signal Type"), m->id.a_or_d ? _("Digital") : _("Analog"), +            _("Bits per Color Channel"), UNKIFNULL2(bpcc) +            ); +        g_free(scr_size); +        g_free(bpcc); +        g_free(dom); +        //printf("ret: %s\n", ret); +        return ret; +    } else +        return g_strdup(""); +} + +gchar *monitors_get_info() { + +    gchar *icons = g_strdup(""); +    gchar *ret = g_strdup_printf("[%s]\n", _("Monitors")); +    gchar tag_prefix[] = "DEV"; + +    gchar **edid_files = get_output_lines("find /sys/devices -name edid"); +    //gchar **edid_files = get_output_lines("find /home/pburt/github/verbose-spork/junk/testing/.testing/edid/ -name edid.*"); +    int i, found = 0; +    for(i = 0; edid_files[i]; i++) { +        monitor *m = monitor_new_from_sysfs(edid_files[i]); +        if (m) { +            found++; +            if (m->id.checksum_ok && m->drm_connection) { +                gchar *tag = g_strdup_printf("%d-%s", found, m->drm_connection); +                tag_make_safe_inplace(tag); +                gchar *desc = monitor_name(m, TRUE); +                gchar *edid_section = make_edid_section(m); +                gchar *details = g_strdup_printf("[%s]\n" +                                "%s=%s\n" +                                "%s\n", +                                _("Connection"), +                                _("DRM"), m->drm_connection, +                                edid_section +                                ); +                moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ +                ret = h_strdup_cprintf("$!%s$%s=%s|%s\n", +                        ret, +                        tag, m->drm_connection, desc +                        ); +                icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, monitor_icon); +                g_free(desc); +                g_free(edid_section); +            } +            monitor_free(m); +        } +    } + +    no_monitors = FALSE; +    if(!found) { +        no_monitors = TRUE; +        g_free(ret); +        ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n", +                _("Monitors"), _("Result"), _("(Empty)") ); +    } else { +        ret = h_strdup_cprintf( +            "[$ShellParam$]\nViewType=1\n" +            "ColumnTitle$TextValue=%s\n" /* DRM connection */ +            "ColumnTitle$Value=%s\n"     /* Name */ +            "ShowColumnHeaders=true\n" +            "%s", +            ret, +            _("Connection"), +            _("Name"), +            icons +            ); +    } + +    return ret; +} + +gboolean monitors_hinote(const char **msg) { +    PARAM_NOT_UNUSED(msg); +    return FALSE; +}  | 
