Esempio n. 1
0
static void apply_psu_entry_token(char *token, char *value, void *data)
{
    PSUEntryPtr pPSUEntry = (PSUEntryPtr) data;

    if (!strcasecmp("current", token)) {
        pPSUEntry->psu_current = atoi(value);
    } else if (!strcasecmp("power", token)) {
        if (!strcasecmp("unknown", value)) {
            pPSUEntry->psu_power = -1;
        } else {
            pPSUEntry->psu_power = atoi(value);
        }
    } else if (!strcasecmp("voltage", token)) {
        if (!strcasecmp("unknown", value)) {
            pPSUEntry->psu_voltage = -1;
        } else {
            pPSUEntry->psu_voltage = atoi(value);
        }
    } else if (!strcasecmp("state", token)) {
        if (!strcasecmp("normal", value)) {
            pPSUEntry->psu_state = VCS_PSU_STATE_NORMAL;
        } else {
            pPSUEntry->psu_state = VCS_PSU_STATE_ABNORMAL;
        }
    } else {
        nv_warning_msg("Unknown PSU Entry token value pair: %s=%s",
                       token, value);
    }
}
Esempio n. 2
0
static void apply_thermal_entry_token(char *token, char *value, void *data)
{
    ThermalEntryPtr pThermalEntry = (ThermalEntryPtr) data;

    if (!strcasecmp("intake", token)) {
        pThermalEntry->intake_temp = atoi(value);
    } else if (!strcasecmp("exhaust", token)) {
        pThermalEntry->exhaust_temp = atoi(value);
    } else if (!strcasecmp("board", token)) {
        pThermalEntry->board_temp = atoi(value);
    } else {
        nv_warning_msg("Unknown Thermal Entry token value pair: %s=%s",
                       token, value);
    }
}
Esempio n. 3
0
static void apply_fan_entry_token(char *token, char *value, void *data)
{
    FanEntryPtr pFanEntry = (FanEntryPtr) data;

    if (!strcasecmp("fan", token)) {
        pFanEntry->fan_number = atoi(value);
    } else if (!strcasecmp("speed", token)) {
        pFanEntry->fan_speed = atoi(value);
    } else if (!strcasecmp("fail", token)) {
        pFanEntry->fan_failed = atoi(value);
    } else {
        nv_warning_msg("Unknown Fan Entry token value pair: %s=%s",
                       token, value);
    }
}
Esempio n. 4
0
static gboolean ctk_event_dispatch(GSource *source,
                                   GSourceFunc callback, gpointer user_data)
{
    XEvent event;
    CtkEventSource *event_source = (CtkEventSource *) source;
    CtkEventStruct event_struct;

    memset(&event_struct, 0, sizeof(event_struct));

    /*
     * if ctk_event_dispatch() is called, then either
     * ctk_event_prepare() or ctk_event_check() returned TRUE, so we
     * know there is an event pending
     */
    
    XNextEvent(event_source->dpy, &event);

    /* 
     * Handle the ATTRIBUTE_CHANGED_EVENT NV-CONTROL event
     */

    if (event_source->event_base != -1 &&
        (event.type == (event_source->event_base + ATTRIBUTE_CHANGED_EVENT))) {

        XNVCtrlAttributeChangedEvent *nvctrlevent =
            (XNVCtrlAttributeChangedEvent *) &event;

        /* make sure the attribute is in our signal array */

        if ((nvctrlevent->attribute <= NV_CTRL_LAST_ATTRIBUTE) &&
            (signals[nvctrlevent->attribute] != 0)) {
            
            event_struct.attribute    = nvctrlevent->attribute;
            event_struct.value        = nvctrlevent->value;
            event_struct.display_mask = nvctrlevent->display_mask;

            /*
             * XXX Is emitting a signal with g_signal_emit() really
             * the "correct" way of dispatching the event?
             */

            CTK_EVENT_BROADCAST(event_source,
                                signals[nvctrlevent->attribute],
                                &event_struct,
                                NV_CTRL_TARGET_TYPE_X_SCREEN,
                                nvctrlevent->screen);
        }

    /* 
     * Handle the TARGET_ATTRIBUTE_CHANGED_EVENT NV-CONTROL event
     */

    } else if (event_source->event_base != -1 &&
               (event.type == (event_source->event_base
                               +TARGET_ATTRIBUTE_CHANGED_EVENT))) {

        XNVCtrlAttributeChangedEventTarget *nvctrlevent =
            (XNVCtrlAttributeChangedEventTarget *) &event;

        /* make sure the attribute is in our signal array */

        if ((nvctrlevent->attribute <= NV_CTRL_LAST_ATTRIBUTE) &&
            (signals[nvctrlevent->attribute] != 0)) {
            
            event_struct.attribute    = nvctrlevent->attribute;
            event_struct.value        = nvctrlevent->value;
            event_struct.display_mask = nvctrlevent->display_mask;

            /*
             * XXX Is emitting a signal with g_signal_emit() really
             * the "correct" way of dispatching the event?
             */

            CTK_EVENT_BROADCAST(event_source,
                                signals[nvctrlevent->attribute],
                                &event_struct,
                                nvctrlevent->target_type,
                                nvctrlevent->target_id);
        }

        /*
         * Handle the TARGET_ATTRIBUTE_AVAILABILITY_CHANGED_EVENT
         * NV-CONTROL event.
         */

    } else if (event_source->event_base != -1 &&
               (event.type == (event_source->event_base
                               + TARGET_ATTRIBUTE_AVAILABILITY_CHANGED_EVENT))) {

        XNVCtrlAttributeChangedEventTargetAvailability *nvctrlevent =
            (XNVCtrlAttributeChangedEventTargetAvailability *) &event;

        /* make sure the attribute is in our signal array */

        if ((nvctrlevent->attribute <= NV_CTRL_LAST_ATTRIBUTE) &&
            (signals[nvctrlevent->attribute] != 0)) {
            
            event_struct.attribute    = nvctrlevent->attribute;
            event_struct.value        = nvctrlevent->value;
            event_struct.display_mask = nvctrlevent->display_mask;
            event_struct.is_availability_changed = TRUE;
            event_struct.availability = nvctrlevent->availability;

            /*
             * XXX Is emitting a signal with g_signal_emit() really
             * the "correct" way of dispatching the event?
             */

            CTK_EVENT_BROADCAST(event_source,
                                signals[nvctrlevent->attribute],
                                &event_struct,
                                nvctrlevent->target_type,
                                nvctrlevent->target_id);
        }
        /*
         * Handle the TARGET_STRING_ATTRIBUTE_CHANGED_EVENT
         * NV-CONTROL event.
         */
    } else if (event_source->event_base != -1 &&
               (event.type == (event_source->event_base
                               + TARGET_STRING_ATTRIBUTE_CHANGED_EVENT))) {
        XNVCtrlStringAttributeChangedEventTarget *nvctrlevent =
            (XNVCtrlStringAttributeChangedEventTarget *) &event;

        /* make sure the attribute is in our signal array */
        
        if ((nvctrlevent->attribute <= NV_CTRL_STRING_LAST_ATTRIBUTE) &&
            (string_signals[nvctrlevent->attribute] != 0)) {

            event_struct.attribute    = nvctrlevent->attribute;
            event_struct.value        = 0;
            event_struct.display_mask = nvctrlevent->display_mask;
            /*
             * XXX Is emitting a signal with g_signal_emit() really
             * the "correct" way of dispatching the event
             */

            CTK_EVENT_BROADCAST(event_source,
                                string_signals[nvctrlevent->attribute],
                                &event_struct,
                                nvctrlevent->target_type,
                                nvctrlevent->target_id);
        }
         /*
          * Handle the TARGET_BINARY_ATTRIBUTE_CHANGED_EVENT
          * NV-CONTROL event.
          */
    } else if (event_source->event_base != -1 &&
               (event.type == (event_source->event_base
                               + TARGET_BINARY_ATTRIBUTE_CHANGED_EVENT))) {
        XNVCtrlBinaryAttributeChangedEventTarget *nvctrlevent =
            (XNVCtrlBinaryAttributeChangedEventTarget *) &event;

        /* make sure the attribute is in our signal array */
        if ((nvctrlevent->attribute <= NV_CTRL_BINARY_DATA_LAST_ATTRIBUTE) &&
            (binary_signals[nvctrlevent->attribute] != 0)) {

            event_struct.attribute    = nvctrlevent->attribute;
            event_struct.value        = 0;
            event_struct.display_mask = nvctrlevent->display_mask;
            /*
             * XXX Is emitting a signal with g_signal_emit() really
             * the "correct" way of dispatching the event
             */

            CTK_EVENT_BROADCAST(event_source,
                                binary_signals[nvctrlevent->attribute],
                                &event_struct,
                                nvctrlevent->target_type,
                                nvctrlevent->target_id);
        }


        /*
         * Also handle XRandR events.
         */

    } else if (event_source->randr_event_base != -1 &&
               (event.type ==
                (event_source->randr_event_base + RRScreenChangeNotify))) {
        
        XRRScreenChangeNotifyEvent *xrandrevent =
            (XRRScreenChangeNotifyEvent *)&event;
        int screen;
        
        /* Find the screen the window belongs to */
        screen = get_screen_of_root(xrandrevent->display, xrandrevent->root);
        if (screen >= 0) {
            CTK_EVENT_BROADCAST(event_source,
                                signal_RRScreenChangeNotify,
                                &event,
                                NV_CTRL_TARGET_TYPE_X_SCREEN,
                                screen);
        }

    /*
     * Trap events that get registered but are not handled
     * properly.
     */

    } else {
        nv_warning_msg("Unknown event type %d.", event.type);
    }
    
    return TRUE;

} /* ctk_event_dispatch() */
NvCtrlXvAttributes * NvCtrlInitXvAttributes(NvCtrlAttributePrivateHandle *h)
{
    NvCtrlXvAttributes *xv = NULL;
    XvAdaptorInfo *ainfo;
    unsigned int req, event_base, error_base, nadaptors;
    int ret, i;
    const char *error_str = NULL;
    const char *warn_str = NULL;
    

    /* Check parameters */
    if ( !h || !h->dpy || h->target_type != NV_CTRL_TARGET_TYPE_X_SCREEN ) {
        goto fail;
    }


    /* Open libXv.so.1 */
    if ( !open_libxv() ) {
        warn_str = "Failed to open libXv.so.1: this library "
            "is not present in your system or is not in your "
            "LD_LIBRARY_PATH.";
        goto fail;
    }


    /* Allocate the attributes structure */
    xv = calloc(1, sizeof(NvCtrlXvAttributes));
    if ( xv == NULL ) {
        error_str = "Out of memory.";
        goto fail;
    }


    /* Verify server support of Xv extension */
    ret = __libXv->XvQueryExtension(h->dpy, &(xv->major_version), &(xv->minor_version), &req,
                                    &event_base, &error_base);
    if (ret != Success) goto fail;
    
    /* XXX do we have a minimum Xv version? */


    /* Get the list of adaptors */
    ret = __libXv->XvQueryAdaptors(h->dpy, RootWindow(h->dpy, h->target_id),
                                   &nadaptors, &ainfo);

    if (ret != Success || !nadaptors || !ainfo) goto fail;
    
    for (i = 0; i < nadaptors; i++) {
        
        if ((strcmp(ainfo[i].name, "NV17 Video Overlay") == 0) ||
            (strcmp(ainfo[i].name, "NV10 Video Overlay") == 0)) {
        
            NvCtrlXvOverlayAttributes *attrs;
            
            attrs = malloc(sizeof(NvCtrlXvOverlayAttributes));
            if ( !attrs ) {
                error_str = "Out of memory.";
                goto fail;
            }
        
            attrs->port = ainfo[i].base_id;
            attrs->saturation = getXvAttribute(h, attrs->port,
                                               "XV_SATURATION");
            attrs->contrast   = getXvAttribute(h, attrs->port,
                                               "XV_CONTRAST");
            attrs->brightness = getXvAttribute(h, attrs->port,
                                               "XV_BRIGHTNESS");
            attrs->hue        = getXvAttribute(h, attrs->port,
                                               "XV_HUE");
            attrs->defaults   = getXvAttribute(h, attrs->port,
                                               "XV_SET_DEFAULTS");
        
            if (!attrs->saturation ||
                !attrs->contrast ||
                !attrs->brightness ||
                !attrs->hue ||
                !attrs->defaults) {

                if (attrs->saturation) free(attrs->saturation);
                if (attrs->contrast)   free(attrs->contrast);
                if (attrs->brightness) free(attrs->brightness);
                if (attrs->hue)        free(attrs->hue);
                if (attrs->defaults)   free(attrs->defaults);
                
                free(attrs);
                attrs = NULL;
                
            } else {
                xv->overlay = attrs;
            }
        }

        if (strcmp(ainfo[i].name, "NV17 Video Texture") == 0) {
            
            NvCtrlXvTextureAttributes *attrs;
            
            attrs = malloc(sizeof(NvCtrlXvTextureAttributes));
            if ( !attrs ) {
                error_str = "Out of memory.";
                goto fail;
            }

            attrs->port = ainfo[i].base_id;
            attrs->sync_to_vblank = getXvAttribute(h, attrs->port,
                                                   "XV_SYNC_TO_VBLANK");
            attrs->contrast       = getXvAttribute(h, attrs->port,
                                                   "XV_CONTRAST");
            attrs->brightness     = getXvAttribute(h, attrs->port,
                                                   "XV_BRIGHTNESS");
            attrs->saturation     = getXvAttribute(h, attrs->port,
                                                   "XV_SATURATION");
            attrs->hue            = getXvAttribute(h, attrs->port,
                                                   "XV_HUE");
            attrs->defaults       = getXvAttribute(h, attrs->port,
                                                   "XV_SET_DEFAULTS");
            if (!attrs->sync_to_vblank ||
                !attrs->defaults) {
                
                if (attrs->sync_to_vblank) free(attrs->sync_to_vblank);
                if (attrs->defaults)       free(attrs->defaults);
                
                free(attrs);
                attrs = NULL;
                
            } else {
                xv->texture = attrs;
            }
        }

        if (strcmp(ainfo[i].name, "NV05 Video Blitter") == 0) {
            
            NvCtrlXvBlitterAttributes *attrs;

            attrs = malloc(sizeof(NvCtrlXvBlitterAttributes));
            if ( !attrs ) {
                error_str = "Out of memory.";
                goto fail;
            }

            attrs->port = ainfo[i].base_id;
            attrs->sync_to_vblank = getXvAttribute(h, attrs->port,
                                                   "XV_SYNC_TO_VBLANK");
            attrs->defaults       = getXvAttribute(h, attrs->port,
                                                   "XV_SET_DEFAULTS");
            if (!attrs->sync_to_vblank ||
                !attrs->defaults) {
                
                if (attrs->sync_to_vblank) free(attrs->sync_to_vblank);
                if (attrs->defaults)       free(attrs->defaults);
                
                free(attrs);
                attrs = NULL;
                
            } else {
                xv->blitter = attrs;
            }
        }
    }

    return xv;


    /* Handle failures */
 fail:
    if (error_str) {
        nv_error_msg("libXv setup error: %s\n", error_str);
    }
    if (warn_str) {
        nv_warning_msg("libXv setup warning: %s\n", warn_str);
    }
    if (xv != NULL) {
        if (xv->overlay) {
            free(xv->overlay);
        }
        if (xv->texture) {
            free(xv->texture);
        }
        if (xv->blitter) {
            free(xv->blitter);
        }
        free(xv);
    }

    return NULL;

} /* NvCtrlInitXvAttributes() */
Esempio n. 6
0
static int parse_config_property(const char *file, const char *line, ConfigProperties *conf)
{
    char *no_spaces, *s;
    char *locale;
    ConfigPropertiesTableEntry *t;
    char *timer, *token;
    TimerConfigProperty *c = NULL;
    int interval;
    int ret = NV_FALSE;
    unsigned int flag;
    
    no_spaces = remove_spaces(line);

    if (!no_spaces) goto done;

    s = strchr(no_spaces, '=');

    if (!s) goto done;

    *s = '\0';
    
    if (nv_strcasecmp(no_spaces, "RcFileLocale")) {
        locale = ++s;
        if (setlocale(LC_NUMERIC, locale) == NULL) {
            nv_warning_msg("Error parsing configuration file '%s': could "
                           "not set the specified locale '%s'.",
                           file, locale);
        }
    } else if (nv_strcasecmp(no_spaces, "Timer")) {
        timer = ++s;

        token = strtok(timer, ",");
        if (!token)
            goto done;

        c = malloc(sizeof(TimerConfigProperty));
        if (!c) {
            nv_warning_msg("Error parsing configuration file '%s': could "
                           "not allocate memory for timer '%s'.",
                           file, timer);
            ret = NV_TRUE;
            goto done;
        }

        c->description = replace_characters(token, '_', ' ');
        if (!c->description) {
            nv_warning_msg("Error parsing configuration file '%s': could "
                           "not allocate memory for timer '%s'.",
                           file, timer);
            ret = NV_TRUE;
            goto done;
        }

        token = strtok(NULL, ",");
        if (!token)
            goto done;

        if (nv_strcasecmp(token, "Yes")) {
            c->user_enabled = 1;
        } else if (nv_strcasecmp(token, "No")) {
            c->user_enabled = 0;
        } else {
            goto done;
        }

        token = strtok(NULL, ",");
        if (!token)
            goto done;

        parse_read_integer(token, &interval);
        c->interval = interval;

        c->next = conf->timers;
        conf->timers = c;
    } else {
        for (t = configPropertyTable, flag = 0; t->name; t++) {
            if (nv_strcasecmp(no_spaces, t->name)) {
                flag = t->flag;
                break;
            }
        }

        if (!flag) goto done;

        s++;

        if (nv_strcasecmp(s, "yes")) {
            conf->booleans |= flag;
        } else if (nv_strcasecmp(s, "no")) {
            conf->booleans &= ~flag;
        } else {
            goto done;
        }
    }
    
    ret = NV_TRUE;

 done:

    if ((ret != NV_TRUE) && c) {
        if (c->description)
            free(c->description);
        free(c);
    }

    if (no_spaces) free(no_spaces);
    return ret;
    
} /* parse_config_property() */
Esempio n. 7
0
int nv_write_config_file(const char *filename, CtrlHandles *h,
                         ParsedAttribute *p, ConfigProperties *conf)
{
    int screen, ret, entry, bit, val;
    FILE *stream;
    time_t now;
    AttributeTableEntry *a;
    ReturnStatus status;
    NVCTRLAttributeValidValuesRec valid;
    uint32 mask;
    CtrlHandleTarget *t;
    char *tmp_d_str, *prefix, scratch[4];
    const char *tmp;
    char *locale = "C";

    if (!filename) {
        nv_error_msg("Unable to open configuration file for writing.");
        return NV_FALSE;
    }

    stream = fopen(filename, "w");
    if (!stream) {
        nv_error_msg("Unable to open file '%s' for writing.", filename);
        return NV_FALSE;
    }
    
    /* write header */
    
    now = time(NULL);
    
    fprintf(stream, "#\n");
    fprintf(stream, "# %s\n", filename);
    fprintf(stream, "#\n");
    fprintf(stream, "# Configuration file for nvidia-settings - the NVIDIA "
            "X Server Settings utility\n");

    /* NOTE: ctime(3) generates a new line */
    
    fprintf(stream, "# Generated on %s", ctime(&now));
    fprintf(stream, "#\n");
    
    /*
     * set the locale to "C" before writing the configuration file to
     * reduce the risk of locale related parsing problems.  Restore
     * the original locale before exiting this function.
     */

    if (setlocale(LC_NUMERIC, "C") == NULL) {
        nv_warning_msg("Error writing configuration file '%s': could "
                       "not set the locale 'C'.", filename);
        locale = conf->locale;
    }

    /* write the values in ConfigProperties */

    write_config_properties(stream, conf, locale);

    /* for each screen, query each attribute in the table */

    fprintf(stream, "\n");
    fprintf(stream, "# Attributes:\n");
    fprintf(stream, "\n");

    /*
     * Note: we only save writable attributes addressable by X screen
     * (i.e., we don't look at other target types, yet).
     */
    
    for (screen = 0; screen < h->targets[X_SCREEN_TARGET].n; screen++) {

        t = &h->targets[X_SCREEN_TARGET].t[screen];

        /* skip it if we don't have a handle for this screen */

        if (!t->h) continue;

        /*
         * construct the prefix that will be printed in the config
         * file infront of each attribute on this screen; this will
         * either be "[screen]" or "[displayname]".
         */

        if (conf->booleans &
            CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE) {
            prefix = t->name;
        } else {
            snprintf(scratch, 4, "%d", screen);
            prefix = scratch;
        }

        /* loop over all the entries in the table */

        for (entry = 0; attributeTable[entry].name; entry++) {

            a = &attributeTable[entry];
            
            /* 
             * skip all attributes that are not supposed to be written
             * to the config file
             */

            if (a->flags & NV_PARSER_TYPE_NO_CONFIG_WRITE) continue;

            /*
             * special case the color attributes because we want to
             * print floats
             */
            
            if (a->flags & NV_PARSER_TYPE_COLOR_ATTRIBUTE) {
                float c[3], b[3], g[3];
                status = NvCtrlGetColorAttributes(t->h, c, b, g);
                if (status != NvCtrlSuccess) continue;
                fprintf(stream, "%s%c%s=%f\n",
                        prefix, DISPLAY_NAME_SEPARATOR, a->name,
                        get_color_value(a->attr, c, b, g));
                continue;
            }
            
            for (bit = 0; bit < 24; bit++) {
                
                mask = 1 << bit;

                /*
                 * if this bit is not present in the screens's enabled
                 * display device mask (and the X screen has enabled
                 * display devices), skip to the next bit
                 */

                if (((t->d & mask) == 0x0) && (t->d)) continue;

                status = NvCtrlGetValidDisplayAttributeValues
                    (t->h, mask, a->attr, &valid);

                if (status != NvCtrlSuccess) goto exit_bit_loop;
                
                if ((valid.permissions & ATTRIBUTE_TYPE_WRITE) == 0x0)
                    goto exit_bit_loop;
                
                status = NvCtrlGetDisplayAttribute(t->h, mask, a->attr, &val);
                
                if (status != NvCtrlSuccess) goto exit_bit_loop;
                
                if (valid.permissions & ATTRIBUTE_TYPE_DISPLAY) {

                    tmp_d_str =
                        display_device_mask_to_display_device_name(mask);

                    fprintf(stream, "%s%c%s[%s]=%d\n", prefix,
                            DISPLAY_NAME_SEPARATOR, a->name, tmp_d_str, val);
                    
                    free(tmp_d_str);
                    
                    continue;
                    
                } else {

                    fprintf(stream, "%s%c%s=%d\n", prefix,
                            DISPLAY_NAME_SEPARATOR, a->name, val);

                    /* fall through to exit_bit_loop */
                }
                
            exit_bit_loop:

                bit = 25; /* XXX force us out of the display device loop */
                
            } /* bit */
            
        } /* entry */
        
    } /* screen */
    
    /*
     * loop the ParsedAttribute list, writing the attributes to file.
     * note that we ignore conf->include_display_name_in_config_file
     * when writing these parsed attributes; this is because parsed
     * attributes (like the framelock properties) require a display
     * name be specified (since there are multiple X servers
     * involved).
     */

    while (p) {
        char target_str[64];

        if (!p->next) {
            p = p->next;
            continue;
        }

        tmp = nv_get_attribute_name(p->attr, NV_PARSER_TYPE_STRING_ATTRIBUTE,
                                    p->flags);
        if (!tmp) {
            nv_error_msg("Failure to save unknown attribute %d.", p->attr);
            p = p->next;
            continue;
        }

        /*
         * if the parsed attribute has a target specification, and a
         * target type other than an X screen, include a target
         * specification in what we write to the .rc file.
         */
        
        target_str[0] = '\0';
        
        if ((p->flags & NV_PARSER_HAS_TARGET) &&
            (p->target_type != NV_CTRL_TARGET_TYPE_X_SCREEN)) {
            
            int j;
            
            /* Find the target name of the target type */
            for (j = 0; targetTypeTable[j].name; j++) {
                if (targetTypeTable[j].nvctrl == p->target_type) {
                    snprintf(target_str, 64, "[%s:%d]",
                             targetTypeTable[j].parsed_name, p->target_id);
                    break;
                }
            }
        }
        
        if (p->display_device_mask) {
            
            tmp_d_str = display_device_mask_to_display_device_name
                (p->display_device_mask);
            
            fprintf(stream, "%s%s%c%s[%s]=%d\n", p->display, target_str,
                    DISPLAY_NAME_SEPARATOR, tmp, tmp_d_str, p->val);
            
            free(tmp_d_str);
            
        } else {
                
            fprintf(stream, "%s%s%c%s=%d\n", p->display, target_str,
                    DISPLAY_NAME_SEPARATOR, tmp, p->val);
        }
        
        p = p->next;
    }

    setlocale(LC_NUMERIC, conf->locale);

    /* close the configuration file */

    ret = fclose(stream);
    if (ret != 0) {
        nv_error_msg("Failure while closing file '%s'.", filename);
        return NV_FALSE;
    }
    
    return NV_TRUE;
    
} /* nv_write_config_file() */
Esempio n. 8
0
int nv_read_config_file(const char *file, const char *display_name,
                        ParsedAttribute *p, ConfigProperties *conf)
{
    int fd, ret, length;
    struct stat stat_buf;
    char *buf, *locale;
    ParsedAttributeWrapper *w = NULL;

    if (!file) {
        /*
         * file is NULL, likely because tilde_expansion() failed and
         * returned NULL; silently fail
         */
        return NV_FALSE;
    }
    
    /* open the file */

    fd = open(file, O_RDONLY);
    if (fd == -1) {
        /*
         * It's OK if the file doesn't exist... but should we print a
         * warning?
         */
        return NV_FALSE;
    }
    
    /* get the size of the file */

    ret = fstat(fd, &stat_buf);
    if (ret == -1) {
        nv_error_msg("Unable to determine size of file '%s' (%s).",
                     file, strerror(errno));
        return NV_FALSE;
    }

    if (stat_buf.st_size == 0) {
        nv_warning_msg("File '%s' has zero size; not reading.", file);
        close(fd);
        return NV_TRUE;
    }

    length = stat_buf.st_size;

    /* map the file into memory */

    buf = mmap(0, length, PROT_READ, MAP_SHARED, fd, 0);
    if (buf == (void *) -1) {
        nv_error_msg("Unable to mmap file '%s' for reading (%s).",
                     file, strerror(errno));
        return NV_FALSE;
    }


    /* 
     * save the current locale, parse the actual text in the file
     * and restore the saved locale (could be changed).
     */

    locale = strdup(conf->locale);

    w = parse_config_file(buf, file, length, conf);

    setlocale(LC_NUMERIC, locale);
    free(locale);

    /* unmap and close the file */

    if (munmap (buf, stat_buf.st_size) == -1) {
        nv_error_msg("Unable to unmap file '%s' after reading (%s).",
                     file, strerror(errno));
        return NV_FALSE;
    }

    close(fd);
    
    if (!w) return NV_FALSE;

    /* process the parsed attributes */

    ret = process_config_file_attributes(file, w, display_name);

    /*
     * add any relevant parsed attributes back to the list to be
     * passed to the gui
     */

    save_gui_parsed_attributes(w, p);
    
    if (w) free(w);

    return ret;
    
} /* nv_read_config_file() */
Esempio n. 9
0
int nv_write_config_file(const char *filename, const CtrlSystem *system,
                         const ParsedAttribute *p, const ConfigProperties *conf)
{
    int ret, entry, val, randr_gamma_available;
    FILE *stream;
    time_t now;
    ReturnStatus status;
    CtrlAttributePerms perms;
    CtrlTargetNode *node;
    CtrlTarget *t;
    char *prefix, scratch[4];
    char *locale = "C";

    if (!filename) {
        nv_error_msg("Unable to open configuration file for writing.");
        return NV_FALSE;
    }

    stream = fopen(filename, "w");
    if (!stream) {
        nv_error_msg("Unable to open file '%s' for writing.", filename);
        return NV_FALSE;
    }
    
    /* write header */
    
    now = time(NULL);
    
    fprintf(stream, "#\n");
    fprintf(stream, "# %s\n", filename);
    fprintf(stream, "#\n");
    fprintf(stream, "# Configuration file for nvidia-settings - the NVIDIA "
            "X Server Settings utility\n");

    /* NOTE: ctime(3) generates a new line */
    
    fprintf(stream, "# Generated on %s", ctime(&now));
    fprintf(stream, "#\n");
    
    /*
     * set the locale to "C" before writing the configuration file to
     * reduce the risk of locale related parsing problems.  Restore
     * the original locale before exiting this function.
     */

    if (setlocale(LC_NUMERIC, "C") == NULL) {
        nv_warning_msg("Error writing configuration file '%s': could "
                       "not set the locale 'C'.", filename);
        locale = conf->locale;
    }

    /* write the values in ConfigProperties */

    write_config_properties(stream, conf, locale);

    /* for each screen, query each attribute in the table */

    fprintf(stream, "\n");
    fprintf(stream, "# Attributes:\n");
    fprintf(stream, "\n");

    /*
     * Note: we only save writable attributes addressable by X screen here
     * followed by attributes for display target types.
     */

    for (node = system->targets[X_SCREEN_TARGET]; node; node = node->next) {

        t = node->t;

        /* skip it if we don't have a handle for this screen */

        if (!t->h) continue;

        /*
         * construct the prefix that will be printed in the config
         * file in front of each attribute on this screen; this will
         * either be "[screen]" or "[displayname]".
         */

        if (conf->booleans &
            CONFIG_PROPERTIES_INCLUDE_DISPLAY_NAME_IN_CONFIG_FILE) {
            prefix = t->name;
        } else {
            snprintf(scratch, 4, "%d", NvCtrlGetTargetId(t));
            prefix = scratch;
        }

        /* loop over all the entries in the table */

        for (entry = 0; entry < attributeTableLen; entry++) {
            const AttributeTableEntry *a = &attributeTable[entry];

            /*
             * skip all attributes that are not supposed to be written
             * to the config file
             */

            if (a->flags.no_config_write) {
                continue;
            }

            /*
             * special case the color attributes because we want to
             * print floats
             */

            if (a->type == CTRL_ATTRIBUTE_TYPE_COLOR) {
                float c[3], b[3], g[3];

                /*
                 * if we are using RandR gamma, skip saving the color info
                 */

                status = NvCtrlGetAttribute(t,
                                            NV_CTRL_ATTR_RANDR_GAMMA_AVAILABLE,
                                            &val);
                if (status == NvCtrlSuccess && val) continue;

                status = NvCtrlGetColorAttributes(t, c, b, g);
                if (status != NvCtrlSuccess) continue;
                fprintf(stream, "%s%c%s=%f\n",
                        prefix, DISPLAY_NAME_SEPARATOR, a->name,
                        get_color_value(a->attr, c, b, g));
                continue;
            }

            /* Only write out integer attributes, string and SDI CSC attributes
             * aren't written here.
             */
            if (a->type != CTRL_ATTRIBUTE_TYPE_INTEGER) {
                continue;
            }

            /*
             * Ignore display attributes (they are written later on) and only
             * write attributes that can be written for an X screen target
             */

            status = NvCtrlGetAttributePerms(t, a->type, a->attr, &perms);
            if (status != NvCtrlSuccess || !(perms.write) ||
                !(perms.valid_targets & CTRL_TARGET_PERM_BIT(X_SCREEN_TARGET)) ||
                (perms.valid_targets & CTRL_TARGET_PERM_BIT(DISPLAY_TARGET))) {
                continue;
            }

            status = NvCtrlGetAttribute(t, a->attr, &val);
            if (status != NvCtrlSuccess) {
                continue;
            }

            if (a->f.int_flags.is_display_id) {
                const char *name = NvCtrlGetDisplayConfigName(system, val);
                if (name) {
                    fprintf(stream, "%s%c%s=%s\n", prefix,
                            DISPLAY_NAME_SEPARATOR, a->name, name);
                }
                continue;
            }

            fprintf(stream, "%s%c%s=%d\n", prefix,
                    DISPLAY_NAME_SEPARATOR, a->name, val);

        } /* entry */

    } /* screen */

    /*
     * Write attributes addressable to display targets
     */

    for (node = system->targets[DISPLAY_TARGET]; node; node = node->next) {

        t = node->t;

        /* skip it if we don't have a handle for this display */

        if (!t->h) continue;

        /* 
         * check to see if we have RANDR gamma available. We may
         * skip writing attributes if it is missing. 
         */

        status = NvCtrlGetAttribute(t, 
                                    NV_CTRL_ATTR_RANDR_GAMMA_AVAILABLE,
                                    &randr_gamma_available);
        if (status != NvCtrlSuccess) {
            randr_gamma_available = 0;
        }

        /* Get the prefix we want to use for the display device target */

        prefix = create_display_device_target_string(t, conf);

        /* loop over all the entries in the table */

        for (entry = 0; entry < attributeTableLen; entry++) {
            const AttributeTableEntry *a = &attributeTable[entry];

            /*
             * skip all attributes that are not supposed to be written
             * to the config file
             */

            if (a->flags.no_config_write) {
                continue;
            }

            /*
             * for the display target we only write color attributes for now
             */

            if (a->type == CTRL_ATTRIBUTE_TYPE_COLOR) {
                float c[3], b[3], g[3];

                if (!randr_gamma_available) continue;

                status = NvCtrlGetColorAttributes(t, c, b, g);
                if (status != NvCtrlSuccess) continue;

                fprintf(stream, "%s%c%s=%f\n",
                        prefix, DISPLAY_NAME_SEPARATOR, a->name,
                        get_color_value(a->attr, c, b, g));
                continue;
            }

            /* Only write out integer attributes, string and SDI CSC attributes
             * aren't written here.
             */
            if (a->type != CTRL_ATTRIBUTE_TYPE_INTEGER) {
                continue;
            }

            /* Make sure this is a display and writable attribute */

            status = NvCtrlGetAttributePerms(t, a->type, a->attr, &perms);
            if (status != NvCtrlSuccess || !(perms.write) ||
                !(perms.valid_targets & CTRL_TARGET_PERM_BIT(DISPLAY_TARGET))) {
                continue;
            }

            status = NvCtrlGetAttribute(t, a->attr, &val);
            if (status == NvCtrlSuccess) {
                fprintf(stream, "%s%c%s=%d\n", prefix,
                        DISPLAY_NAME_SEPARATOR, a->name, val);
            }
        }

        free(prefix);
    }
    
    /*
     * loop the ParsedAttribute list, writing the attributes to file.
     * note that we ignore conf->include_display_name_in_config_file
     * when writing these parsed attributes; this is because parsed
     * attributes (like the framelock properties) require a display
     * name be specified (since there are multiple X servers
     * involved).
     */

    while (p) {
        char target_str[64];
        const AttributeTableEntry *a = p->attr_entry;

        if (!p->next) {
            p = p->next;
            continue;
        }

        /*
         * if the parsed attribute has a target specification, and a
         * target type other than an X screen, include a target
         * specification in what we write to the .rc file.
         */

        target_str[0] = '\0';

        if (p->parser_flags.has_target &&
            (p->target_type != X_SCREEN_TARGET)) {

            const CtrlTargetTypeInfo *targetTypeInfo;

            /* Find the target name of the target type */
            targetTypeInfo = NvCtrlGetTargetTypeInfo(p->target_type);
            if (targetTypeInfo) {
                snprintf(target_str, 64, "[%s:%d]",
                         targetTypeInfo->parsed_name, p->target_id);
            }
        }

        if (a->flags.hijack_display_device) {
            fprintf(stream, "%s%s%c%s[0x%08x]=%d\n", p->display, target_str,
                    DISPLAY_NAME_SEPARATOR, a->name,
                    p->display_device_mask,
                    p->val.i);
        } else {
            fprintf(stream, "%s%s%c%s=%d\n", p->display, target_str,
                    DISPLAY_NAME_SEPARATOR, a->name, p->val.i);
        }


        p = p->next;
    }

    setlocale(LC_NUMERIC, conf->locale);

    /* close the configuration file */

    ret = fclose(stream);
    if (ret != 0) {
        nv_error_msg("Failure while closing file '%s'.", filename);
        return NV_FALSE;
    }
    
    return NV_TRUE;
    
} /* nv_write_config_file() */
Esempio n. 10
0
/*
 * get_screens_to_clone() - try to detect automatically how many heads has each
 * device in order to use that number to create more than two separate X
 * screens. If the user specifies the option --num-x-screens <quantity>, that
 * value is used. If neither the user specifies the quantity or the number of
 * heads can be detected automatically, it uses 2 heads (the standard
 * behavior). This function returns an array of size nscreens with the number
 * of screens to clone per screen candidate. The caller is responsible of
 * freeing the memory of that array.
 */
