/*
 * Copyright (c) 2006-2008 NVIDIA, Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


/*
 * nv-control-targets.c - NV-CONTROL client that demonstrates how to
 * talk to various target types on an X Server (X Screens, GPU, FrameLock)
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/Xlib.h>

#include "NVCtrl.h"
#include "NVCtrlLib.h"

#include "nv-control-screen.h"



static void print_target_indices(const char *prefix, const int *pData)
{
    int i, first = 1;

    for (i = 1; i <= pData[0]; i++) {
        if (!first) {
            printf(", ");
        }
        first = 0;
        printf("%s-%d", prefix, pData[i]);
    }
}

static void print_display_device_target_indices(const int *pData)
{
    print_target_indices("DPY", pData);
}

static void print_cooler_target_indices(const int *pData)
{
    print_target_indices("COOLER", pData);
}

static void print_thermal_sensor_target_indices(const int *pData)
{
    print_target_indices("THERMAL-SENSOR", pData);
}

static void print_framelock_target_indices(const int *pData)
{
    print_target_indices("FRAMELOCK", pData);
}

int main(int argc, char *argv[])
{
    Display *dpy;
    Bool ret;

    int major, minor;

    int num_gpus, num_screens, num_syncs;
    int num_coolers;
    int num_thermal_sensors;
    int gpu, screen;
    int *pData;
    int len, j;
    char *str;

    
    
    /*
     * Open a display connection, and make sure the NV-CONTROL X
     * extension is present on the screen we want to use.
     */
    
    dpy = XOpenDisplay(NULL);
    if (!dpy) {
        fprintf(stderr, "Cannot open display '%s'.\n", XDisplayName(NULL));
        return 1;
    }

    screen = GetNvXScreen(dpy);

    ret = XNVCTRLQueryVersion(dpy, &major, &minor);
    if (ret != True) {
        fprintf(stderr, "The NV-CONTROL X extension does not exist on '%s'.\n",
                XDisplayName(NULL));
        return 1;
    }
    
    /* Print some information */

    printf("\n");
    printf("Using NV-CONTROL extension %d.%d on %s\n",
           major, minor, XDisplayName(NULL));


    /* Start printing server system information */

    printf("\n");
    printf("Server System Information:\n");
    printf("\n");


    /* Get the number of gpus in the system */

    ret = XNVCTRLQueryTargetCount(dpy, NV_CTRL_TARGET_TYPE_GPU, &num_gpus);
    if (!ret) {
        fprintf(stderr, "Failed to query number of gpus\n");
        return 1;
    }
    printf("  number of GPUs: %d\n", num_gpus);


    /* Get the number of X Screens in the system */

    ret = XNVCTRLQueryTargetCount(dpy, NV_CTRL_TARGET_TYPE_X_SCREEN,
                                  &num_screens);
    if (!ret) {
        fprintf(stderr, "Failed to query number of xscreens\n");
        return 1;
    }
    printf("  number of X Screens: %d\n", num_screens);


    /* Get the number of display devices in the system */

    ret = XNVCTRLQueryTargetBinaryData
        (dpy,
         NV_CTRL_TARGET_TYPE_GPU,
         0, // target_id
         0, // display_mask
         NV_CTRL_BINARY_DATA_DISPLAY_TARGETS,
         (unsigned char **) &pData,
         &len);
    if (!ret) {
        fprintf(stderr, "Failed to query number of display devices\n");
        return 1;
    }
    printf("  number of display devices: %d (", pData[0]);
    print_display_device_target_indices(pData);
    printf(")\n");
    XFree(pData);


    /* Get the number of Frame Lock devices in the system */

    ret = XNVCTRLQueryTargetCount(dpy, NV_CTRL_TARGET_TYPE_FRAMELOCK,
                                  &num_syncs);
    if (!ret) {
        fprintf(stderr, "Failed to query number of xscreens\n");
        return 1;
    }
    printf("  number of Frame Lock Devices: %d\n", num_syncs);


    /* Get the number of Cooler devices in the system */

    ret = XNVCTRLQueryTargetCount(dpy, NV_CTRL_TARGET_TYPE_COOLER,
                                  &num_coolers);
    if (!ret) {
        fprintf(stderr, "Failed to query number of Coolers\n");
        return 1;
    }
    printf("  number of Cooler Devices: %d\n",
           num_coolers);


    /* Get the number of Thermal Sensor devices in the system */

    ret = XNVCTRLQueryTargetCount(dpy, NV_CTRL_TARGET_TYPE_THERMAL_SENSOR,
                                  &num_thermal_sensors);
    if (!ret) {
        fprintf(stderr, "Failed to query number of Thermal Sensors\n");
        return 1;
    }
    printf("  number of Thermal Sensor Devices: %d\n",
           num_thermal_sensors);


    /* display information about all GPUs */

    for (gpu = 0; gpu < num_gpus; gpu++) {

        int *pDisplayData;

        printf("\n\n");
        printf("GPU %d information:\n", gpu);


        /* GPU name */

        ret = XNVCTRLQueryTargetStringAttribute(dpy,
                                                NV_CTRL_TARGET_TYPE_GPU,
                                                gpu, // target_id
                                                0, // display_mask
                                                NV_CTRL_STRING_PRODUCT_NAME,
                                                &str);
        if (!ret) {
            fprintf(stderr, "Failed to query gpu product name\n");
            return 1;
        }
        printf("   Product Name                    : %s\n", str);
        XFree(str);
        str = NULL;

        /* GPU UUID */

        ret = XNVCTRLQueryTargetStringAttribute(dpy,
                                                NV_CTRL_TARGET_TYPE_GPU,
                                                gpu, // target_id
                                                0, // display_mask
                                                NV_CTRL_STRING_GPU_UUID,
                                                &str);
        printf("   GPU UUID                        : %s\n",
               (ret ? str : "Unknown"));
        XFree(str);
        str = NULL;

        /* Coolers on GPU */

        ret = XNVCTRLQueryTargetBinaryData
            (dpy,
             NV_CTRL_TARGET_TYPE_GPU,
             gpu, // target_id
             0, // display_mask
             NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU,
             (unsigned char **) &pData,
             &len);
        if (!ret) {
            fprintf(stderr, "Failed to query connected coolers\n");
            return 1;
        }
        printf("   Coolers on GPU                  : ");
        print_cooler_target_indices(pData);
        XFree(pData);
        printf("\n");

        /* Thermal Sensors on GPU */

        ret = XNVCTRLQueryTargetBinaryData
            (dpy,
             NV_CTRL_TARGET_TYPE_GPU,
             gpu, // target_id
             0, // display_mask
             NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU,
             (unsigned char **) &pData,
             &len);
        if (!ret) {
            fprintf(stderr, "Failed to query connected thermal sensors\n");
            return 1;
        }
        printf("   Thermal Sensors on GPU          : ");
        print_thermal_sensor_target_indices(pData);
        XFree(pData);
        printf("\n");

        /* Connected Display Devices on GPU */

        ret = XNVCTRLQueryTargetBinaryData
            (dpy,
             NV_CTRL_TARGET_TYPE_GPU,
             gpu, // target_id
             0, // display_mask
             NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU,
             (unsigned char **) &pDisplayData,
             &len);
        if (!ret) {
            fprintf(stderr, "Failed to query connected displays\n");
            return 1;
        }
        printf("   Connected Display Devices       : ");
        print_display_device_target_indices(pDisplayData);
        XFree(pDisplayData);
        printf("\n");

        /* FrameLock Devices on GPU */

        ret = XNVCTRLQueryTargetBinaryData
            (dpy,
             NV_CTRL_TARGET_TYPE_GPU,
             gpu, // target_id
             0, // display_mask
             NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU,
             (unsigned char **) &pData,
             &len);
        if (!ret) {
            fprintf(stderr, "Failed to query framelock devices\n");
            return 1;
        }
        printf("   Framelock Devices               : ");
        print_framelock_target_indices(pData);
        XFree(pData);
        printf("\n");

        /* X Screens driven by this GPU */

        ret = XNVCTRLQueryTargetBinaryData
            (dpy,
             NV_CTRL_TARGET_TYPE_GPU,
             gpu, // target_id
             0, // display_mask
             NV_CTRL_BINARY_DATA_XSCREENS_USING_GPU,
             (unsigned char **) &pData,
             &len);
        if (!ret) {
            fprintf(stderr, "Failed to query list of X Screens\n");
            return 1;
        }
        printf("   Number of X Screens on GPU %d    : %d\n", gpu, pData[0]);


        /* List all X Screens on GPU */

        for (j = 1; j <= pData[0]; j++) {
            screen = pData[j];

            printf("\n");
            printf("   X Screen %d information:\n", screen);

            /* Assigned Display Devices on X Screen */

            ret = XNVCTRLQueryTargetBinaryData
                (dpy,
                 NV_CTRL_TARGET_TYPE_X_SCREEN,
                 screen, // target_id
                 0, // display_mask
                 NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN,
                 (unsigned char **) &pDisplayData,
                 &len);
            if (!ret) {
                fprintf(stderr, "Failed to query assigned displays\n");
                return 1;
            }
            printf("       Assigned Display Devices    : ");
            print_display_device_target_indices(pDisplayData);
            XFree(pDisplayData);
            printf("\n");

            /* Enabled Display Devices on X Screen */

            ret = XNVCTRLQueryTargetBinaryData
                (dpy,
                 NV_CTRL_TARGET_TYPE_X_SCREEN,
                 screen, // target_id
                 0, // display_mask
                 NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN,
                 (unsigned char **) &pDisplayData,
                 &len);
            if (!ret) {
                fprintf(stderr, "Failed to query assigned displays\n");
                return 1;
            }
            printf("       Enabled Display Devices     : ");
            print_display_device_target_indices(pDisplayData);
            XFree(pDisplayData);
            printf("\n");
        }
        XFree(pData);
    }

    return 0;
}
