"Fossies" - the Fresh Open Source Software Archive

Member "conky-1.12.2/src/nvidia.cc" (25 Apr 2021, 37378 Bytes) of package /linux/privat/conky-1.12.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "nvidia.cc" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.11.6_vs_1.12.1.

    1 /*
    2  *
    3  * Conky, a system monitor, based on torsmo
    4  *
    5  * Any original torsmo code is licensed under the BSD license
    6  *
    7  * All code written since the fork of torsmo is licensed under the GPL
    8  *
    9  * Please see COPYING for details
   10  *
   11  * Copyright (c) 2008 Markus Meissner
   12  * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al.
   13  *  (see AUTHORS)
   14  * All rights reserved.
   15  *
   16  * This program is free software: you can redistribute it and/or modify
   17  * it under the terms of the GNU General Public License as published by
   18  * the Free Software Foundation, either version 3 of the License, or
   19  * (at your option) any later version.
   20  *
   21  * This program is distributed in the hope that it will be useful,
   22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   24  * GNU General Public License for more details.
   25  * You should have received a copy of the GNU General Public License
   26  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   27  *
   28  */
   29 
   30 /*
   31  *
   32  * Author:
   33  * Fonic <fonic.maxxim@live.com>
   34  *
   35  * Things to do:
   36  * - Move decoding of GPU/MEM freqs to print_nvidia_value() using QUERY_SPECIAL
   37  *   so that all quirks are located there
   38  * - Implement nvs->print_type to allow control over how the value is printed
   39  *   (int, float, temperature...)
   40  *
   41  * Showcase (conky.conf):
   42  * --==| NVIDIA | ==--
   43  * GPU    ${nvidia gpufreq [target_id]}MHz (${nvidia gpufreqmin
   44  * [target_id]}-${nvidia gpufreqmax [target_id]}MHz) MEM    ${nvidia memfreq
   45  * [target_id]}MHz (${nvidia memfreqmin [target_id]}-${nvidia memfreqmax
   46  * [target_id]}MHz) MTR    ${nvidia mtrfreq [target_id]}MHz (${nvidia mtrfreqmin
   47  * [target_id]}-${nvidia mtrfreqmax [target_id]}MHz) PERF   Level ${nvidia
   48  * perflevel [target_id]} (${nvidia perflevelmin [target_id]}-${nvidia
   49  * perflevelmax [target_id]}), Mode: ${nvidia perfmode [target_id]} VRAM
   50  * ${nvidia memutil [target_id]}% (${nvidia memused [target_id]}MB/${nvidia
   51  * memtotal [target_id]}MB) LOAD   GPU ${nvidia gpuutil [target_id]}%, RAM
   52  * ${nvidia membwutil [target_id]}%, VIDEO ${nvidia videoutil [target_id]}%,
   53  * PCIe ${nvidia pcieutil [target_id]}% TEMP   GPU ${nvidia gputemp
   54  * [target_id]}°C (${nvidia gputempthreshold [target_id]}°C max.), SYS ${nvidia
   55  * ambienttemp [target_id]}°C FAN    ${nvidia fanspeed [target_id]} RPM
   56  * (${nvidia fanlevel [target_id]}%)
   57  *
   58  * Miscellaneous:
   59  * OPENGL ${nvidia imagequality [target_id]}
   60  * GPU    ${nvidia modelname [target_id]}
   61  * DRIVER ${nvidia driverversion [target_id]}
   62  *
   63  * --==| NVIDIA Bars |==--
   64  * LOAD  ${nvidiabar [height][,width] gpuutil [target_id]}
   65  * VRAM  ${nvidiabar [height][,width] memutil [target_id]}
   66  * RAM   ${nvidiabar [height][,width] membwutil [target_id]}
   67  * VIDEO ${nvidiabar [height][,width] videoutil [target_id]}
   68  * PCIe  ${nvidiabar [height][,width] pcieutil [target_id]}
   69  * Fan   ${nvidiabar [height][,width] fanlevel [target_id]}
   70  * TEMP  ${nvidiabar [height][,width] gputemp [target_id]}
   71  *
   72  * --==| NVIDIA Gauge |==--
   73  * LOAD  ${nvidiagauge [height][,width] gpuutil [target_id]}
   74  * VRAM  ${nvidiagauge [height][,width] memutil [target_id]}
   75  * RAM   ${nvidiagauge [height][,width] membwutil [target_id]}
   76  * VIDEO ${nvidiagauge [height][,width] videoutil [target_id]}
   77  * PCIe  ${nvidiagauge [height][,width] pcieutil [target_id]}
   78  * Fan   ${nvidiagauge [height][,width] fanlevel [target_id]}
   79  * TEMP  ${nvidiagauge [height][,width] gputemp [target_id]}
   80  *
   81  * --==| NVIDIA Graph |==-- (target_id is not optional in this case)
   82  * LOAD  ${nvidiagraph gpuutil [height][,width] [gradient color 1] [gradient
   83  * color 2] [scale] [-t] [-l] target_id} VRAM  ${nvidiagraph memutil
   84  * [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l]
   85  * target_id} RAM   ${nvidiagraph membwutil [height][,width] [gradient color 1]
   86  * [gradient color 2] [scale] [-t] [-l] target_id} VIDEO ${nvidiagraph videoutil
   87  * [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l]
   88  * target_id} PCIe  ${nvidiagraph pcieutil [height][,width] [gradient color 1]
   89  * [gradient color 2] [scale] [-t] [-l] target_id} Fan   ${nvidiagraph fanlevel
   90  * [height][,width] [gradient color 1] [gradient color 2] [scale] [-t] [-l]
   91  * target_id} TEMP  ${nvidiagraph gputemp [height][,width] [gradient color 1]
   92  * [gradient color 2] [scale] [-t] [-l] target_id}
   93  */
   94 
   95 #include "nvidia.h"
   96 #include <X11/Xlib.h>
   97 #include "NVCtrl/NVCtrl.h"
   98 #include "NVCtrl/NVCtrlLib.h"
   99 #include "conky.h"
  100 #include "logging.h"
  101 #include "temphelper.h"
  102 #include "x11.h"
  103 
  104 // Separators for nvidia string parsing
  105 // (sample: "perf=0, nvclock=324, nvclockmin=324, nvclockmax=324 ; perf=1,
  106 // nvclock=549, nvclockmin=549, nvclockmax=549")
  107 #define NV_KVPAIR_SEPARATORS ", ;"
  108 #define NV_KEYVAL_SEPARATORS "="
  109 
  110 // Module arguments
  111 const char *translate_module_argument[] = {
  112     "temp",  // Temperatures
  113     "gputemp", "threshold", "gputempthreshold", "ambient", "ambienttemp",
  114 
  115     "gpufreq",  // GPU frequency
  116     "gpufreqcur", "gpufreqmin", "gpufreqmax",
  117 
  118     "memfreq",  // Memory frequency
  119     "memfreqcur", "memfreqmin", "memfreqmax",
  120 
  121     "mtrfreq",  // Memory transfer rate frequency
  122     "mtrfreqcur", "mtrfreqmin", "mtrfreqmax",
  123 
  124     "perflevel",  // Performance levels
  125     "perflevelcur", "perflevelmin", "perflevelmax", "perfmode",
  126 
  127     "gpuutil",    // Load/utilization
  128     "membwutil",  // NOTE: this is the memory _bandwidth_ utilization, not the
  129                   // percentage of used/available memory!
  130     "videoutil", "pcieutil",
  131 
  132     "mem",  // RAM statistics
  133     "memused", "memfree", "memavail", "memmax", "memtotal", "memutil",
  134     "memperc",
  135 
  136     "fanspeed",  // Fan/cooler
  137     "fanlevel",
  138 
  139     "imagequality",  // Miscellaneous
  140     "modelname", "driverversion"};
  141 
  142 // Enum for module arguments
  143 typedef enum _ARG_ID {
  144   ARG_TEMP,
  145   ARG_GPU_TEMP,
  146   ARG_THRESHOLD,
  147   ARG_GPU_TEMP_THRESHOLD,
  148   ARG_AMBIENT,
  149   ARG_AMBIENT_TEMP,
  150 
  151   ARG_GPU_FREQ,
  152   ARG_GPU_FREQ_CUR,
  153   ARG_GPU_FREQ_MIN,
  154   ARG_GPU_FREQ_MAX,
  155 
  156   ARG_MEM_FREQ,
  157   ARG_MEM_FREQ_CUR,
  158   ARG_MEM_FREQ_MIN,
  159   ARG_MEM_FREQ_MAX,
  160 
  161   ARG_MTR_FREQ,
  162   ARG_MTR_FREQ_CUR,
  163   ARG_MTR_FREQ_MIN,
  164   ARG_MTR_FREQ_MAX,
  165 
  166   ARG_PERF_LEVEL,
  167   ARG_PERF_LEVEL_CUR,
  168   ARG_PERF_LEVEL_MIN,
  169   ARG_PERF_LEVEL_MAX,
  170   ARG_PERF_MODE,
  171 
  172   ARG_GPU_UTIL,
  173   ARG_MEM_BW_UTIL,
  174   ARG_VIDEO_UTIL,
  175   ARG_PCIE_UTIL,
  176 
  177   ARG_MEM,
  178   ARG_MEM_USED,
  179   ARG_MEM_FREE,
  180   ARG_MEM_AVAIL,
  181   ARG_MEM_MAX,
  182   ARG_MEM_TOTAL,
  183   ARG_MEM_UTIL,
  184   ARG_MEM_PERC,
  185 
  186   ARG_FAN_SPEED,
  187   ARG_FAN_LEVEL,
  188 
  189   ARG_IMAGEQUALITY,
  190   ARG_MODEL_NAME,
  191   ARG_DRIVER_VERSION,
  192 
  193   ARG_UNKNOWN
  194 } ARG_ID;
  195 
  196 // Nvidia query targets
  197 const int translate_nvidia_target[] = {
  198     NV_CTRL_TARGET_TYPE_X_SCREEN,
  199     NV_CTRL_TARGET_TYPE_GPU,
  200     NV_CTRL_TARGET_TYPE_FRAMELOCK,
  201     NV_CTRL_TARGET_TYPE_VCSC,
  202     NV_CTRL_TARGET_TYPE_GVI,
  203     NV_CTRL_TARGET_TYPE_COOLER,
  204     NV_CTRL_TARGET_TYPE_THERMAL_SENSOR,
  205     NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER,
  206     NV_CTRL_TARGET_TYPE_DISPLAY,
  207 };
  208 
  209 // Enum for nvidia query targets
  210 typedef enum _TARGET_ID {
  211   TARGET_SCREEN,
  212   TARGET_GPU,
  213   TARGET_FRAMELOCK,
  214   TARGET_VCSC,
  215   TARGET_GVI,
  216   TARGET_COOLER,
  217   TARGET_THERMAL,
  218   TARGET_3DVISION,
  219   TARGET_DISPLAY
  220 } TARGET_ID;
  221 
  222 // Nvidia query attributes
  223 const int translate_nvidia_attribute[] = {
  224     NV_CTRL_GPU_CORE_TEMPERATURE,
  225     NV_CTRL_GPU_CORE_THRESHOLD,
  226     NV_CTRL_AMBIENT_TEMPERATURE,
  227 
  228     NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
  229     NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
  230     NV_CTRL_STRING_PERFORMANCE_MODES,
  231     NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS,
  232     NV_CTRL_GPU_POWER_MIZER_MODE,
  233 
  234     NV_CTRL_STRING_GPU_UTILIZATION,
  235 
  236     NV_CTRL_USED_DEDICATED_GPU_MEMORY,
  237     0,
  238     NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY,  // NOTE: NV_CTRL_TOTAL_GPU_MEMORY would
  239                                          // be better, but returns KB instead of
  240                                          // MB
  241     0,
  242 
  243     NV_CTRL_THERMAL_COOLER_SPEED,
  244     NV_CTRL_THERMAL_COOLER_LEVEL,
  245 
  246     NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL,
  247     NV_CTRL_IMAGE_SETTINGS,
  248 
  249     NV_CTRL_STRING_PRODUCT_NAME,
  250     NV_CTRL_STRING_NVIDIA_DRIVER_VERSION,
  251 };
  252 
  253 // Enum for nvidia query attributes
  254 typedef enum _ATTR_ID {
  255   ATTR_GPU_TEMP,
  256   ATTR_GPU_TEMP_THRESHOLD,
  257   ATTR_AMBIENT_TEMP,
  258 
  259   ATTR_GPU_FREQ,
  260   ATTR_MEM_FREQ,
  261   ATTR_PERFMODES_STRING,
  262   ATTR_FREQS_STRING,
  263   ATTR_PERF_MODE,
  264 
  265   ATTR_UTILS_STRING,
  266 
  267   ATTR_MEM_USED,
  268   ATTR_MEM_FREE,
  269   ATTR_MEM_TOTAL,
  270   ATTR_MEM_UTIL,
  271 
  272   ATTR_FAN_SPEED,
  273   ATTR_FAN_LEVEL,
  274 
  275   ATTR_PERF_LEVEL,
  276   ATTR_IMAGE_QUALITY,
  277 
  278   ATTR_MODEL_NAME,
  279   ATTR_DRIVER_VERSION,
  280 } ATTR_ID;
  281 
  282 // Enum for query type
  283 typedef enum _QUERY_ID {
  284   QUERY_VALUE,
  285   QUERY_STRING,
  286   QUERY_STRING_VALUE,
  287   QUERY_SPECIAL
  288 } QUERY_ID;
  289 
  290 // Enum for string token search mode
  291 typedef enum _SEARCH_ID {
  292   SEARCH_FIRST,
  293   SEARCH_LAST,
  294   SEARCH_MIN,
  295   SEARCH_MAX
  296 } SEARCH_ID;
  297 
  298 // Translate special_type into command string
  299 const char *translate_nvidia_special_type[] = {
  300     "nvidia",       // NONSPECIAL
  301     "",             // HORIZONTAL_LINE
  302     "",             // STIPPLED_HR
  303     "nvidiabar",    // BAR
  304     "",             // FG
  305     "",             // BG
  306     "",             // OUTLINE
  307     "",             // ALIGNR
  308     "",             // ALIGNC
  309     "nvidiagague",  // GAUGE
  310     "nvidiagraph",  // GRAPH
  311     "",             // OFFSET
  312     "",             // VOFFSET
  313     "",             // SAVE_COORDINATES
  314     "",             // FONT
  315     "",             // GOTO
  316     ""              // TAB
  317 };
  318 
  319 // Global struct to keep track of queries
  320 class nvidia_s {
  321  public:
  322   nvidia_s()
  323       : command(0),
  324         arg(0),
  325         query(QUERY_VALUE),
  326         target(TARGET_SCREEN),
  327         attribute(ATTR_GPU_TEMP),
  328         token(0),
  329         search(SEARCH_FIRST),
  330         target_id(0) {}
  331   const char *command;
  332   const char *arg;
  333   QUERY_ID query;
  334   TARGET_ID target;
  335   ATTR_ID attribute;
  336   char *token;
  337   SEARCH_ID search;
  338   //  added new field for GPU id
  339   int target_id;
  340 };
  341 
  342 // Cache by value
  343 struct nvidia_c_value {
  344   int memtotal = -1;
  345   int gputempthreshold = -1;
  346 };
  347 
  348 // Cache by string
  349 struct nvidia_c_string {
  350   int nvclockmin = -1;
  351   int nvclockmax = -1;
  352   int memclockmin = -1;
  353   int memclockmax = -1;
  354   int memTransferRatemin = -1;
  355   int memTransferRatemax = -1;
  356   int perfmin = -1;
  357   int perfmax = -1;
  358 };
  359 
  360 static Display *nvdisplay = nullptr;
  361 
  362 // Maximum number of GPU connected:
  363 // For cache default value: choosed a model of direct access to array instead of
  364 // list for speed improvement value based on the incoming quad Naples tech
  365 // having 256 PCIe lanes available
  366 const int MAXNUMGPU = 64;
  367 
  368 namespace {
  369 class nvidia_display_setting
  370     : public conky::simple_config_setting<std::string> {
  371   typedef conky::simple_config_setting<std::string> Base;
  372 
  373  protected:
  374   virtual void lua_setter(lua::state &l, bool init);
  375   virtual void cleanup(lua::state &l);
  376 
  377  public:
  378   nvidia_display_setting() : Base("nvidia_display", std::string(), false) {}
  379 };
  380 
  381 void nvidia_display_setting::lua_setter(lua::state &l, bool init) {
  382   lua::stack_sentry s(l, -2);
  383 
  384   Base::lua_setter(l, init);
  385 
  386   std::string str = do_convert(l, -1).first;
  387   if (!str.empty()) {
  388     nvdisplay = XOpenDisplay(str.c_str());
  389     if (nvdisplay == nullptr) {
  390       CRIT_ERR(nullptr, NULL, "can't open nvidia display: %s",
  391                XDisplayName(str.c_str()));
  392     }
  393   }
  394 
  395   ++s;
  396 }  // namespace
  397 
  398 void nvidia_display_setting::cleanup(lua::state &l) {
  399   lua::stack_sentry s(l, -1);
  400 
  401   if (nvdisplay && nvdisplay != display) {
  402     XCloseDisplay(nvdisplay);
  403     nvdisplay = nullptr;
  404   }
  405 
  406   l.pop();
  407 }
  408 
  409 nvidia_display_setting nvidia_display;
  410 }  // namespace
  411 
  412 // Evaluate module parameters and prepare query
  413 int set_nvidia_query(struct text_object *obj, const char *arg,
  414                      unsigned int special_type) {
  415   nvidia_s *nvs;
  416   int aid;
  417   int ilen;
  418 
  419   // Initialize global struct
  420   nvs = new nvidia_s();
  421   obj->data.opaque = nvs;
  422 
  423   // Added new parameter parsing GPU_ID as 0,1,2,..
  424   // if no GPU_ID parameter then default to 0
  425   nvs->target_id = 0;
  426   char *strbuf = strdup(arg);
  427   char *p = strrchr(strbuf, ' ');
  428   if (p && *(p + 1)) {
  429     nvs->target_id = atoi(p + 1);
  430     if ((nvs->target_id > 0) || !strcmp(p + 1, "0")) {
  431       ilen = strlen(strbuf);
  432       ilen = ilen - strlen(p);
  433       strbuf[ilen] = 0;
  434       arg = strbuf;
  435     }
  436   }
  437 
  438   // If the value is negative it is set to 0
  439   if (nvs->target_id < 0) nvs->target_id = 0;
  440 
  441   // Extract arguments for nvidiabar, etc, and run set_nvidia_query
  442   switch (special_type) {
  443     case BAR:
  444       arg = scan_bar(obj, arg, 100);
  445       break;
  446     case GRAPH:
  447       arg = scan_graph(obj, arg, 100);
  448       break;
  449     case GAUGE:
  450       arg = scan_gauge(obj, arg, 100);
  451       break;
  452     default:
  453       break;
  454   }
  455 
  456   // Return error if no argument
  457   // (sometimes scan_graph gets excited and eats the whole string!
  458   if (!arg) {
  459     free_and_zero(strbuf);
  460     return 1;
  461   }
  462 
  463   // Translate parameter to id
  464   for (aid = 0; aid < ARG_UNKNOWN; aid++) {
  465     if (strcmp(arg, translate_module_argument[aid]) == 0) break;
  466   }
  467 
  468   // free the string buffer after arg is not anymore needed
  469   if (strbuf != nullptr) free_and_zero(strbuf);
  470 
  471   // Save pointers to the arg and command strings for debugging and printing
  472   nvs->arg = translate_module_argument[aid];
  473   nvs->command = translate_nvidia_special_type[special_type];
  474 
  475   // Evaluate parameter
  476   switch (aid) {
  477     case ARG_TEMP:  // GPU temperature
  478     case ARG_GPU_TEMP:
  479       nvs->query = QUERY_VALUE;
  480       nvs->target = TARGET_GPU;
  481       nvs->attribute = ATTR_GPU_TEMP;
  482       break;
  483     case ARG_THRESHOLD:  // GPU temperature threshold
  484     case ARG_GPU_TEMP_THRESHOLD:
  485       nvs->query = QUERY_VALUE;
  486       nvs->target = TARGET_GPU;
  487       nvs->attribute = ATTR_GPU_TEMP_THRESHOLD;
  488       break;
  489     case ARG_AMBIENT:  // Ambient temperature
  490     case ARG_AMBIENT_TEMP:
  491       nvs->query = QUERY_VALUE;
  492       nvs->target = TARGET_GPU;
  493       nvs->attribute = ATTR_AMBIENT_TEMP;
  494       break;
  495 
  496     case ARG_GPU_FREQ:  // Current GPU clock
  497     case ARG_GPU_FREQ_CUR:
  498       nvs->query = QUERY_VALUE;
  499       nvs->target = TARGET_GPU;
  500       nvs->attribute = ATTR_GPU_FREQ;
  501       break;
  502     case ARG_GPU_FREQ_MIN:  // Minimum GPU clock
  503       nvs->query = QUERY_STRING_VALUE;
  504       nvs->target = TARGET_GPU;
  505       nvs->attribute = ATTR_PERFMODES_STRING;
  506       nvs->token = (char *)"nvclockmin";
  507       nvs->search = SEARCH_MIN;
  508       break;
  509     case ARG_GPU_FREQ_MAX:  // Maximum GPU clock
  510       nvs->query = QUERY_STRING_VALUE;
  511       nvs->target = TARGET_GPU;
  512       nvs->attribute = ATTR_PERFMODES_STRING;
  513       nvs->token = (char *)"nvclockmax";
  514       nvs->search = SEARCH_MAX;
  515       break;
  516 
  517     case ARG_MEM_FREQ:  // Current memory clock
  518     case ARG_MEM_FREQ_CUR:
  519       nvs->query = QUERY_VALUE;
  520       nvs->target = TARGET_GPU;
  521       nvs->attribute = ATTR_MEM_FREQ;
  522       break;
  523     case ARG_MEM_FREQ_MIN:  // Minimum memory clock
  524       nvs->query = QUERY_STRING_VALUE;
  525       nvs->target = TARGET_GPU;
  526       nvs->attribute = ATTR_PERFMODES_STRING;
  527       nvs->token = (char *)"memclockmin";
  528       nvs->search = SEARCH_MIN;
  529       break;
  530     case ARG_MEM_FREQ_MAX:  // Maximum memory clock
  531       nvs->query = QUERY_STRING_VALUE;
  532       nvs->target = TARGET_GPU;
  533       nvs->attribute = ATTR_PERFMODES_STRING;
  534       nvs->token = (char *)"memclockmax";
  535       nvs->search = SEARCH_MAX;
  536       break;
  537 
  538     case ARG_MTR_FREQ:  // Current memory transfer rate clock
  539     case ARG_MTR_FREQ_CUR:
  540       nvs->query = QUERY_STRING_VALUE;
  541       nvs->target = TARGET_GPU;
  542       nvs->attribute = ATTR_FREQS_STRING;
  543       nvs->token = (char *)"memTransferRate";
  544       nvs->search = SEARCH_FIRST;
  545       break;
  546     case ARG_MTR_FREQ_MIN:  // Minimum memory transfer rate clock
  547       nvs->query = QUERY_STRING_VALUE;
  548       nvs->target = TARGET_GPU;
  549       nvs->attribute = ATTR_PERFMODES_STRING;
  550       nvs->token = (char *)"memTransferRatemin";
  551       nvs->search = SEARCH_MIN;
  552       break;
  553     case ARG_MTR_FREQ_MAX:  // Maximum memory transfer rate clock
  554       nvs->query = QUERY_STRING_VALUE;
  555       nvs->target = TARGET_GPU;
  556       nvs->attribute = ATTR_PERFMODES_STRING;
  557       nvs->token = (char *)"memTransferRatemax";
  558       nvs->search = SEARCH_MAX;
  559       break;
  560 
  561     case ARG_PERF_LEVEL:  // Current performance level
  562     case ARG_PERF_LEVEL_CUR:
  563       nvs->query = QUERY_VALUE;
  564       nvs->target = TARGET_GPU;
  565       nvs->attribute = ATTR_PERF_LEVEL;
  566       break;
  567     case ARG_PERF_LEVEL_MIN:  // Lowest performance level
  568       nvs->query = QUERY_STRING_VALUE;
  569       nvs->target = TARGET_GPU;
  570       nvs->attribute = ATTR_PERFMODES_STRING;
  571       nvs->token = (char *)"perf";
  572       nvs->search = SEARCH_MIN;
  573       break;
  574     case ARG_PERF_LEVEL_MAX:  // Highest performance level
  575       nvs->query = QUERY_STRING_VALUE;
  576       nvs->target = TARGET_GPU;
  577       nvs->attribute = ATTR_PERFMODES_STRING;
  578       nvs->token = (char *)"perf";
  579       nvs->search = SEARCH_MAX;
  580       break;
  581     case ARG_PERF_MODE:  // Performance mode
  582       nvs->query = QUERY_SPECIAL;
  583       nvs->target = TARGET_GPU;
  584       nvs->attribute = ATTR_PERF_MODE;
  585       break;
  586 
  587     case ARG_GPU_UTIL:  // GPU utilization %
  588       nvs->query = QUERY_STRING_VALUE;
  589       nvs->target = TARGET_GPU;
  590       nvs->attribute = ATTR_UTILS_STRING;
  591       nvs->token = (char *)"graphics";
  592       nvs->search = SEARCH_FIRST;
  593       break;
  594     case ARG_MEM_BW_UTIL:  // Memory bandwidth utilization %
  595       nvs->query = QUERY_STRING_VALUE;
  596       nvs->target = TARGET_GPU;
  597       nvs->attribute = ATTR_UTILS_STRING;
  598       nvs->token = (char *)"memory";
  599       nvs->search = SEARCH_FIRST;
  600       break;
  601     case ARG_VIDEO_UTIL:  // Video engine utilization %
  602       nvs->query = QUERY_STRING_VALUE;
  603       nvs->target = TARGET_GPU;
  604       nvs->attribute = ATTR_UTILS_STRING;
  605       nvs->token = (char *)"video";
  606       nvs->search = SEARCH_FIRST;
  607       break;
  608     case ARG_PCIE_UTIL:  // PCIe bandwidth utilization %
  609       nvs->query = QUERY_STRING_VALUE;
  610       nvs->target = TARGET_GPU;
  611       nvs->attribute = ATTR_UTILS_STRING;
  612       nvs->token = (char *)"PCIe";
  613       nvs->search = SEARCH_FIRST;
  614       break;
  615 
  616     case ARG_MEM:  // Amount of used memory
  617     case ARG_MEM_USED:
  618       nvs->query = QUERY_VALUE;
  619       nvs->target = TARGET_GPU;
  620       nvs->attribute = ATTR_MEM_USED;
  621       break;
  622     case ARG_MEM_FREE:  // Amount of free memory
  623     case ARG_MEM_AVAIL:
  624       nvs->query = QUERY_SPECIAL;
  625       nvs->target = TARGET_GPU;
  626       nvs->attribute = ATTR_MEM_FREE;
  627       break;
  628     case ARG_MEM_MAX:  // Total amount of memory
  629     case ARG_MEM_TOTAL:
  630       nvs->query = QUERY_VALUE;
  631       nvs->target = TARGET_GPU;
  632       nvs->attribute = ATTR_MEM_TOTAL;
  633       break;
  634     case ARG_MEM_UTIL:  // Memory utilization %
  635     case ARG_MEM_PERC:
  636       nvs->query = QUERY_SPECIAL;
  637       nvs->target = TARGET_GPU;
  638       nvs->attribute = ATTR_MEM_UTIL;
  639       break;
  640 
  641     case ARG_FAN_SPEED:  // Fan speed
  642       nvs->query = QUERY_VALUE;
  643       nvs->target = TARGET_COOLER;
  644       nvs->attribute = ATTR_FAN_SPEED;
  645       break;
  646     case ARG_FAN_LEVEL:  // Fan level %
  647       nvs->query = QUERY_VALUE;
  648       nvs->target = TARGET_COOLER;
  649       nvs->attribute = ATTR_FAN_LEVEL;
  650       break;
  651 
  652     case ARG_IMAGEQUALITY:  // Image quality
  653       nvs->query = QUERY_VALUE;
  654       nvs->target = TARGET_SCREEN;
  655       nvs->attribute = ATTR_IMAGE_QUALITY;
  656       break;
  657 
  658     case ARG_MODEL_NAME:
  659       nvs->query = QUERY_STRING;
  660       nvs->target = TARGET_GPU;
  661       nvs->attribute = ATTR_MODEL_NAME;
  662       break;
  663 
  664     case ARG_DRIVER_VERSION:
  665       nvs->query = QUERY_STRING;
  666       nvs->target = TARGET_GPU;
  667       nvs->attribute = ATTR_DRIVER_VERSION;
  668       break;
  669 
  670     default:  // Unknown/invalid argument
  671       // Error printed by core.cc
  672       return 1;
  673   }
  674   return 0;
  675 }
  676 
  677 // Return the amount of targets present or raise error)
  678 static inline int get_nvidia_target_count(Display *dpy, TARGET_ID tid) {
  679   int num_tgts;
  680   if (!XNVCTRLQueryTargetCount(dpy, translate_nvidia_target[tid], &num_tgts)) {
  681     num_tgts = -1;
  682   }
  683 
  684   if (num_tgts < 1 && tid == TARGET_GPU) {
  685     // Print error and exit if there's no NVIDIA's GPU
  686     NORM_ERR(nullptr, NULL,
  687              "%s:"
  688              "\n          Trying to query Nvidia target failed (using the "
  689              "proprietary drivers)."
  690              "\n          Are you sure they are installed correctly and a "
  691              "Nvidia GPU is in use?"
  692              "\n          (display: %d,Nvidia target_count: %d)",
  693              __func__, dpy, num_tgts);
  694   }
  695 
  696   return num_tgts;
  697 }
  698 
  699 static int cache_nvidia_value(TARGET_ID tid, ATTR_ID aid, Display *dpy,
  700                               int *value, int gid, const char *arg) {
  701   static nvidia_c_value ac_value[MAXNUMGPU];
  702 
  703   if (aid == ATTR_MEM_TOTAL) {
  704     if (ac_value[gid].memtotal < 0) {
  705       if (!dpy || !XNVCTRLQueryTargetAttribute(
  706                       dpy, translate_nvidia_target[tid], gid, 0,
  707                       translate_nvidia_attribute[aid], value)) {
  708         NORM_ERR(
  709             "%s: Something went wrong running nvidia query (arg: %s tid: %d, "
  710             "aid: %d)",
  711             __func__, arg, tid, aid);
  712         return -1;
  713       }
  714       ac_value[gid].memtotal = *value;
  715     } else {
  716       *value = ac_value[gid].memtotal;
  717     }
  718   } else if (aid == ATTR_GPU_TEMP_THRESHOLD) {
  719     if (ac_value[gid].gputempthreshold < 0) {
  720       if (!dpy || !XNVCTRLQueryTargetAttribute(
  721                       dpy, translate_nvidia_target[tid], gid, 0,
  722                       translate_nvidia_attribute[aid], value)) {
  723         NORM_ERR(
  724             "%s: Something went wrong running nvidia query (arg: %s, tid: "
  725             "%d, aid: %d)",
  726             __func__, arg, tid, aid);
  727         return -1;
  728       }
  729       ac_value[gid].gputempthreshold = *value;
  730     } else {
  731       *value = ac_value[gid].gputempthreshold;
  732     }
  733   }
  734 
  735   return 0;
  736 }
  737 
  738 // Retrieve attribute value via nvidia interface
  739 static int get_nvidia_value(TARGET_ID tid, ATTR_ID aid, int gid,
  740                             const char *arg) {
  741   Display *dpy = nvdisplay ? nvdisplay : display;
  742   int value;
  743 
  744   // Check if the aid is cacheable
  745   if (aid == ATTR_MEM_TOTAL || aid == ATTR_GPU_TEMP_THRESHOLD) {
  746     if (cache_nvidia_value(tid, aid, dpy, &value, gid, arg)) { return -1; }
  747     // If not, then query it
  748   } else {
  749     if (!dpy ||
  750         !XNVCTRLQueryTargetAttribute(dpy, translate_nvidia_target[tid], gid, 0,
  751                                      translate_nvidia_attribute[aid], &value)) {
  752       NORM_ERR(
  753           "%s: Something went wrong running nvidia query (arg: %s, tid: %d, "
  754           "aid: %d)",
  755           __func__, arg, tid, aid);
  756       return -1;
  757     }
  758   }
  759 
  760   // Unpack clock values (see NVCtrl.h for details)
  761   if (aid == ATTR_GPU_FREQ) return value >> 16;
  762   if (aid == ATTR_MEM_FREQ) return value & 0xFFFF;
  763 
  764   // Return value
  765   return value;
  766 }
  767 
  768 // Retrieve attribute string via nvidia interface
  769 static char *get_nvidia_string(TARGET_ID tid, ATTR_ID aid, int gid,
  770                                const char *arg) {
  771   Display *dpy = nvdisplay ? nvdisplay : display;
  772   char *str;
  773 
  774   // Query nvidia interface
  775   if (!dpy || !XNVCTRLQueryTargetStringAttribute(
  776                   dpy, translate_nvidia_target[tid], gid, 0,
  777                   translate_nvidia_attribute[aid], &str)) {
  778     NORM_ERR(
  779         "%s: Something went wrong running nvidia string query (arg, tid: %d, "
  780         "aid: "
  781         "%d, GPU %d)",
  782         __func__, arg, tid, aid, gid);
  783     return nullptr;
  784   }
  785   return str;
  786 }
  787 
  788 void cache_nvidia_string_value_update(nvidia_c_string *ac_string, char *token,
  789                                       SEARCH_ID search, int *value, int gid) {
  790   if (strcmp(token, (char *)"nvclockmin") == 0 &&
  791       ac_string[gid].nvclockmin < 0) {
  792     ac_string[gid].nvclockmin = *value;
  793   } else if (strcmp(token, (char *)"nvclockmax") == 0 &&
  794              ac_string[gid].nvclockmax < 0) {
  795     ac_string[gid].nvclockmax = *value;
  796   } else if (strcmp(token, (char *)"memclockmin") == 0 &&
  797              ac_string[gid].memclockmin < 0) {
  798     ac_string[gid].memclockmin = *value;
  799   } else if (strcmp(token, (char *)"memclockmax") == 0 &&
  800              ac_string[gid].memclockmax < 0) {
  801     ac_string[gid].memclockmax = *value;
  802   } else if (strcmp(token, (char *)"memTransferRatemin") == 0 &&
  803              ac_string[gid].memTransferRatemin < 0) {
  804     ac_string[gid].memTransferRatemin = *value;
  805   } else if (strcmp(token, (char *)"memTransferRatemax") == 0 &&
  806              ac_string[gid].memTransferRatemax < 0) {
  807     ac_string[gid].memTransferRatemax = *value;
  808 
  809   } else if (strcmp(token, (char *)"perf") == 0 &&
  810              ac_string[gid].memTransferRatemax < 0) {
  811     if (search == SEARCH_MIN) {
  812       ac_string[gid].perfmin = *value;
  813     } else if (search == SEARCH_MAX) {
  814       ac_string[gid].perfmax = *value;
  815     }
  816   }
  817 }
  818 
  819 void cache_nvidia_string_value_noupdate(nvidia_c_string *ac_string, char *token,
  820                                         SEARCH_ID search, int *value, int gid) {
  821   if (strcmp(token, (char *)"nvclockmin") == 0) {
  822     *value = ac_string[gid].nvclockmin;
  823   } else if (strcmp(token, (char *)"nvclockmax") == 0) {
  824     *value = ac_string[gid].nvclockmax;
  825   } else if (strcmp(token, (char *)"memclockmin") == 0) {
  826     *value = ac_string[gid].memclockmin;
  827   } else if (strcmp(token, (char *)"memclockmax") == 0) {
  828     *value = ac_string[gid].memclockmax;
  829   } else if (strcmp(token, (char *)"memTransferRatemin") == 0) {
  830     *value = ac_string[gid].memTransferRatemin;
  831   } else if (strcmp(token, (char *)"memTransferRatemax") == 0) {
  832     *value = ac_string[gid].memTransferRatemax;
  833 
  834   } else if (strcmp(token, (char *)"perf") == 0) {
  835     if (search == SEARCH_MIN) {
  836       *value = ac_string[gid].perfmin;
  837     } else if (search == SEARCH_MAX) {
  838       *value = ac_string[gid].perfmax;
  839     }
  840   }
  841 }
  842 
  843 static int cache_nvidia_string_value(TARGET_ID tid, ATTR_ID aid, char *token,
  844                                      SEARCH_ID search, int *value, int update,
  845                                      int gid) {
  846   static nvidia_c_string ac_string[MAXNUMGPU];
  847   (void)tid;
  848   (void)aid;
  849 
  850   if (update) {
  851     cache_nvidia_string_value_update(ac_string, token, search, value, gid);
  852   } else {
  853     cache_nvidia_string_value_noupdate(ac_string, token, search, value, gid);
  854   }
  855 
  856   return 0;
  857 }
  858 
  859 // Retrieve token value from nvidia string
  860 static int get_nvidia_string_value(TARGET_ID tid, ATTR_ID aid, char *token,
  861                                    SEARCH_ID search, int gid, const char *arg) {
  862   char *str;
  863   char *kvp;
  864   char *key;
  865   char *val;
  866   char *saveptr1;
  867   char *saveptr2;
  868   int temp;
  869   int value = -1;
  870 
  871   // Checks if the value is cacheable and is already loaded
  872   cache_nvidia_string_value(tid, aid, token, search, &value, 0, gid);
  873   if (value != -1) { return value; }
  874 
  875   // Get string via nvidia interface
  876   str = get_nvidia_string(tid, aid, gid, arg);
  877 
  878   // Split string into 'key=value' substrings, split substring
  879   // into key and value, from value, check if token was found,
  880   // convert value to int, evaluate value according to specified
  881   // token search mode
  882   kvp = strtok_r(str, NV_KVPAIR_SEPARATORS, &saveptr1);
  883   while (kvp) {
  884     key = strtok_r(kvp, NV_KEYVAL_SEPARATORS, &saveptr2);
  885     val = strtok_r(nullptr, NV_KEYVAL_SEPARATORS, &saveptr2);
  886     if (key && val && (strcmp(token, key) == 0)) {
  887       temp = (int)strtol(val, nullptr, 0);
  888       if (search == SEARCH_FIRST) {
  889         value = temp;
  890         break;
  891       } else if (search == SEARCH_LAST) {
  892         value = temp;
  893       } else if (search == SEARCH_MIN) {
  894         if ((value == -1) || (temp < value)) value = temp;
  895       } else if (search == SEARCH_MAX) {
  896         if (temp > value) value = temp;
  897       } else {
  898         value = -1;
  899         break;
  900       }
  901     }
  902     kvp = strtok_r(nullptr, NV_KVPAIR_SEPARATORS, &saveptr1);
  903   }
  904 
  905   // This call updated the cache for the cacheable values
  906   cache_nvidia_string_value(tid, aid, token, search, &value, 1, gid);
  907 
  908   // Free string, return value
  909   free_and_zero(str);
  910   return value;
  911 }
  912 
  913 bool validate_target_id(Display *dpy, int target_id, ATTR_ID attribute) {
  914   // num_GPU and num_COOLER calculated only once based on the physical target
  915   static int num_GPU = get_nvidia_target_count(dpy, TARGET_GPU) - 1;
  916   static int num_COOLER = get_nvidia_target_count(dpy, TARGET_COOLER) - 1;
  917 
  918   if (target_id < 0) return false;
  919   switch (attribute) {
  920     case ATTR_FAN_LEVEL:
  921     case ATTR_FAN_SPEED:
  922       if (target_id > num_COOLER) return false;
  923       break;
  924     default:
  925       if (target_id > num_GPU) return false;
  926       break;
  927   }
  928   return true;
  929 }
  930 
  931 // Perform query and print result
  932 void print_nvidia_value(struct text_object *obj, char *p,
  933                         unsigned int p_max_size) {
  934   nvidia_s *nvs = static_cast<nvidia_s *>(obj->data.opaque);
  935   int value;
  936   int temp1;
  937   int temp2;
  938   int result;
  939   char *str;
  940   int event_base;
  941   int error_base;
  942 
  943   Display *dpy = nvdisplay ? nvdisplay : display;
  944 
  945   if (!dpy) {
  946     NORM_ERR("%s: no display set (try setting nvidia_display)", __func__);
  947     return;
  948   }
  949 
  950   if (!XNVCTRLQueryExtension(dpy, &event_base, &error_base)) {
  951     NORM_ERR("%s: NV-CONTROL X extension not present", __func__);
  952     return;
  953   }
  954 
  955   // Assume failure
  956   value = -1;
  957   str = nullptr;
  958 
  959   // Perform query if the query exists and isnt stupid
  960   if (nvs != nullptr &&
  961       validate_target_id(dpy, nvs->target_id, nvs->attribute)) {
  962     // Execute switch by query type
  963     switch (nvs->query) {
  964       case QUERY_VALUE:
  965         value = get_nvidia_value(nvs->target, nvs->attribute, nvs->target_id,
  966                                  nvs->arg);
  967         break;
  968       case QUERY_STRING:
  969         str = get_nvidia_string(nvs->target, nvs->attribute, nvs->target_id,
  970                                 nvs->arg);
  971         break;
  972       case QUERY_STRING_VALUE:
  973         value = get_nvidia_string_value(nvs->target, nvs->attribute, nvs->token,
  974                                         nvs->search, nvs->target_id, nvs->arg);
  975         break;
  976       case QUERY_SPECIAL:
  977         switch (nvs->attribute) {
  978           case ATTR_PERF_MODE:
  979             temp1 = get_nvidia_value(nvs->target, nvs->attribute,
  980                                      nvs->target_id, nvs->arg);
  981             switch (temp1) {
  982               case NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE:
  983                 result = asprintf(&str, "Adaptive");
  984                 break;
  985               case NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE:
  986                 result = asprintf(&str, "Max. Perf.");
  987                 break;
  988               case NV_CTRL_GPU_POWER_MIZER_MODE_AUTO:
  989                 result = asprintf(&str, "Auto");
  990                 break;
  991               case NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_CONSISTENT_PERFORMANCE:
  992                 result = asprintf(&str, "Consistent");
  993                 break;
  994               default:
  995                 result = asprintf(&str, "Unknown (%d)", value);
  996                 break;
  997             }
  998             if (result < 0) { str = nullptr; }
  999             break;
 1000           case ATTR_MEM_FREE:
 1001             temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->target_id,
 1002                                      nvs->arg);
 1003             temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL,
 1004                                      nvs->target_id, nvs->arg);
 1005             value = temp2 - temp1;
 1006             break;
 1007           case ATTR_MEM_UTIL:
 1008             temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->target_id,
 1009                                      nvs->arg);
 1010             temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL,
 1011                                      nvs->target_id, nvs->arg);
 1012             value = ((float)temp1 * 100 / (float)temp2) + 0.5;
 1013             break;
 1014           default:
 1015             break;
 1016         }
 1017         break;
 1018       default:
 1019         break;
 1020     }
 1021   }
 1022 
 1023   // Print result
 1024   if (value != -1) {
 1025     snprintf(p, p_max_size, "%d", value);
 1026   } else if (str != nullptr) {
 1027     snprintf(p, p_max_size, "%s", str);
 1028     free_and_zero(str);
 1029   } else {
 1030     snprintf(p, p_max_size, "%s", "N/A");
 1031   }
 1032 }
 1033 
 1034 double get_nvidia_barval(struct text_object *obj) {
 1035   nvidia_s *nvs = static_cast<nvidia_s *>(obj->data.opaque);
 1036   int temp1;
 1037   int temp2;
 1038   double value;
 1039   int event_base;
 1040   int error_base;
 1041 
 1042   Display *dpy = nvdisplay ? nvdisplay : display;
 1043 
 1044   if (!dpy) {
 1045     NORM_ERR("%s: no display set (try setting nvidia_display)", __func__);
 1046     return 0;
 1047   }
 1048 
 1049   if (!XNVCTRLQueryExtension(dpy, &event_base, &error_base)) {
 1050     NORM_ERR("%s: NV-CONTROL X extension not present", __func__);
 1051     return 0;
 1052   }
 1053 
 1054   // Assume failure
 1055   value = 0;
 1056 
 1057   // Convert query_result to a percentage using ((val-min)÷(max-min)×100)+0.5 if
 1058   // needed.
 1059   if (nvs != nullptr &&
 1060       validate_target_id(dpy, nvs->target_id, nvs->attribute)) {
 1061     switch (nvs->attribute) {
 1062       case ATTR_UTILS_STRING:  // one of the percentage utils (gpuutil,
 1063                                // membwutil, videoutil and pcieutil)
 1064         value =
 1065             get_nvidia_string_value(nvs->target, ATTR_UTILS_STRING, nvs->token,
 1066                                     nvs->search, nvs->target_id, nvs->arg);
 1067         break;
 1068       case ATTR_MEM_UTIL:  // memutil
 1069       case ATTR_MEM_USED:
 1070         temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->target_id,
 1071                                  nvs->arg);
 1072         temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL, nvs->target_id,
 1073                                  nvs->arg);
 1074         value = ((float)temp1 * 100 / (float)temp2) + 0.5;
 1075         break;
 1076       case ATTR_MEM_FREE:  // memfree
 1077         temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED, nvs->target_id,
 1078                                  nvs->arg);
 1079         temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL, nvs->target_id,
 1080                                  nvs->arg);
 1081         value = temp2 - temp1;
 1082         break;
 1083       case ATTR_FAN_SPEED:  // fanspeed: Warn user we are using fanlevel
 1084         NORM_ERR(
 1085             "%s: invalid argument specified: '%s' (using 'fanlevel' instead).",
 1086             nvs->command, nvs->arg);
 1087         /* falls through */
 1088       case ATTR_FAN_LEVEL:  // fanlevel
 1089         value = get_nvidia_value(nvs->target, ATTR_FAN_LEVEL, nvs->target_id,
 1090                                  nvs->arg);
 1091         break;
 1092       case ATTR_GPU_TEMP:  // gputemp (calculate out of gputempthreshold)
 1093         temp1 = get_nvidia_value(nvs->target, ATTR_GPU_TEMP, nvs->target_id,
 1094                                  nvs->arg);
 1095         temp2 = get_nvidia_value(nvs->target, ATTR_GPU_TEMP_THRESHOLD,
 1096                                  nvs->target_id, nvs->arg);
 1097         value = ((float)temp1 * 100 / (float)temp2) + 0.5;
 1098         break;
 1099       case ATTR_AMBIENT_TEMP:  // ambienttemp (calculate out of gputempthreshold
 1100                                // for consistency)
 1101         temp1 = get_nvidia_value(nvs->target, ATTR_AMBIENT_TEMP, nvs->target_id,
 1102                                  nvs->arg);
 1103         temp2 = get_nvidia_value(nvs->target, ATTR_GPU_TEMP_THRESHOLD,
 1104                                  nvs->target_id, nvs->arg);
 1105         value = ((float)temp1 * 100 / (float)temp2) + 0.5;
 1106         break;
 1107       case ATTR_GPU_FREQ:  // gpufreq (calculate out of gpufreqmax)
 1108         temp1 = get_nvidia_value(nvs->target, ATTR_GPU_FREQ, nvs->target_id,
 1109                                  nvs->arg);
 1110         temp2 = get_nvidia_string_value(nvs->target, ATTR_PERFMODES_STRING,
 1111                                         (char *)"nvclockmax", SEARCH_MAX,
 1112                                         nvs->target_id, nvs->arg);
 1113         value = ((float)temp1 * 100 / (float)temp2) + 0.5;
 1114         break;
 1115       case ATTR_MEM_FREQ:  // memfreq (calculate out of memfreqmax)
 1116         temp1 = get_nvidia_value(nvs->target, ATTR_MEM_FREQ, nvs->target_id,
 1117                                  nvs->arg);
 1118         temp2 = get_nvidia_string_value(nvs->target, ATTR_PERFMODES_STRING,
 1119                                         (char *)"memclockmax", SEARCH_MAX,
 1120                                         nvs->target_id, nvs->arg);
 1121         value = ((float)temp1 * 100 / (float)temp2) + 0.5;
 1122         break;
 1123       case ATTR_FREQS_STRING:  // mtrfreq (calculate out of memfreqmax)
 1124         if (strcmp(nvs->token, "memTransferRate") != 0) {
 1125           // Just in case error for silly devs
 1126           CRIT_ERR(nullptr, NULL,
 1127                    "%s: attribute is 'ATTR_FREQS_STRING' but token is not "
 1128                    "\"memTransferRate\" (arg: '%s')",
 1129                    nvs->command, nvs->arg);
 1130           return 0;
 1131         }
 1132         temp1 =
 1133             get_nvidia_string_value(nvs->target, ATTR_FREQS_STRING, nvs->token,
 1134                                     SEARCH_MAX, nvs->target_id, nvs->arg);
 1135         temp2 = get_nvidia_string_value(nvs->target, ATTR_PERFMODES_STRING,
 1136                                         (char *)"memTransferRatemax",
 1137                                         SEARCH_MAX, nvs->target_id, nvs->arg);
 1138         if (temp2 > temp1) temp1 = temp2;  // extra safe here
 1139         value = ((float)temp1 * 100 / (float)temp2) + 0.5;
 1140         break;
 1141       case ATTR_IMAGE_QUALITY:  // imagequality
 1142         value = get_nvidia_value(nvs->target, ATTR_IMAGE_QUALITY,
 1143                                  nvs->target_id, nvs->arg);
 1144         break;
 1145 
 1146       default:  // Throw error if unsupported args are used
 1147         CRIT_ERR(nullptr, NULL, "%s: invalid argument specified: '%s'",
 1148                  nvs->command, nvs->arg);
 1149     }
 1150   }
 1151 
 1152   // Return the percentage
 1153   return value;
 1154 }
 1155 
 1156 // Cleanup
 1157 void free_nvidia(struct text_object *obj) {
 1158   nvidia_s *nvs = static_cast<nvidia_s *>(obj->data.opaque);
 1159   delete nvs;
 1160   obj->data.opaque = nullptr;
 1161 }