static int* get_screens_to_clone(Options *op,
                                 const XConfigScreenPtr *screen_candidates,
                                 int nscreens)
{
    DevicesPtr pDevices;
    int *screens_to_clone, *supported_screens;
    int i, j, devs_found;

    screens_to_clone = nvalloc(nscreens * sizeof(int));
    supported_screens = nvalloc(nscreens * sizeof(int));

    /* Detect the number of supported screens per screen candidate */
    devs_found = FALSE;
    pDevices = find_devices(op);
    if (pDevices) {
        for (i = 0; i < nscreens; i++) {
            int bus, slot, scratch;

            if (!screen_candidates[i]) {
                continue;
            }

            /* parse the bus id for this candidate screen */
            if (!xconfigParsePciBusString(screen_candidates[i]->device->busid,
                                          &bus, &slot, &scratch)) {
                continue;
            }

            for (j = 0; j < pDevices->nDevices; j++) {
                if ((pDevices->devices[j].dev.bus == bus) &&
                    (pDevices->devices[j].dev.slot == slot)) {

                    if (pDevices->devices[j].crtcs > 0) {
                        supported_screens[i] = pDevices->devices[j].crtcs;
                    }
                    break;
                }
            }
        }
        free_devices(pDevices);
        devs_found = TRUE;
    }

    /* If user has defined a number of screens per GPU, use that value */
    if (op->num_x_screens > 0) {
        for (i = 0; i < nscreens; i++) {
            if (!screen_candidates[i]) {
                continue;
            }

            /* Print error when user specifies more X screens than supported */
            if (devs_found && op->num_x_screens > supported_screens[i]) {
                nv_warning_msg("Number of X screens specified is higher than the "
                               "supported quantity (%d)", supported_screens[i]);
            }

            screens_to_clone[i] = op->num_x_screens;
        }

        goto done;
    }

    for (i = 0; i < nscreens; i++) {
        if (screen_candidates[i]) {
            if (devs_found) {
                /* If devices found, use the supported screens number */
                screens_to_clone[i] = supported_screens[i];
            }
            else {
                /* Default behavior (2 heads per GPU) */
                screens_to_clone[i] = 2;
            }
        }
    }

done:
    nvfree(supported_screens);
    return screens_to_clone;
}
Esempio n. 11
0
DevicesPtr find_devices(Options *op)
{
    DevicesPtr pDevices = NULL;
    DisplayDevicePtr pDisplayDevice;
    int i, j, n, count = 0;
    unsigned int mask, bit;
    DeviceRec tmpDevice;
    NvCfgPciDevice *devs = NULL;
    NvCfgBool is_primary_device;
    NvCfgBool ret;
    char *lib_path;
    void *lib_handle;

    NvCfgBool (*__getDevices)(int *n, NvCfgDevice **devs);
    NvCfgBool (*__openDevice)(int bus, int slot, NvCfgDeviceHandle *handle);
    NvCfgBool (*__getPciDevices)(int *n, NvCfgPciDevice **devs);
    NvCfgBool (*__openPciDevice)(int domain, int bus, int slot, int function,
                                 NvCfgDeviceHandle *handle);
    NvCfgBool (*__getNumCRTCs)(NvCfgDeviceHandle handle, int *crtcs);
    NvCfgBool (*__getProductName)(NvCfgDeviceHandle handle, char **name);
    NvCfgBool (*__getDisplayDevices)(NvCfgDeviceHandle handle,
                                     unsigned int *display_device_mask);
    NvCfgBool (*__getEDID)(NvCfgDeviceHandle handle,
                           unsigned int display_device,
                           NvCfgDisplayDeviceInformation *info);
    NvCfgBool (*__isPrimaryDevice)(NvCfgDeviceHandle handle,
                                  NvCfgBool *is_primary_device);
    NvCfgBool (*__closeDevice)(NvCfgDeviceHandle handle);
    NvCfgBool (*__getDeviceUUID)(NvCfgDeviceHandle handle, char **uuid);
    
    /* dlopen() the nvidia-cfg library */
    
#define __LIB_NAME "libnvidia-cfg.so.1"

    if (op->nvidia_cfg_path) {
        lib_path = nvstrcat(op->nvidia_cfg_path, "/", __LIB_NAME, NULL);
    } else {
        lib_path = strdup(__LIB_NAME);
    }
    
    lib_handle = dlopen(lib_path, RTLD_NOW);
    
    nvfree(lib_path);
    
    if (!lib_handle) {
        nv_warning_msg("error opening %s: %s.", __LIB_NAME, dlerror());
        return NULL;
    }
    
#define __GET_FUNC(proc, name)                                        \
    (proc) = dlsym(lib_handle, (name));                               \
    if (!(proc)) {                                                    \
        nv_warning_msg("error retrieving symbol %s from %s: %s",      \
                       (name), __LIB_NAME, dlerror());                \
        dlclose(lib_handle);                                          \
        return NULL;                                                  \
    }

    /* required functions */
    __GET_FUNC(__getDevices, "nvCfgGetDevices");
    __GET_FUNC(__openDevice, "nvCfgOpenDevice");
    __GET_FUNC(__getPciDevices, "nvCfgGetPciDevices");
    __GET_FUNC(__openPciDevice, "nvCfgOpenPciDevice");
    __GET_FUNC(__getNumCRTCs, "nvCfgGetNumCRTCs");
    __GET_FUNC(__getProductName, "nvCfgGetProductName");
    __GET_FUNC(__getDisplayDevices, "nvCfgGetDisplayDevices");
    __GET_FUNC(__getEDID, "nvCfgGetEDID");
    __GET_FUNC(__closeDevice, "nvCfgCloseDevice");
    __GET_FUNC(__getDeviceUUID, "nvCfgGetDeviceUUID");

    /* optional functions */
    __isPrimaryDevice = dlsym(lib_handle, "nvCfgIsPrimaryDevice");
    
    if (__getPciDevices(&count, &devs) != NVCFG_TRUE) {
        return NULL;
    }

    if (count == 0) return NULL;

    pDevices = nvalloc(sizeof(DevicesRec));
    
    pDevices->devices = nvalloc(sizeof(DeviceRec) * count);

    pDevices->nDevices = count;

    for (i = 0; i < count; i++) {
        
        pDevices->devices[i].dev = devs[i];
        
        if (__openPciDevice(devs[i].domain, devs[i].bus, devs[i].slot, 0,
                            &(pDevices->devices[i].handle)) != NVCFG_TRUE) {
            goto fail;
        }
        
        if (__getNumCRTCs(pDevices->devices[i].handle,
                          &pDevices->devices[i].crtcs) != NVCFG_TRUE) {
            goto fail;
        }

        if (__getProductName(pDevices->devices[i].handle,
                             &pDevices->devices[i].name) != NVCFG_TRUE) {
            /* This call may fail with little impact to the Device section */
            pDevices->devices[i].name = NULL;
        }

        if (__getDeviceUUID(pDevices->devices[i].handle,
                            &pDevices->devices[i].uuid) != NVCFG_TRUE) {
            goto fail;
        }
        if (__getDisplayDevices(pDevices->devices[i].handle, &mask) !=
            NVCFG_TRUE) {
            goto fail;
        }
        
        pDevices->devices[i].displayDeviceMask = mask;

        /* count the number of display devices */
        
        for (n = j = 0; j < 32; j++) {
            if (mask & (1 << j)) n++;
        }
        
        pDevices->devices[i].nDisplayDevices = n;

        if (n) {

            /* allocate the info array of the right size */
            
            pDevices->devices[i].displayDevices =
                nvalloc(sizeof(DisplayDeviceRec) * n);
            
            /* fill in the info array */
            
            for (n = j = 0; j < 32; j++) {
                bit = 1 << j;
                if (!(bit & mask)) continue;
                
                pDisplayDevice = &pDevices->devices[i].displayDevices[n];
                pDisplayDevice->mask = bit;

                if (__getEDID(pDevices->devices[i].handle, bit,
                              &pDisplayDevice->info) != NVCFG_TRUE) {
                    pDisplayDevice->info_valid = FALSE;
                } else {
                    pDisplayDevice->info_valid = TRUE;
                }
                n++;
            }
        } else {
            pDevices->devices[i].displayDevices = NULL;
        }

        if ((i != 0) && (__isPrimaryDevice != NULL) &&
            (__isPrimaryDevice(pDevices->devices[i].handle,
                               &is_primary_device) == NVCFG_TRUE) &&
            (is_primary_device == NVCFG_TRUE)) {
            memcpy(&tmpDevice, &pDevices->devices[0], sizeof(DeviceRec));
            memcpy(&pDevices->devices[0], &pDevices->devices[i], sizeof(DeviceRec));
            memcpy(&pDevices->devices[i], &tmpDevice, sizeof(DeviceRec));
        }
        
        ret = __closeDevice(pDevices->devices[i].handle);
        pDevices->devices[i].handle = NULL;

        if (ret != NVCFG_TRUE) {
            goto fail;
        }
    }
    
    goto done;
    
 fail:

    nv_warning_msg("Unable to use the nvidia-cfg library to query NVIDIA "
                   "hardware.");

    for (i = 0; i < pDevices->nDevices; i++) {
        /* close the opened device */
        if (pDevices->devices[i].handle) {
            __closeDevice(pDevices->devices[i].handle);
        }
    }

    free_devices(pDevices);
    pDevices = NULL;

    /* fall through */

 done:
    
    if (devs) free(devs);
    
    return pDevices;
    
} /* find_devices() */
Esempio n. 12
0
/*
 * Rebuilds the list of display devices available for syncing.
 */
