diff --git a/src/main/nvgpu/main.c b/src/main/nvgpu/main.c new file mode 100644 index 0000000..331edf3 --- /dev/null +++ b/src/main/nvgpu/main.c @@ -0,0 +1,1610 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "nv/api.h" +#include "nv/module.h" + +#include "util/fs.h" +#include "util/mem.h" +#include "util/str.h" + +#define NV_DRS_SETTINGS_FOLDER "C:\\ProgramData\\NVIDIA Corporation\\Drs" + +#define printf_out(fmt, ...) \ + fprintf(stdout, fmt, ##__VA_ARGS__) +#define printf_err(fmt, ...) \ + fprintf(stderr, fmt, ##__VA_ARGS__) +#define printfln_out(fmt, ...) \ + fprintf(stdout, fmt "\n", ##__VA_ARGS__) +#define printfln_err(fmt, ...) \ + fprintf(stderr, fmt "\n", ##__VA_ARGS__) + +#define PRINT_ERR_WITH_NVAPI_MESSAGE(status, fmt, ...) \ + NvAPI_ShortString error_str; \ + nv_api->NvAPI_GetErrorMessage(status, error_str); \ + fprintf(stderr, fmt ", reason: %s\n", ##__VA_ARGS__, error_str); + +typedef struct displayconfig_path_info { + NvU32 path_info_count; + NV_DISPLAYCONFIG_PATH_INFO *path_info; +} displayconfig_path_info_t; + +static bool _session_destroy(const nv_api_t *nv_api, NvDRSSessionHandle session) +{ + assert(nv_api); + assert(session); + + NvAPI_Status status; + + status = nv_api->NvAPI_DRS_DestroySession(session); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Destroying driver settings session"); + return false; + } + + return true; +} + +static bool _session_create_and_settings_load(const nv_api_t *nv_api, NvDRSSessionHandle *session) +{ + assert(nv_api); + assert(session); + + NvAPI_Status status; + + status = nv_api->NvAPI_DRS_CreateSession(session); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Creating driver settings session"); + return false; + } + + status = nv_api->NvAPI_DRS_LoadSettings(*session); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Loading driver settings"); + + return _session_destroy(nv_api, *session); + } + + return true; +} + +static bool _settings_save_and_session_destroy(const nv_api_t *nv_api, NvDRSSessionHandle session) +{ + assert(nv_api); + assert(session); + + NvAPI_Status status; + + printfln_err("Saving ..."); + + status = nv_api->NvAPI_DRS_SaveSettings(session); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Saving driver settings"); + + return _session_destroy(nv_api, session); + } + + return _session_destroy(nv_api, session); +} + +static bool _profile_get( + const nv_api_t *nv_api, + const char *profile_name, + NvDRSSessionHandle session, + NvDRSProfileHandle *profile) +{ + NvAPI_UnicodeString nv_profile_name; + NvAPI_Status status; + wchar_t *profile_name_wstr; + + assert(nv_api); + assert(session); + assert(profile); + + profile_name_wstr = str_widen(profile_name); + wstr_cpy(nv_profile_name, sizeof(nv_profile_name), profile_name_wstr); + + status = nv_api->NvAPI_DRS_FindProfileByName(session, nv_profile_name, profile); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Finding driver profile %s", profile_name); + return false; + } + + return true; +} + +static bool _profile_setting_set( + const nv_api_t *nv_api, + NvDRSSessionHandle session, + NvDRSProfileHandle profile, + NVDRS_SETTING *setting) +{ + NvAPI_Status status; + + assert(nv_api); + assert(setting); + + status = nv_api->NvAPI_DRS_SetSetting(session, profile, setting); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "Error setting driver settings"); + return false; + } + + return true; +} + +static void _display_config_free(displayconfig_path_info_t *displayconfig_path_info) +{ + assert(displayconfig_path_info); + assert(displayconfig_path_info->path_info); + + for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) { + assert(displayconfig_path_info->path_info[i].sourceModeInfo); + assert(displayconfig_path_info->path_info[i].targetInfo); + + for (NvU32 j = 0; j < displayconfig_path_info->path_info[i].targetInfoCount; j++) { + assert(displayconfig_path_info->path_info[i].targetInfo[i].details); + + free(displayconfig_path_info->path_info[i].targetInfo[i].details); + } + + free(displayconfig_path_info->path_info[i].targetInfo); + free(displayconfig_path_info->path_info[i].sourceModeInfo); + } + + free(displayconfig_path_info->path_info); +} + +static bool _display_config_get(const nv_api_t *nv_api, displayconfig_path_info_t *displayconfig_path_info) +{ + NvAPI_Status status; + + assert(nv_api); + assert(displayconfig_path_info); + + status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count , NULL); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config path info count"); + return false; + } + + displayconfig_path_info->path_info = (NV_DISPLAYCONFIG_PATH_INFO*) + xmalloc(sizeof(NV_DISPLAYCONFIG_PATH_INFO) * displayconfig_path_info->path_info_count); + memset(displayconfig_path_info->path_info, 0, sizeof(NV_DISPLAYCONFIG_PATH_INFO) * displayconfig_path_info->path_info_count); + + for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) { + displayconfig_path_info->path_info[i].version = NV_DISPLAYCONFIG_PATH_INFO_VER; + } + + status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count, displayconfig_path_info->path_info); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config target info counts"); + + free(displayconfig_path_info->path_info); + + return false; + } + + for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) { + if (displayconfig_path_info->path_info[i].version == NV_DISPLAYCONFIG_PATH_INFO_VER1 || + displayconfig_path_info->path_info[i].version == NV_DISPLAYCONFIG_PATH_INFO_VER2) { + displayconfig_path_info->path_info[i].sourceModeInfo = + (NV_DISPLAYCONFIG_SOURCE_MODE_INFO*) xmalloc(sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO)); + } else { +#ifdef NV_DISPLAYCONFIG_PATH_INFO_VER3 + displayconfig_path_info->path_info[i].sourceModeInfo =(NV_DISPLAYCONFIG_SOURCE_MODE_INFO*) malloc( + displayconfig_path_info->path_info[i].sourceModeInfoCount * sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO)); +#endif + } + + memset(displayconfig_path_info->path_info[i].sourceModeInfo, 0, sizeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO)); + + displayconfig_path_info->path_info[i].targetInfo = (NV_DISPLAYCONFIG_PATH_TARGET_INFO*) xmalloc( + displayconfig_path_info->path_info[i].targetInfoCount * sizeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO)); + + memset(displayconfig_path_info->path_info[i].targetInfo, 0, + displayconfig_path_info->path_info[i].targetInfoCount * sizeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO)); + + for (NvU32 j = 0 ; j < displayconfig_path_info->path_info[i].targetInfoCount ; j++) { + displayconfig_path_info->path_info[i].targetInfo[j].details = + (NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO*) xmalloc(sizeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO)); + memset(displayconfig_path_info->path_info[i].targetInfo[j].details, 0, + sizeof(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO)); + displayconfig_path_info->path_info[i].targetInfo[j].details->version = + NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO_VER; + } + } + + status = nv_api->NvAPI_DISP_GetDisplayConfig(&displayconfig_path_info->path_info_count, displayconfig_path_info->path_info); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting display config"); + + _display_config_free(displayconfig_path_info); + + return false; + } + + return true; +} + +static bool _display_config_set(const nv_api_t *nv_api, displayconfig_path_info_t *displayconfig_path_info) +{ + NvAPI_Status status; + + assert(nv_api); + assert(displayconfig_path_info); + + printfln_err("Validating display config..."); + + status = nv_api->NvAPI_DISP_SetDisplayConfig(displayconfig_path_info->path_info_count, displayconfig_path_info->path_info, NV_DISPLAYCONFIG_VALIDATE_ONLY); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Validating display config"); + return false; + } + + printfln_err("Saving display config..."); + + status = nv_api->NvAPI_DISP_SetDisplayConfig(displayconfig_path_info->path_info_count, displayconfig_path_info->path_info, NV_DISPLAYCONFIG_SAVE_TO_PERSISTENCE | NV_DISPLAYCONFIG_DRIVER_RELOAD_ALLOWED); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Saving display config"); + return false; + } + + return true; +} + +static bool _display_config_visit(displayconfig_path_info_t *displayconfig_path_info, NvU32 display_id, bool (*display_config_filter)(NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info)) +{ + NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info; + + for (NvU32 i = 0; i < displayconfig_path_info->path_info_count; i++) { + target_info = &displayconfig_path_info->path_info[i].targetInfo[i]; + + if (display_id != 0 && target_info->displayId != display_id) { + continue; + } + + return display_config_filter(target_info); + } + + return false; +} + +static void _ensure_drs_settings_folder_exists() +{ + // Even on a fresh install, this might not exist which result + // in NVAPI_ACCESS_DENIED errors on many (or even all?) nvapi DRS functions + if (!path_exists(NV_DRS_SETTINGS_FOLDER)) { + printfln_err("NOTE: DRS settings folder to store profiles does not exist, creating..."); + path_mkdir(NV_DRS_SETTINGS_FOLDER); + } +} + +// ------------------------------------------------------------------------------------------------- + +static bool _nv_info(const nv_api_t *nv_api) +{ + NvAPI_ShortString interface_version; + NvAPI_ShortString driver_version; + NvU32 driver_version_number; + NvAPI_Status status; + + printfln_err("Getting interface version..."); + + status = nv_api->NvAPI_GetInterfaceVersionStringEx(interface_version); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting interface version string"); + return false; + } + + printfln_err("Getting driver and branch version..."); + + status = nv_api->NvAPI_SYS_GetDriverAndBranchVersion(&driver_version_number, driver_version); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting driver and branch version"); + return false; + } + + printfln_out("Interface version: %s", interface_version); + printfln_out("Driver version: %s", driver_version); + printfln_out("Driver version number: %lu", driver_version_number); + + return true; +} + +static bool _profile_create(const nv_api_t *nv_api, const char *profile_name) +{ + NvDRSSessionHandle session; + NVDRS_PROFILE profile; + wchar_t *profile_name_wstr; + NvAPI_Status status; + NvDRSProfileHandle profile_handle; + + assert(nv_api); + assert(profile_name); + + if (!_session_create_and_settings_load(nv_api, &session)) { + return false; + } + + profile.version = NVDRS_PROFILE_VER; + + profile_name_wstr = str_widen(profile_name); + wstr_cpy(profile.profileName, sizeof(profile.profileName), profile_name_wstr); + free(profile_name_wstr); + + printfln_err("Creating profile %s...", profile_name); + + status = nv_api->NvAPI_DRS_CreateProfile(session, &profile, &profile_handle); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Creating driver profile"); + + _session_destroy(nv_api, session); + return false; + } + + return _settings_save_and_session_destroy(nv_api, session); +} + +static bool _profile_delete(const nv_api_t *nv_api, const char *profile_name) +{ + NvDRSSessionHandle session; + NvDRSProfileHandle profile; + NvAPI_Status status; + + assert(nv_api); + assert(profile_name); + + if (!_session_create_and_settings_load(nv_api, &session)) { + return false; + } + + if (!_profile_get(nv_api, profile_name, session, &profile)) { + _session_destroy(nv_api, session); + + return false; + } + + printfln_err("Deleting profile %s...", profile_name); + + status = nv_api->NvAPI_DRS_DeleteProfile(session, profile); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Deleting driver profile %s", profile_name); + + _session_destroy(nv_api, session); + + return false; + } + + return _settings_save_and_session_destroy(nv_api, session); +} + +static bool _profile_application_add(const nv_api_t *nv_api, const char *profile_name, const char *application_name) +{ + NvDRSSessionHandle session; + NvDRSProfileHandle profile; + NvAPI_Status status; + NVDRS_APPLICATION application; + wchar_t *application_name_wstr; + + assert(nv_api); + assert(profile_name); + assert(application_name); + + if (!_session_create_and_settings_load(nv_api, &session)) { + return false; + } + + if (!_profile_get(nv_api, profile_name, session, &profile)) { + _session_destroy(nv_api, session); + + return false; + } + + application.version = NVDRS_APPLICATION_VER; + + application_name_wstr = str_widen(application_name); + wstr_cpy(application.appName, sizeof(application.appName), application_name_wstr); + free(application_name_wstr); + + printfln_err("Adding application %s to profile %s...", application_name, profile_name); + + status = nv_api->NvAPI_DRS_CreateApplication(session, profile, &application); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Adding application %s to profile %s", application_name, profile_name); + + _session_destroy(nv_api, session); + + return false; + } + + return _settings_save_and_session_destroy(nv_api, session); +} + +static bool _profile_gsync_disable(const nv_api_t *nv_api, const char *profile_name) +{ + NvDRSSessionHandle session; + NvDRSProfileHandle profile; + NVDRS_SETTING setting; + + assert(nv_api); + assert(profile_name); + + if (!_session_create_and_settings_load(nv_api, &session)) { + return false; + } + + if (!_profile_get(nv_api, profile_name, session, &profile)) { + _session_destroy(nv_api, session); + + return false; + } + + setting.version = NVDRS_SETTING_VER; + setting.settingId = VRR_APP_OVERRIDE_ID; + setting.settingType = NVDRS_DWORD_TYPE; + setting.u32PredefinedValue = VRR_APP_OVERRIDE_FIXED_REFRESH; + setting.u32CurrentValue = VRR_APP_OVERRIDE_FIXED_REFRESH; + + printfln_err("Disabling G-SYNC for profile %s...", profile_name); + + if (!_profile_setting_set(nv_api, session, profile, &setting)) { + _session_destroy(nv_api, session); + + return false; + } + + return _settings_save_and_session_destroy(nv_api, session); +} + +static bool _profile_gpu_power_state_max(const nv_api_t *nv_api, const char *profile_name) +{ + NvDRSSessionHandle session; + NvDRSProfileHandle profile; + NVDRS_SETTING setting; + + assert(nv_api); + assert(profile_name); + + if (!_session_create_and_settings_load(nv_api, &session)) { + return false; + } + + if (!_profile_get(nv_api, profile_name, session, &profile)) { + _session_destroy(nv_api, session); + + return false; + } + + setting.version = NVDRS_SETTING_VER; + setting.settingId = PREFERRED_PSTATE_ID; + setting.settingType = NVDRS_DWORD_TYPE; + setting.u32PredefinedValue = PREFERRED_PSTATE_PREFER_MAX; + setting.u32CurrentValue = PREFERRED_PSTATE_PREFER_MAX; + + printfln_err("Setting GPU power state to maximum for profile %s...", profile_name); + + if (!_profile_setting_set(nv_api, session, profile, &setting)) { + _session_destroy(nv_api, session); + + return false; + } + + return _settings_save_and_session_destroy(nv_api, session); +} + +static bool _display_primary_display_id(const nv_api_t *nv_api) +{ + NvAPI_Status status; + NvPhysicalGpuHandle gpu_handle[NVAPI_MAX_PHYSICAL_GPUS]; + NvU32 gpu_count; + NvU32 display_id_count; + NV_GPU_DISPLAYIDS *display_ids; + + assert(nv_api); + + status = nv_api->NvAPI_EnumPhysicalGPUs(gpu_handle, &gpu_count); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Enumerating physical GPUs"); + return false; + } + + // Assuming first physical GPU, first display = primary display + if (gpu_count > 0) { + status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[0], NULL, &display_id_count, 0); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display count of GPU"); + return false; + } + + if (display_id_count > 0) { + display_ids = (NV_GPU_DISPLAYIDS*) xmalloc(sizeof(NV_GPU_DISPLAYIDS) * display_id_count); + + for (NvU32 j = 0; j < display_id_count; j++) { + display_ids[j].version = NV_GPU_DISPLAYIDS_VER; + } + + status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[0], display_ids, &display_id_count, 0); + + if (status != NVAPI_OK) { + free(display_ids); + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display ids of GPU"); + return false; + } + + printfln_out("0x%lX", display_ids[0].displayId); + + free(display_ids); + + return true; + } + } + + return false; +} + +static bool _displays_list(const nv_api_t *nv_api) +{ + NvAPI_Status status; + NvPhysicalGpuHandle gpu_handle[NVAPI_MAX_PHYSICAL_GPUS]; + NvU32 gpu_count; + NvU32 display_id_count; + NV_GPU_DISPLAYIDS *display_ids; + const char *connector_type; + + assert(nv_api); + + status = nv_api->NvAPI_EnumPhysicalGPUs(gpu_handle, &gpu_count); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Enumerating physical GPUs"); + return false; + } + + printfln_err("Display ID, Connector Type, Active, Connected, Physically Connected"); + + for (NvU32 i = 0; i < gpu_count; i++) { + // Get display count per GPU + display_id_count = 0; + status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[i], NULL, &display_id_count, 0); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display count of GPU"); + return false; + } + + if (display_id_count > 0) { + display_ids = (NV_GPU_DISPLAYIDS*) xmalloc(sizeof(NV_GPU_DISPLAYIDS) * display_id_count); + + for (NvU32 j = 0; j < display_id_count; j++) { + display_ids[j].version = NV_GPU_DISPLAYIDS_VER; + } + + status = nv_api->NvAPI_GPU_GetConnectedDisplayIds(gpu_handle[i], display_ids, &display_id_count, 0); + + if (status != NVAPI_OK) { + free(display_ids); + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Getting connected display ids of GPU"); + return false; + } + + for (NvU32 j = 0; j < display_id_count; j++) { + switch (display_ids[j].connectorType) { + case NV_MONITOR_CONN_TYPE_VGA: + connector_type = "VGA"; + break; + case NV_MONITOR_CONN_TYPE_COMPONENT: + connector_type = "Component"; + break; + case NV_MONITOR_CONN_TYPE_SVIDEO: + connector_type = "S-Video"; + break; + case NV_MONITOR_CONN_TYPE_HDMI: + connector_type = "HDMI"; + break; + case NV_MONITOR_CONN_TYPE_DVI: + connector_type = "DVI"; + break; + case NV_MONITOR_CONN_TYPE_LVDS: + connector_type = "LVDS"; + break; + case NV_MONITOR_CONN_TYPE_DP: + connector_type = "DP"; + break; + case NV_MONITOR_CONN_TYPE_COMPOSITE: + connector_type = "Composite"; + break; + default: + connector_type = "unknown"; + break; + } + + printfln_out("0x%lX, %s, %s, %s, %s", + display_ids[j].displayId, + connector_type, + display_ids[j].isActive ? "active" : "inactive", + display_ids[j].isConnected ? "connected" : "disconnected", + display_ids[j].isPhysicallyConnected ? "connected" : "disconnected"); + } + + free(display_ids); + } + } + + return true; +} + +static bool _display_config_print(const nv_api_t *nv_api, NvU32 display_id) +{ + displayconfig_path_info_t displayconfig_path_info; + NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 *target_info; + NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 *source_mode_info; + + assert(nv_api); + + if (!_display_config_get(nv_api, &displayconfig_path_info)) { + return false; + } + + if (display_id != 0) { + printfln_err("Applying display ID filter: %lX", display_id); + } + + printfln_err("Num total adapters in config: %ld", displayconfig_path_info.path_info_count); + + for (NvU32 i = 0; i < displayconfig_path_info.path_info_count; i++) { + printfln_err("Num total displays in adapter: %ld", displayconfig_path_info.path_info[i].targetInfoCount); + + for (NvU32 j = 0; j < displayconfig_path_info.path_info[i].targetInfoCount; j++) { + target_info = &displayconfig_path_info.path_info[i].targetInfo[j]; + source_mode_info = &displayconfig_path_info.path_info[i].sourceModeInfo[j]; + + if (display_id != 0 && target_info->displayId != display_id) { + continue; + } + + printfln_out("--------------------------------"); + printfln_out("Adapter source ID %ld", displayconfig_path_info.path_info[i].sourceId); + printfln_out("Display ID: %lX", target_info->displayId); + printfln_out("Resolution width: %ld", source_mode_info->resolution.width); + printfln_out("Resolution height: %ld", source_mode_info->resolution.height); + printfln_out("Color depth: %d", source_mode_info->colorFormat); + printfln_out("Position x: %d", source_mode_info->position.x); + printfln_out("Position y: %d", source_mode_info->position.y); + printfln_out("Spanning orientation: %d", source_mode_info->spanningOrientation); + printfln_out("GDI primary: %d", source_mode_info->bGDIPrimary); + printfln_out("SLI focus: %d", source_mode_info->bSLIFocus); + + if (target_info->details) { + switch (target_info->details->rotation) { + case NV_ROTATE_0: + printfln_out("Rotation: 0 degrees"); + break; + case NV_ROTATE_90: + printfln_out("Rotation: 90 degrees"); + break; + case NV_ROTATE_180: + printfln_out("Rotation: 180 degrees"); + break; + case NV_ROTATE_270: + printfln_out("Rotation: 270 degrees"); + break; + case NV_ROTATE_IGNORED: + printfln_out("Rotation: ignored"); + break; + default: + printfln_out("Rotation: unknown (%d)", target_info->details->rotation); + break; + } + + switch (target_info->details->scaling) { + case NV_SCALING_DEFAULT: + printfln_out("Scaling: default"); + break; + case NV_SCALING_GPU_SCALING_TO_CLOSEST: + printfln_out("Scaling: GPU scaling to closest"); + break; + case NV_SCALING_GPU_SCALING_TO_NATIVE: + printfln_out("Scaling: GPU scaling to native"); + break; + case NV_SCALING_GPU_SCANOUT_TO_NATIVE: + printfln_out("Scaling: GPU scanout to native"); + break; + case NV_SCALING_GPU_SCALING_TO_ASPECT_SCANOUT_TO_NATIVE: + printfln_out("Scaling: GPU scaling to aspect scanout to native"); + break; + case NV_SCALING_GPU_SCALING_TO_ASPECT_SCANOUT_TO_CLOSEST: + printfln_out("Scaling: GPU scaling to aspect scanout to closest"); + break; + case NV_SCALING_GPU_SCANOUT_TO_CLOSEST: + printfln_out("Scaling: GPU scanout to closest"); + break; + case NV_SCALING_GPU_INTEGER_ASPECT_SCALING: + printfln_out("Scaling: GPU integer aspect scaling"); + break; + default: + printfln_out("Scaling: unknown (%d)", target_info->details->scaling); + break; + } + + printfln_out("Refresh rate (non interlaced): %ld", target_info->details->refreshRate1K * 1000); + printfln_out("Interlaced: %d", target_info->details->interlaced); + printfln_out("Primary: %d", target_info->details->primary); + printfln_out("Disable virtual mode support: %d", target_info->details->disableVirtualModeSupport); + printfln_out("Is preferred unscaled target: %d", target_info->details->isPreferredUnscaledTarget); + + switch (target_info->details->connector) { + case NVAPI_GPU_CONNECTOR_VGA_15_PIN: + printfln_out("Connector: VGA 15 Pin"); + break; + case NVAPI_GPU_CONNECTOR_TV_COMPOSITE: + printfln_out("Connector: TV Composite"); + break; + case NVAPI_GPU_CONNECTOR_TV_SVIDEO: + printfln_out("Connector: TV S-Video"); + break; + case NVAPI_GPU_CONNECTOR_TV_HDTV_COMPONENT: + printfln_out("Connector: TV HDTV Component"); + break; + case NVAPI_GPU_CONNECTOR_TV_SCART: + printfln_out("Connector: TV SCART"); + break; + case NVAPI_GPU_CONNECTOR_TV_COMPOSITE_SCART_ON_EIAJ4120: + printfln_out("Connector: TV Composite SCART on EIAJ4120"); + break; + case NVAPI_GPU_CONNECTOR_TV_HDTV_EIAJ4120: + printfln_out("Connector: TV HDTV EIAJ4120"); + break; + case NVAPI_GPU_CONNECTOR_PC_POD_HDTV_YPRPB: + printfln_out("Connector: PC POD HDTV YPRPB"); + break; + case NVAPI_GPU_CONNECTOR_PC_POD_SVIDEO: + printfln_out("Connector: PC POD S-Video"); + break; + case NVAPI_GPU_CONNECTOR_PC_POD_COMPOSITE: + printfln_out("Connector: PC POD Composite"); + break; + case NVAPI_GPU_CONNECTOR_DVI_I_TV_SVIDEO: + printfln_out("Connector: DVI-I TV S-Video"); + break; + case NVAPI_GPU_CONNECTOR_DVI_I_TV_COMPOSITE: + printfln_out("Connector: DVI-I TV Composite"); + break; + case NVAPI_GPU_CONNECTOR_DVI_I: + printfln_out("Connector: DVI-I"); + break; + case NVAPI_GPU_CONNECTOR_DVI_D: + printfln_out("Connector: DVI-D"); + break; + case NVAPI_GPU_CONNECTOR_ADC: + printfln_out("Connector: ADC"); + break; + case NVAPI_GPU_CONNECTOR_LFH_DVI_I_1: + printfln_out("Connector: LFH DVI-I 1"); + break; + case NVAPI_GPU_CONNECTOR_LFH_DVI_I_2: + printfln_out("Connector: LFH DVI-I 2"); + break; + case NVAPI_GPU_CONNECTOR_SPWG: + printfln_out("Connector: SPWG"); + break; + case NVAPI_GPU_CONNECTOR_OEM: + printfln_out("Connector: OEM"); + break; + case NVAPI_GPU_CONNECTOR_DISPLAYPORT_EXTERNAL: + printfln_out("Connector: DisplayPort External"); + break; + case NVAPI_GPU_CONNECTOR_DISPLAYPORT_INTERNAL: + printfln_out("Connector: DisplayPort Internal"); + break; + case NVAPI_GPU_CONNECTOR_DISPLAYPORT_MINI_EXT: + printfln_out("Connector: DisplayPort Mini External"); + break; + case NVAPI_GPU_CONNECTOR_HDMI_A: + printfln_out("Connector: HDMI A"); + break; + case NVAPI_GPU_CONNECTOR_HDMI_C_MINI: + printfln_out("Connector: HDMI C Mini"); + break; + case NVAPI_GPU_CONNECTOR_LFH_DISPLAYPORT_1: + printfln_out("Connector: LFH DisplayPort 1"); + break; + case NVAPI_GPU_CONNECTOR_LFH_DISPLAYPORT_2: + printfln_out("Connector: LFH DisplayPort 2"); + break; + case NVAPI_GPU_CONNECTOR_VIRTUAL_WFD: + printfln_out("Connector: Virtual WFD"); + break; + case NVAPI_GPU_CONNECTOR_USB_C: + printfln_out("Connector: USB C"); + break; + default: + printfln_out("Connector: unknown (%d)", target_info->details->connector); + break; + } + + switch (target_info->details->tvFormat) { + case NV_DISPLAY_TV_FORMAT_NONE: + printfln_out("TV format: none"); + break; + case NV_DISPLAY_TV_FORMAT_SD_NTSCM: + printfln_out("TV format: SD NTSC-M"); + break; + case NV_DISPLAY_TV_FORMAT_SD_NTSCJ: + printfln_out("TV format: SD NTSC-J"); + break; + case NV_DISPLAY_TV_FORMAT_SD_PALM: + printfln_out("TV format: SD PAL-M"); + break; + case NV_DISPLAY_TV_FORMAT_SD_PALBDGH: + printfln_out("TV format: SD PAL-BDGH"); + break; + case NV_DISPLAY_TV_FORMAT_SD_PALN: + printfln_out("TV format: SD PAL-N"); + break; + case NV_DISPLAY_TV_FORMAT_SD_PALNC: + printfln_out("TV format: SD PAL-NC"); + break; + case NV_DISPLAY_TV_FORMAT_SD_576i: + printfln_out("TV format: SD 576i"); + break; + case NV_DISPLAY_TV_FORMAT_SD_480i: + printfln_out("TV format: SD 480i"); + break; + case NV_DISPLAY_TV_FORMAT_ED_480p: + printfln_out("TV format: ED 480p"); + break; + case NV_DISPLAY_TV_FORMAT_ED_576p: + printfln_out("TV format: ED 576p"); + break; + case NV_DISPLAY_TV_FORMAT_HD_720p: + printfln_out("TV format: HD 720p"); + break; + case NV_DISPLAY_TV_FORMAT_HD_1080i: + printfln_out("TV format: HD 1080i"); + break; + case NV_DISPLAY_TV_FORMAT_HD_1080p: + printfln_out("TV format: HD 1080p"); + break; + case NV_DISPLAY_TV_FORMAT_HD_720p50: + printfln_out("TV format: HD 720p50"); + break; + case NV_DISPLAY_TV_FORMAT_HD_1080p24: + printfln_out("TV format: HD 1080p24"); + break; + case NV_DISPLAY_TV_FORMAT_HD_1080i50: + printfln_out("TV format: HD 1080i50"); + break; + case NV_DISPLAY_TV_FORMAT_HD_1080p50: + printfln_out("TV format: HD 1080p50"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp30_3840: + printfln_out("TV format: UHD 4Kp30 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp25_3840: + printfln_out("TV format: UHD 4Kp25 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_3840: + printfln_out("TV format: UHD 4Kp24 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_SMPTE: + printfln_out("TV format: UHD 4Kp24 SMPTE"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp50_3840: + printfln_out("TV format: UHD 4Kp50 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp60_3840: + printfln_out("TV format: UHD 4Kp60 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp30_4096: + printfln_out("TV format: UHD 4Kp30 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp25_4096: + printfln_out("TV format: UHD 4Kp25 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_4096: + printfln_out("TV format: UHD 4Kp24 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp50_4096: + printfln_out("TV format: UHD 4Kp50 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp60_4096: + printfln_out("TV format: UHD 4Kp60 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp24_7680: + printfln_out("TV format: UHD 8Kp24 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp25_7680: + printfln_out("TV format: UHD 8Kp25 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp30_7680: + printfln_out("TV format: UHD 8Kp30 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp48_7680: + printfln_out("TV format: UHD 8Kp48 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp50_7680: + printfln_out("TV format: UHD 8Kp50 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp60_7680: + printfln_out("TV format: UHD 8Kp60 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp100_7680: + printfln_out("TV format: UHD 8Kp100 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_8Kp120_7680: + printfln_out("TV format: UHD 8Kp120 7680"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp48_3840: + printfln_out("TV format: UHD 4Kp48 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp48_4096: + printfln_out("TV format: UHD 4Kp48 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp100_3840: + printfln_out("TV format: UHD 4Kp100 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp100_4096: + printfln_out("TV format: UHD 4Kp100 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp120_4096: + printfln_out("TV format: UHD 4Kp120 4096"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp120_3840: + printfln_out("TV format: UHD 4Kp120 3840"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp100_5120: + printfln_out("TV format: UHD 4Kp100 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp120_5120: + printfln_out("TV format: UHD 4Kp120 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp24_5120: + printfln_out("TV format: UHD 4Kp24 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp25_5120: + printfln_out("TV format: UHD 4Kp25 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp30_5120: + printfln_out("TV format: UHD 4Kp30 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp48_5120: + printfln_out("TV format: UHD 4Kp48 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp50_5120: + printfln_out("TV format: UHD 4Kp50 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_4Kp60_5120: + printfln_out("TV format: UHD 4Kp60 5120"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp24_10240: + printfln_out("TV format: UHD 10Kp24 10240"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp25_10240: + printfln_out("TV format: UHD 10Kp25 10240"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp30_10240: + printfln_out("TV format: UHD 10Kp30 10240"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp48_10240: + printfln_out("TV format: UHD 10Kp48 10240"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp50_10240: + printfln_out("TV format: UHD 10Kp50 10240"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp60_10240: + printfln_out("TV format: UHD 10Kp60 10240"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp100_10240: + printfln_out("TV format: UHD 10Kp100 10240"); + break; + case NV_DISPLAY_TV_FORMAT_UHD_10Kp120_10240: + printfln_out("TV format: UHD 10Kp120 10240"); + break; + case NV_DISPLAY_TV_FORMAT_SD_OTHER: + printfln_out("TV format: SD Other"); + break; + case NV_DISPLAY_TV_FORMAT_ED_OTHER: + printfln_out("TV format: ED Other"); + break; + case NV_DISPLAY_TV_FORMAT_HD_OTHER: + printfln_out("TV format: HD Other"); + break; + case NV_DISPLAY_TV_FORMAT_ANY: + printfln_out("TV format: Any"); + break; + default: + printfln_out("TV format: unknown (%d)", target_info->details->tvFormat); + break; + } + + switch (target_info->details->timingOverride) { + case NV_TIMING_OVERRIDE_CURRENT: + printfln_out("Timing override: current"); + break; + case NV_TIMING_OVERRIDE_AUTO: + printfln_out("Timing override: auto"); + break; + case NV_TIMING_OVERRIDE_EDID: + printfln_out("Timing override: EDID"); + break; + case NV_TIMING_OVERRIDE_DMT: + printfln_out("Timing override: VESA DMT"); + break; + case NV_TIMING_OVERRIDE_DMT_RB: + printfln_out("Timing override: VESA DMT RB"); + break; + case NV_TIMING_OVERRIDE_CVT: + printfln_out("Timing override: VESA CVT"); + break; + case NV_TIMING_OVERRIDE_CVT_RB: + printfln_out("Timing override: VESA CVT RB"); + break; + case NV_TIMING_OVERRIDE_GTF: + printfln_out("Timing override: VESA GTF"); + break; + case NV_TIMING_OVERRIDE_EIA861: + printfln_out("Timing override: EIA 861x pre-defined timing"); + break; + case NV_TIMING_OVERRIDE_ANALOG_TV: + printfln_out("Timing override: analog SD/HDTV timing"); + break; + case NV_TIMING_OVERRIDE_CUST: + printfln_out("Timing override: NV custom timings"); + break; + case NV_TIMING_OVERRIDE_NV_PREDEFINED: + printfln_out("Timing override: NV pre-defined timing (basically the PsF timings)"); + break; + case NV_TIMING_OVERRIDE_NV_ASPR: + printfln_out("Timing override: NV ASPR"); + break; + case NV_TIMING_OVERRIDE_SDI: + printfln_out("Timing override: SDI"); + break; + case NV_TIMING_OVRRIDE_MAX: + printfln_out("Timing override: max"); + break; + default: + printfln_out("Timing override: unknown (%d)", target_info->details->timingOverride); + break; + } + + printfln_out("Override custom (backend raster) timing"); + + printfln_out("Horizontal visible: %d", target_info->details->timing.HVisible); + printfln_out("Horizontal border: %d", target_info->details->timing.HBorder); + printfln_out("Horizontal front porch: %d", target_info->details->timing.HFrontPorch); + printfln_out("Horizontal sync width: %d", target_info->details->timing.HSyncWidth); + printfln_out("Horizontal total: %d", target_info->details->timing.HTotal); + printfln_out("Horizontal sync polarity: %s", target_info->details->timing.HSyncPol == 1 ? "negative" : "positive"); + + printfln_out("Vertical visible: %d", target_info->details->timing.VVisible); + printfln_out("Vertical border: %d", target_info->details->timing.VBorder); + printfln_out("Vertical front porch: %d", target_info->details->timing.VFrontPorch); + printfln_out("Vertical sync width: %d", target_info->details->timing.VSyncWidth); + printfln_out("Vertical total: %d", target_info->details->timing.VTotal); + printfln_out("Vertical sync polarity: %s", target_info->details->timing.VSyncPol == 1 ? "negative" : "positive"); + + printfln_out("Interlaced: %s", target_info->details->timing.interlaced == 1 ? "interlaced" : "progressive"); + printfln_out("Pixel clock: %lu kHz", target_info->details->timing.pclk * 10); + + printfln_out("Flag: 0x%lX", target_info->details->timing.etc.flag); + printfln_out("Logical refresh rate to present: %d", target_info->details->timing.etc.rr); + printfln_out("Physical vertical refresh rate: %f Hz", target_info->details->timing.etc.rrx1k * 0.001); + printfln_out("Display aspect ratio: %lu:%lu", target_info->details->timing.etc.aspect >> 16, target_info->details->timing.etc.aspect & 0xFFFF); + printfln_out("Bit-wise pixel repetition factor (1 = no pixel repetition, 2 = each pixel repeats twice horizontally, etc.): %d", target_info->details->timing.etc.rep); + printfln_out("Timing status: 0x%lX", target_info->details->timing.etc.status); + printfln_out("Timing name: %s", target_info->details->timing.etc.name); + } + + printfln_out("--------------------------------"); + } + } + + _display_config_free(&displayconfig_path_info); + + return true; +} + +static bool _custom_resolution_set( + const nv_api_t *nv_api, + NvU32 display_id, + uint16_t screen_width, + uint16_t screen_height, + float screen_refresh_rate, + uint8_t test_only_timeout_sec) +{ + NV_CUSTOM_DISPLAY custom_display; + NV_TIMING_FLAG flag; + NV_TIMING_INPUT timing; + NvAPI_Status status; + + assert(nv_api); + + memset(&custom_display, 0, sizeof(NV_CUSTOM_DISPLAY)); + memset(&flag, 0, sizeof(NV_TIMING_FLAG)); + memset(&timing, 0, sizeof(NV_TIMING_INPUT)); + + custom_display.version = NV_CUSTOM_DISPLAY_VER; + custom_display.width = screen_width; + custom_display.height = screen_height; + custom_display.depth = 32; + custom_display.colorFormat = NV_FORMAT_A8R8G8B8; + custom_display.srcPartition.x = 0; + custom_display.srcPartition.y = 0; + custom_display.srcPartition.w = 1; + custom_display.srcPartition.h = 1; + custom_display.xRatio = 1; + custom_display.yRatio = 1; + + timing.version = NV_TIMING_INPUT_VER; + timing.height = screen_height; + timing.width = screen_width; + timing.rr = screen_refresh_rate; + timing.flag = flag; + timing.type = NV_TIMING_OVERRIDE_CVT_RB; + + printfln_err("Calculating custom display timing for display ID 0x%lX", display_id); + + status = nv_api->NvAPI_DISP_GetTiming(display_id, &timing, &custom_display.timing); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Calculating custom display timing"); + return false; + } + + status = nv_api->NvAPI_DISP_TryCustomDisplay(&display_id, 1, &custom_display); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Trying custom display configuration"); + return false; + } + + if (test_only_timeout_sec == 0) { + printfln_err("Persisting custom display configuration"); + + status = nv_api->NvAPI_DISP_SaveCustomDisplay(&display_id, 1, true, true); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Saving custom display configuration"); + return false; + } + + printfln_err("Custom display configuration persisted"); + } else { + printfln_err("Trying custom display configuration for %d seconds...", test_only_timeout_sec); + + Sleep(test_only_timeout_sec * 1000); + + printfln_err("Reverting custom display configuration, not persisting"); + + status = nv_api->NvAPI_DISP_RevertCustomDisplayTrial(&display_id, 1); + + if (status != NVAPI_OK) { + PRINT_ERR_WITH_NVAPI_MESSAGE(status, "ERROR: Reverting custom display configuration"); + return false; + } + } + + return true; +} + +static bool _display_config_gpu_scaling_enable_filter(NV_DISPLAYCONFIG_PATH_TARGET_INFO *target_info) +{ + if (target_info->details == NULL) { + printfln_err("ERROR: No details found for target info"); + return false; + } + + if (target_info->details->scaling == NV_SCALING_GPU_SCALING_TO_NATIVE) { + printfln_err("GPU scaling to native resolution is already enabled"); + return false; + } else { + target_info->details->scaling = NV_SCALING_GPU_SCALING_TO_NATIVE; + printfln_err("Enabling GPU scaling to native resolution"); + return true; + } +} + +static bool _display_config_gpu_scaling_to_native_resolution_enable(const nv_api_t *nv_api, NvU32 display_id) +{ + displayconfig_path_info_t displayconfig_path_info; + bool changes_to_save; + + assert(nv_api); + + if (!_display_config_get(nv_api, &displayconfig_path_info)) { + return false; + } + + changes_to_save = _display_config_visit(&displayconfig_path_info, display_id, _display_config_gpu_scaling_enable_filter); + + if (changes_to_save) { + if (!_display_config_set(nv_api, &displayconfig_path_info)) { + _display_config_free(&displayconfig_path_info); + return false; + } + } + + _display_config_free(&displayconfig_path_info); + + return true; +} + +// ------------------------------------------------------------------------------------------------- + +static void _print_synopsis() +{ + printfln_err("Usage: nvgpu "); + printfln_err("Commands:"); + printfln_err(" nv"); + printfln_err(" info Print information about the NVAPI module and driver"); + printfln_err(""); + printfln_err(" profile"); + printfln_err(" create Create a new driver profile with the given name"); + printfln_err(" delete Delete an existing driver profile"); + printfln_err(" application-add "); + printfln_err(" Add an application to the driver profile. This will apply the profile to the application when the driver detects a process being launched, e.g. MyApplication.exe"); + printfln_err(" gsync-disable Disable G-SYNC for the driver profile"); + printfln_err(" gpu-power-state-max Set GPU power state to maximum for the driver profile"); + printfln_err(""); + printfln_err(" display"); + printfln_err(" primary-display-id Print the ID of the primary display"); + printfln_err(" list List all connected displays and their display IDs"); + printfln_err(" config [display_id] Print the current display configurations. Optionally, specify a display ID to get the configuration of that display only"); + printfln_err(" custom-resolution-set "); + printfln_err(" Set a custom display mode with the given parameters for the given display ID. The settings are persisted immediately. Ensure you tested these before with the custom-display-test command."); + printfln_err(" custom-resolution-test [--test-timeout-secs n]"); + printfln_err(" Test a custom display mode for a limited amount of time. This will revert the display mode after the given amount of seconds and not persist the changes."); + printfln_err(" test-timeout-secs: Optional. Number of seconds to test the custom display mode for. Default is 10 seconds."); + printfln_err(" gpu-scaling-to-native-resolution-enable Enable GPU scaling to native resolution for the given display ID"); +} + +static bool _cmd_nv_info(const nv_api_t *nv_api) +{ + return _nv_info(nv_api); +} + +static bool _cmd_profile_create(const nv_api_t *nv_api, int argc, char **argv) +{ + const char *profile_name; + + if (argc < 1) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + profile_name = argv[0]; + + return _profile_create(nv_api, profile_name); +} + +static bool _cmd_profile_delete(const nv_api_t *nv_api, int argc, char **argv) +{ + const char *profile_name; + + if (argc < 1) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + profile_name = argv[0]; + + return _profile_delete(nv_api, profile_name); +} + +static bool _cmd_profile_application_add(const nv_api_t *nv_api, int argc, char **argv) +{ + const char *profile_name; + const char *application_name; + + if (argc < 2) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + profile_name = argv[0]; + application_name = argv[1]; + + return _profile_application_add(nv_api, profile_name, application_name); +} + +static bool _cmd_profile_gsync_disable(const nv_api_t *nv_api, int argc, char **argv) +{ + const char *profile_name; + + if (argc < 1) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + profile_name = argv[0]; + + return _profile_gsync_disable(nv_api, profile_name); +} + +static bool _cmd_profile_gpu_power_state_max(const nv_api_t *nv_api, int argc, char **argv) +{ + const char *profile_name; + + if (argc < 1) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + profile_name = argv[0]; + + return _profile_gpu_power_state_max(nv_api, profile_name); +} + +static bool _cmd_display_primary_display_id(const nv_api_t *nv_api) +{ + return _display_primary_display_id(nv_api); +} + +static bool _cmd_display_list(const nv_api_t *nv_api) +{ + return _displays_list(nv_api); +} + +static bool _cmd_display_config(const nv_api_t *nv_api, int argc, char **argv) +{ + uint32_t display_id; + + if (argc > 0) { + if (argv[0][0] == '0' && argv[0][1] == 'x') { + display_id = strtoul(argv[0] + 2, NULL, 16); + } else { + display_id = strtoul(argv[0], NULL, 10); + } + } else { + display_id = 0; + } + + return _display_config_print(nv_api, display_id); +} + +static bool _cmd_custom_resolution_set(const nv_api_t *nv_api, int argc, char **argv) +{ + uint32_t display_id; + uint16_t screen_width; + uint16_t screen_height; + float screen_refresh_rate; + + if (argc < 4) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + if (argv[0][0] == '0' && argv[0][1] == 'x') { + display_id = strtoul(argv[0] + 2, NULL, 16); + } else { + display_id = strtoul(argv[0], NULL, 10); + } + + screen_width = atoi(argv[1]); + screen_height = atoi(argv[2]); + screen_refresh_rate = atof(argv[3]); + + printf_err("Setting custom resolution for display ID 0x%X: %dx%d@%f\n", display_id, screen_width, screen_height, screen_refresh_rate); + + return _custom_resolution_set( + nv_api, + display_id, + screen_width, + screen_height, + screen_refresh_rate, + 0); +} + +static bool _cmd_custom_resolution_test(const nv_api_t *nv_api, int argc, char **argv) +{ + uint32_t display_id; + uint16_t screen_width; + uint16_t screen_height; + float screen_refresh_rate; + uint8_t test_only_timeout_sec; + + if (argc < 5) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + if (argv[0][0] == '0' && argv[0][1] == 'x') { + display_id = strtoul(argv[0] + 2, NULL, 16); + } else { + display_id = strtoul(argv[0], NULL, 10); + } + + screen_width = atoi(argv[1]); + screen_height = atoi(argv[2]); + screen_refresh_rate = atof(argv[3]); + + // Sane defaults for optional parameters + test_only_timeout_sec = 10; + + for (int i = 4; i < argc; i++) { + if (!strcmp(argv[i], "--test-timeout-secs")) { + if (i + 1 < argc) { + test_only_timeout_sec = atoi(argv[++i]); + + if (test_only_timeout_sec == 0) { + printfln_err("ERROR: Time out parameter must be greater than 0"); + return false; + } + } else { + printfln_err("ERROR: Missing argument for --test-timeout-secs"); + return false; + } + } + } + + printf_err("Testing custom resolution for display ID %u: %dx%d@%f, timeout %d seconds\n", display_id, screen_width, screen_height, screen_refresh_rate, test_only_timeout_sec); + + return _custom_resolution_set( + nv_api, + display_id, + screen_width, + screen_height, + screen_refresh_rate, + test_only_timeout_sec); +} + +static bool _cmd_display_gpu_scaling_to_native_resolution_enable(const nv_api_t *nv_api, int argc, char **argv) +{ + uint32_t display_id; + + if (argc < 1) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + return false; + } + + if (argv[0][0] == '0' && argv[0][1] == 'x') { + display_id = strtoul(argv[0] + 2, NULL, 16); + } else { + display_id = strtoul(argv[0], NULL, 10); + } + + return _display_config_gpu_scaling_to_native_resolution_enable(nv_api, display_id); +} + +// ------------------------------------------------------------------------------------------------- + +int main(int argc, char **argv) +{ + const char *command; + const char *sub_command; + nv_module_t *nv_module; + nv_api_t nv_api; + NvAPI_Status status; + bool result; + + if (argc < 3) { + _print_synopsis(); + printfln_err("ERROR: Insufficient arguments"); + exit(1); + } + + command = argv[1]; + sub_command = argv[2]; + + printfln_err("Loading NVAPI module..."); + + nv_module_load(&nv_module); + nv_module_api_get(nv_module, &nv_api); + + printfln_err("Initializing NVAPI..."); + + status = nv_api.NvAPI_Initialize(); + + if (status != NVAPI_OK) { + NvAPI_ShortString error_str; + nv_api.NvAPI_GetErrorMessage(status, error_str); + fprintf(stderr, "ERROR: Initializing NVAPI, reason: %s\n", error_str); + + nv_module_free(&nv_module); + exit(1); + } + + printfln_err("Running command: %s %s", command, sub_command); + + if (!strcmp(command, "nv")) { + if (!strcmp(sub_command, "info")) { + result = _cmd_nv_info(&nv_api); + } else { + printfln_err("ERROR: Unknown sub-command: %s", sub_command); + _print_synopsis(); + result = false; + } + } else if (!strcmp(command, "profile")) { + _ensure_drs_settings_folder_exists(); + + if (!strcmp(sub_command, "create")) { + result = _cmd_profile_create(&nv_api, argc - 3, argv + 3); + } else if (!strcmp(sub_command, "delete")) { + result = _cmd_profile_delete(&nv_api, argc - 3, argv + 3); + } else if (!strcmp(sub_command, "application-add")) { + result = _cmd_profile_application_add(&nv_api, argc - 3, argv + 3); + } else if (!strcmp(sub_command, "gsync-disable")) { + result = _cmd_profile_gsync_disable(&nv_api, argc - 3, argv + 3); + } else if (!strcmp(sub_command, "gpu-power-state-max")) { + result = _cmd_profile_gpu_power_state_max(&nv_api, argc - 3, argv + 3); + } else { + printfln_err("ERROR: Unknown sub-command: %s", sub_command); + _print_synopsis(); + result = false; + } + } else if (!strcmp(command, "display")) { + if (!strcmp(sub_command, "primary-display-id")) { + result = _cmd_display_primary_display_id(&nv_api); + } else if (!strcmp(sub_command, "list")) { + result = _cmd_display_list(&nv_api); + } else if (!strcmp(sub_command, "config")) { + result = _cmd_display_config(&nv_api, argc - 3, argv + 3); + } else if (!strcmp(sub_command, "custom-resolution-set")) { + result = _cmd_custom_resolution_set(&nv_api, argc - 3, argv + 3); + } else if (!strcmp(sub_command, "custom-resolution-test")) { + result = _cmd_custom_resolution_test(&nv_api, argc - 3, argv + 3); + } else if (!strcmp(sub_command, "gpu-scaling-to-native-resolution-enable")) { + result = _cmd_display_gpu_scaling_to_native_resolution_enable(&nv_api, argc - 3, argv + 3); + } else { + printfln_err("ERROR: Unknown sub-command: %s", sub_command); + _print_synopsis(); + result = false; + } + } else { + printfln_err("ERROR: Unknown command: %s", command); + _print_synopsis(); + result = false; + } + + status = nv_api.NvAPI_Unload(); + + if (status != NVAPI_OK) { + NvAPI_ShortString error_str; + nv_api.NvAPI_GetErrorMessage(status, error_str); + printfln_err("ERROR: Unloading NVAPI, reason: %s", error_str); + } + + nv_module_free(&nv_module); + + if (result) { + printfln_err("Command completed successfully"); + exit(0); + } else { + printfln_err("Command failed"); + exit(1); + } +} \ No newline at end of file