* nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
* and Linux systems.
*
* Copyright (C) 2004 NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>.
*/
#include "NvCtrlAttributes.h"
#include "command-line.h"
#include "config-file.h"
#include "query-assign.h"
#include "msg.h"
#include "version.h"
#include "wayland-connector.h"
#include <dlfcn.h>
#include <sys/stat.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
static const char* library_names[] = {
"libnvidia-gtk3.so." NVIDIA_VERSION,
"libnvidia-gtk3.so",
"libnvidia-gtk2.so." NVIDIA_VERSION,
"libnvidia-gtk2.so",
};
typedef struct {
void *gui_lib_handle;
char *error_msg;
int (*fn_ctk_init_check)(int *, char **[]);
char *(*fn_ctk_get_display)(void);
void (*fn_ctk_main)(ParsedAttribute *, ConfigProperties *,
CtrlSystem *, const char *);
} GtkLibraryData;
wayland_lib wllib;
* get_full_library_name() - build the library name to use by selecting the
* library name based on the index along with a possible path prefix.
*/
static char *get_full_library_name(const char *prefix, int index)
{
int length = strlen(prefix);
return nvstrcat(prefix,
(length > 0 && prefix[length-1] != '/') ? "/" : "",
library_names[index],
NULL);
}
* check_and_save_dlerror() - If an error occurs related to our dynamic loading,
* save the error message. Since we can check for more than one library and will
* fallback to another library name if the first is not found, we don't want to
* print an error message unless we abort execution because of it.
*/
static int check_and_save_dlerror(char **error_msg)
{
const char *e;
char *new_error_msg;
e = dlerror();
if (e) {
if (*error_msg) {
new_error_msg = nvstrcat(*error_msg, "\n", e, NULL);
free(*error_msg);
*error_msg = new_error_msg;
} else {
*error_msg = nvstrdup(e);
}
return 1;
}
return 0;
}
* load_and_resolve_libdata() - Load the given shared object name and check that
* it loads and all function pointers are resolved. If any error is detected,
* close the object handle and save the resulting error messages.
*/
static void load_and_resolve_libdata(const char *gui_lib_name,
GtkLibraryData *libdata)
{
int symbol_error = 0;
libdata->gui_lib_handle = dlopen(gui_lib_name, RTLD_NOW);
if (libdata->gui_lib_handle) {
dlerror();
libdata->fn_ctk_init_check = dlsym(libdata->gui_lib_handle,
"ctk_init_check");
symbol_error += check_and_save_dlerror(&libdata->error_msg);
libdata->fn_ctk_get_display = dlsym(libdata->gui_lib_handle,
"ctk_get_display");
symbol_error += check_and_save_dlerror(&libdata->error_msg);
libdata->fn_ctk_main = dlsym(libdata->gui_lib_handle, "ctk_main");
symbol_error += check_and_save_dlerror(&libdata->error_msg);
if (symbol_error > 0 ||
libdata->fn_ctk_init_check == NULL ||
libdata->fn_ctk_get_display == NULL ||
libdata->fn_ctk_main == NULL) {
dlclose(libdata->gui_lib_handle);
libdata->gui_lib_handle = NULL;
}
} else {
check_and_save_dlerror(&libdata->error_msg);
}
}
static void load_waylandlib(void)
{
int name_index;
const char* waylandlib_names[] = {
"libnvidia-wayland-client.so." NVIDIA_VERSION,
"libnvidia-wayland-client.so",
"./libnvidia-wayland-client.so",
NULL};
if (wllib.lib_handle) {
return;
}
for (name_index = 0;
!wllib.lib_handle && waylandlib_names[name_index] != NULL;
name_index++) {
wllib.lib_handle = dlopen(waylandlib_names[name_index], RTLD_NOW);
}
if (wllib.lib_handle == NULL) {
nv_warning_msg("Wayland Connector Library failed to load.");
}
}
int wconn_wayland_handle_loaded()
{
return wllib.lib_handle != NULL;
}
void *wconn_get_wayland_display(void)
{
if (wconn_wayland_handle_loaded()) {
if (!wllib.fn_get_wayland_display) {
wllib.fn_get_wayland_display = dlsym(wllib.lib_handle,
"get_wayland_display");
}
if (wllib.fn_get_wayland_display) {
return wllib.fn_get_wayland_display();
}
}
return NULL;
}
void *wconn_get_wayland_output_info(void)
{
void *w_output = NULL;
if (wconn_wayland_handle_loaded()) {
if (!wllib.fn_get_wayland_output_info) {
wllib.fn_get_wayland_output_info = dlsym(wllib.lib_handle,
"get_wayland_output_info");
}
if (wllib.fn_get_wayland_output_info) {
w_output = wllib.fn_get_wayland_output_info();
}
if (w_output == NULL) {
nv_warning_msg("Wayland Connector Library failed to connect.");
}
}
return w_output;
}
* remove_flag_from_command_line() - Remove the "--" option and reindexes argv
* and reindex the command line options so that gtk_init_check() can process its
* options. If that flag is not found, no change to argv or argc is made.
*/
static void remove_flag_from_command_line(int *argc, char ***argv)
{
int i, j;
for (i=0, j=0; i<(*argc); i++) {
if (i > j) {
(*argv)[j] = (*argv)[i];
j++;
} else if (strcmp("--", (*argv)[i]) != 0) {
j++;
}
}
(*argc) = j;
(*argv)[(*argc)] = NULL;
}
* load_ui_library() - Decide whether we need to build a library name or use one
* already specified. If we build the name, iterate over our possible name
* options and either open the library or return a NULL pointer on failure.
*/
static void *load_ui_library(GtkLibraryData *libdata, Options *op)
{
struct stat buf;
char *gui_lib_name;
int index = 0;
int max_index = ARRAY_LEN(library_names);
* If the user did not specify a path or the path specified is a directory,
* we must attempt to open our default library names.
*/
if (op->gtk_lib_path != NULL &&
(stat(op->gtk_lib_path, &buf) == 0 && !S_ISDIR(buf.st_mode))) {
gui_lib_name = op->gtk_lib_path;
load_and_resolve_libdata(gui_lib_name, libdata);
} else {
while (!libdata->gui_lib_handle && index < max_index) {
* If we fail to open the default library, work down the list of
* known library names and versions.
*/
gui_lib_name =
get_full_library_name(op->gtk_lib_path ?
op->gtk_lib_path : "", index);
index++;
if (op->use_gtk2 && strstr(gui_lib_name, "gtk2") == NULL) {
continue;
}
load_and_resolve_libdata(gui_lib_name, libdata);
nvfree(gui_lib_name);
}
}
return libdata->gui_lib_handle;
}
* main() - nvidia-settings application start
*/
int main(int argc, char **argv)
{
ConfigProperties conf;
ParsedAttribute *p;
CtrlSystem *system;
CtrlSystemList systems;
Options *op;
int ret;
int gui = 0;
void *w_output = NULL;
GtkLibraryData libdata;
libdata.gui_lib_handle = NULL;
libdata.error_msg = NULL;
wllib.lib_handle = NULL;
wllib.error_msg = NULL;
systems.n = 0;
systems.array = NULL;
nv_set_verbosity(NV_VERBOSITY_DEPRECATED);
load_waylandlib();
op = parse_command_line(argc, argv, &systems);
* Using the default library names, along with a possible path or name
* specified by the user, attempt to dlopen the appropriate user interface
* shared object.
*/
load_ui_library(&libdata, op);
if (libdata.gui_lib_handle) {
* initialize the ui
*
* gui flag used to decide if ctk should be used or not, as
* the user might just use control the display from a remote console
* but for some reason cannot initialize the gtk gui. - TY 2005-05-27
*
* All options intented for gtk must come after the "--" option flag.
* Since gtk will also stop parsing options when encountering this
* flag, we must remove it from argv before calling ctk_init_check.
*/
remove_flag_from_command_line(&argc, &argv);
if (libdata.fn_ctk_init_check(&argc, &argv)) {
if (!op->ctrl_display) {
op->ctrl_display = libdata.fn_ctk_get_display();
}
gui = 1;
}
}
* Quit here if the dynamic load above fails.
*/
if (!libdata.gui_lib_handle) {
nv_error_msg("%s", libdata.error_msg);
nv_error_msg("A problem occurred when loading the GUI library. Please "
"check your installation and library path. You may need "
"to specify this library when calling nvidia-settings. "
"Please run `%s --help` for usage information.\n",
argv[0]);
return 1;
}
if (op->ctrl_display == NULL) {
nv_error_msg("The control display is undefined; please run "
"`%s --help` for usage information.\n", argv[0]);
return 1;
}
NvCtrlConnectToSystem(op->ctrl_display, &systems);
if (op->num_assignments || op->num_queries) {
ret = nv_process_assignments_and_queries(op, &systems);
NvCtrlFreeAllSystems(&systems);
return ret ? 0 : 1;
}
p = nv_parsed_attribute_init();
init_config_properties(&conf);
* Rewrite the X server settings to configuration file
* and exit, without starting a Graphical User Interface.
*/
if (op->rewrite) {
nv_parsed_attribute_clean(p);
system = NvCtrlGetSystem(op->ctrl_display, &systems);
if (!system || !system->dpy) {
return 1;
}
ret = nv_write_config_file(op->config, system, p, &conf);
NvCtrlFreeAllSystems(&systems);
nv_parsed_attribute_free(p);
free(op);
op = NULL;
return ret ? 0 : 1;
}
if (!op->no_load) {
ret = nv_read_config_file(op, op->config, op->ctrl_display,
p, &conf, &systems);
} else {
ret = 1;
}
* if the user requested that we only load the config file, or that
* we only list the resolved targets, then exit now.
*/
if (op->only_load || op->list_targets) {
return ret ? 0 : 1;
}
* past this point, we need to be able to create a gui; fail if
* the gui isn't available; TY 2005-05-27
*/
if (gui == 0) {
nv_error_msg("Unable to create nvidia-settings GUI; please run "
"`%s --help` for usage information.\n", argv[0]);
return 1;
}
system = NvCtrlGetSystem(op->ctrl_display, &systems);
if (!system) {
return 1;
}
* Attempt to load the Wayland connection library. This is not required
* so it may fail to load with no issue.
*/
w_output = wconn_get_wayland_output_info();
system->wayland_output = w_output;
libdata.fn_ctk_main(p, &conf, system, op->page);
if (op->write_config) {
nv_write_config_file(op->config, system, p, &conf);
}
NvCtrlFreeAllSystems(&systems);
nv_parsed_attribute_free(p);
dlclose(libdata.gui_lib_handle);
if (wconn_wayland_handle_loaded()) {
dlclose(wllib.lib_handle);
}
return 0;
}