static void xv_sync_to_display_rebuild_buttons(CtkXVideo *ctk_xvideo,
                                               gboolean update_status)
{
    ReturnStatus ret;
    int enabled_display_id;
    int *pData;
    int len;
    int i;

    GtkWidget *last_button;
    GtkWidget *button;
    CtrlTarget *ctrl_target = ctk_xvideo->ctrl_target;


    /* Remove all buttons */

    ctk_empty_container(ctk_xvideo->xv_sync_to_display_button_box);


    /* Rebuild the list based on the curren configuration */

    ret = NvCtrlGetAttribute(ctrl_target,
                             NV_CTRL_XV_SYNC_TO_DISPLAY_ID,
                             &enabled_display_id);
    if (ret != NvCtrlSuccess) {
        nv_warning_msg("Failed to query XV Sync display ID on X screen %d.",
                       NvCtrlGetTargetId(ctrl_target));
        return;
    }

    ret = NvCtrlGetBinaryAttribute(ctrl_target, 0,
                                   NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN,
                                   (unsigned char **)(&pData), &len);
    if (ret != NvCtrlSuccess) {
        nv_warning_msg("Failed to query list of displays assigned to X screen "
                       " %d.",
                       NvCtrlGetTargetId(ctrl_target));
        return;
    }


    /* Add a button for No Display Specified */
    last_button = NULL;
    button = xv_sync_to_display_radio_button_add(ctk_xvideo, NULL,
                                                 NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO);

    if (button) {
        /* Track the first button */
        if (!last_button) {
            ctk_xvideo->xv_sync_to_display_buttons = button;
        }

        last_button = button;
    }
    /* Add a button for each display device */
    for (i = 0; i < pData[0]; i++) {
        int display_id = pData[1+i];

        button = xv_sync_to_display_radio_button_add(ctk_xvideo,
                                                     last_button,
                                                     display_id);
        if (!button) {
            continue;
        }

        /* Make sure the enabled display is marked as so */
        if (display_id == enabled_display_id) {
            xv_sync_to_display_set_enabled(ctk_xvideo, button,
                                           update_status);
        }

        /* Track the first button */
        if (!last_button) {
            ctk_xvideo->xv_sync_to_display_buttons = button;
        }

        last_button = button;
    }

    gtk_widget_show_all(ctk_xvideo->xv_sync_to_display_button_box);
}
Esempio n. 13
0
static gboolean update_fan_status(CtkVcs *ctk_object)
{
    gint ret;
    char *fan_entry_str = NULL;
    char *tokens;
    GtkWidget *table;
    GtkWidget *label;
    FanEntry current_fan;
    gchar output_str[16];
    gint current_row;

    if (!ctk_object->fan_status_container) {
        return FALSE;
    }
    ret = NvCtrlGetStringAttribute(ctk_object->handle,
                                   NV_CTRL_STRING_VCSC_FAN_STATUS,
                                   &fan_entry_str);
    if (ret != NvCtrlSuccess) {
        return FALSE;
    }

    ctk_empty_container(ctk_object->fan_status_container);

    /* Generate the new table */

    table = gtk_table_new(1, 3, FALSE);
    gtk_table_set_row_spacings(GTK_TABLE(table), 3);
    gtk_table_set_col_spacings(GTK_TABLE(table), 15);
    gtk_container_set_border_width(GTK_CONTAINER(table), 5);
    gtk_box_pack_start(GTK_BOX(ctk_object->fan_status_container), 
                       table, FALSE, FALSE, 0);


    label = gtk_label_new("Fan Number");
    gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
    gtk_widget_set_size_request(label, ctk_object->req.width, -1);
    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
                     GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0);

    label = gtk_label_new("Fan Speed");
    gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1,
                     GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0);

    label = gtk_label_new("Fan Status");
    gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
    gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1,
                     GTK_FILL, GTK_FILL | GTK_EXPAND, 5, 0);


    /* Parse string of fan entries and populate table */
    current_row = 1;
    for (tokens = strtok(fan_entry_str, ";");
         tokens;
         tokens = strtok(NULL, ";")) {

        /* Invalidate fan entry */
        current_fan.fan_number = -1;
        current_fan.fan_speed = -1;
        current_fan.fan_failed = -1;

        parse_token_value_pairs(tokens, apply_fan_entry_token, &current_fan);

        if ((current_fan.fan_number != -1) &&
            (current_fan.fan_speed != -1) &&
            (current_fan.fan_failed != -1)) {
    
            gtk_table_resize(GTK_TABLE(table), current_row + 1, 3);
            g_snprintf(output_str, 16, "%d", current_fan.fan_number);
            label = gtk_label_new(output_str);
            gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
            gtk_widget_set_size_request(label, ctk_object->req.width, -1);
            gtk_table_attach(GTK_TABLE(table), label, 0, 1, current_row,
                             current_row + 1, GTK_FILL,
                             GTK_FILL | GTK_EXPAND, 5, 0);
 
            g_snprintf(output_str, 16, "%d rpm", current_fan.fan_speed);
            label = gtk_label_new(output_str);
            gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
            gtk_table_attach(GTK_TABLE(table), label, 1, 2, current_row,
                             current_row + 1, GTK_FILL,
                             GTK_FILL | GTK_EXPAND, 5, 0);


            if (!current_fan.fan_failed) {
                g_snprintf(output_str, 16, "Ok");
            } else {
                g_snprintf(output_str, 16, "Failed");
            }
            label = gtk_label_new(output_str);
            gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
            gtk_table_attach(GTK_TABLE(table), label, 2, 3, current_row,
                             current_row + 1, GTK_FILL,
                             GTK_FILL | GTK_EXPAND, 5, 0);

            current_row++;

        } else {
            nv_warning_msg("Incomplete Fan Entry (fan=%d, speed=%d, failFlag=%d)",
                           current_fan.fan_number,
                           current_fan.fan_speed,
                           current_fan.fan_failed);
        }
    }
    gtk_widget_show_all(table);
    XFree(fan_entry_str);
    return TRUE;
}