* 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>.
*/
* ctkwindow.c - implementation of the CtkWindow widget; this widget
* displays a treeview on the left side, and one of several children
* widgets on the right side, depending on which item in the treeview
* is selected.
*/
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ctkwindow.h"
#include "ctkframelock.h"
#include "ctkconfig.h"
#include "ctkutils.h"
#include "ctkscreen.h"
#include "ctkslimm.h"
#include "ctkgpu.h"
#include "ctkcolorcorrection.h"
#include "ctkcolorcorrectionpage.h"
#include "ctkxvideo.h"
#include "ctkopengl.h"
#include "ctkglx.h"
#include "ctkmultisample.h"
#include "ctkthermal.h"
#include "ctkpowermizer.h"
#include "ctk3dvisionpro.h"
#include "ctkdisplaydevice.h"
#include "ctkdisplayconfig.h"
#include "ctkserver.h"
#include "ctkecc.h"
#include "ctkgridlicense.h"
#include "ctkvdpau.h"
#include "ctkappprofile.h"
#include "ctkhelp.h"
#include "ctkevent.h"
#include "ctkconstants.h"
#include "msg.h"
#include "common-utils.h"
#include "query-assign.h"
#include "opengl_loading.h"
#include "ctkpowermode.h"
enum {
CTK_WINDOW_LABEL_COLUMN = 0,
CTK_WINDOW_WIDGET_COLUMN,
CTK_WINDOW_HELP_COLUMN,
CTK_WINDOW_CONFIG_FILE_ATTRIBUTES_FUNC_COLUMN,
CTK_WINDOW_SELECT_WIDGET_FUNC_COLUMN,
CTK_WINDOW_UNSELECT_WIDGET_FUNC_COLUMN,
CTK_WINDOW_NUM_COLUMNS
};
typedef struct {
CtkWindow *window;
CtrlTarget *gpu_target;
GtkTextTagTable *tag_table;
GtkTreeIter parent_iter;
GtkTreeIter *display_iters;
CtkEvent **display_events;
int num_displays;
} UpdateDisplaysData;
typedef void (*config_file_attributes_func_t)(GtkWidget *, ParsedAttribute *);
typedef void (*select_widget_func_t)(GtkWidget *);
typedef void (*unselect_widget_func_t)(GtkWidget *);
static void ctk_window_class_init(CtkWindowClass *, gpointer);
#ifdef CTK_GTK3
static void ctk_window_real_destroy(GtkWidget *);
#else
static void ctk_window_real_destroy(GtkObject *);
#endif
static void add_page(GtkWidget *, GtkTextBuffer *, CtkWindow *,
GtkTreeIter *, GtkTreeIter *, const gchar *,
config_file_attributes_func_t func,
select_widget_func_t load_func,
unselect_widget_func_t unload_func);
static GtkWidget *create_quit_dialog(CtkWindow *ctk_window);
static void quit_response(GtkWidget *, gint, gpointer);
static void save_settings_and_exit(CtkWindow *);
static void add_display_devices(CtkWindow *ctk_window, GtkTreeIter *iter,
CtrlTarget *gpu_target,
CtkEvent *ctk_event,
GtkTextTagTable *tag_table,
UpdateDisplaysData *data,
ParsedAttribute *p);
static void update_display_devices(GtkWidget *object, CtrlEvent *event,
gpointer user_data);
static GObjectClass *parent_class;
* ctk_window_get_type() - when first called, tells GTK about the
* CtkWindow widget class, and gets an ID that uniquely identifies the
* widget class. Upon subsequent calls, it just returns the ID.
*/
GType ctk_window_get_type(void)
{
static GType ctk_window_type = 0;
if (!ctk_window_type) {
static const GTypeInfo ctk_window_info = {
sizeof(CtkWindowClass),
NULL,
NULL,
(GClassInitFunc) ctk_window_class_init,
NULL,
NULL,
sizeof(CtkWindow),
0,
NULL,
NULL
};
ctk_window_type = g_type_register_static
(GTK_TYPE_WINDOW, "CtkWindow", &ctk_window_info, 0);
}
return ctk_window_type;
}
* ctk_window_class_init() - initializes the fields of the widget's
* class structure
*/
static void ctk_window_class_init(CtkWindowClass *ctk_window_class,
gpointer class_data)
{
#ifdef CTK_GTK3
GtkWidgetClass *widget_class;
widget_class = (GtkWidgetClass *) ctk_window_class;
parent_class = g_type_class_peek_parent(ctk_window_class);
widget_class->destroy = ctk_window_real_destroy;
#else
GtkObjectClass *object_class;
object_class = (GtkObjectClass *) ctk_window_class;
parent_class = g_type_class_peek_parent(ctk_window_class);
object_class->destroy = ctk_window_real_destroy;
#endif
}
* ctk_window_real_destroy() - quit gtk. XXX Maybe we should write
* the configuration file here?
*/
#ifdef CTK_GTK3
static void ctk_window_real_destroy(GtkWidget *object)
{
GTK_WIDGET_CLASS(parent_class)->destroy(object);
gtk_main_quit();
}
#else
static void ctk_window_real_destroy(GtkObject *object)
{
GTK_OBJECT_CLASS(parent_class)->destroy(object);
gtk_main_quit();
}
#endif
* confirm_quit_and_save() - Shows the user the quit dialog if there are
* pending changes and saves configuration information before exiting.
*/
static void confirm_quit_and_save(CtkWindow *ctk_window)
{
CtkConfig *ctk_config = ctk_window->ctk_config;
if (ctk_config->pending_config) {
const char *beg = "You have pending changes on following page(s):\n\n";
const char *end = "Do you really want to quit?";
const char *prefix;
char *pages, *tmp;
pages = strdup("");
if (ctk_config->pending_config &
CTK_CONFIG_PENDING_APPLY_DISPLAY_CONFIG) {
prefix = (strlen(pages) == 0) ? "" : ",\n";
tmp = nvstrcat(pages, prefix,
"X Server Display Configuration - Apply", NULL);
free(pages);
pages = tmp;
}
if (ctk_config->pending_config &
CTK_CONFIG_PENDING_WRITE_DISPLAY_CONFIG) {
prefix = (strlen(pages) == 0) ? "" : ",\n";
tmp = nvstrcat(pages, prefix,
"X Server Display Configuration - "
"Save to X Configuration File", NULL);
free(pages);
pages = tmp;
}
if (ctk_config->pending_config &
CTK_CONFIG_PENDING_WRITE_MOSAIC_CONFIG) {
prefix = (strlen(pages) == 0) ? "" : ",\n";
tmp = nvstrcat(pages, prefix,
"SLI Mosaic Mode Settings - "
"Save to X Configuration File", NULL);
free(pages);
pages = tmp;
}
if (ctk_config->pending_config &
CTK_CONFIG_PENDING_WRITE_APP_PROFILES) {
prefix = (strlen(pages) == 0) ? "" : ",\n";
tmp = nvstrcat(pages, prefix,
"Application Profiles - Save Changes", NULL);
free(pages);
pages = tmp;
}
if (pages[0] != '\0') {
tmp = nvstrcat(beg, pages, "\n\n", end, NULL);
gtk_label_set_text(
GTK_LABEL(ctk_window->quit_dialog_pending_label),
tmp);
} else {
gtk_label_set_text(
GTK_LABEL(ctk_window->quit_dialog_pending_label),
"You have pending changes.\n\n"
"Do you really want to quit?");
}
gtk_widget_show_all(ctk_window->quit_dialog);
return;
}
save_settings_and_exit(ctk_window);
}
* close_button_clicked() - called when the "Quit" button is clicked.
*/
static void close_button_clicked(GtkButton *button, gpointer user_data)
{
CtkWindow *ctk_window = CTK_WINDOW(user_data);
confirm_quit_and_save(ctk_window);
}
static gboolean ctk_window_delete_event(GObject *object)
{
CtkWindow *ctk_window = CTK_WINDOW(object);
confirm_quit_and_save(ctk_window);
* so halt the progress of the delete-event here so we don't exit
* prematurely.
*/
return TRUE;
}
* help_button_toggled() - callback when the help button is toggled;
* hides or shows the help window.
*/
static void help_button_toggled(GtkToggleButton *button, gpointer user_data)
{
CtkWindow *ctk_window;
gboolean enabled;
ctk_window = CTK_WINDOW(user_data);
enabled = gtk_toggle_button_get_active(button);
if (enabled) {
if (ctk_window->ctk_help == NULL) {
ctk_window->ctk_help = ctk_help_new(GTK_WIDGET(button),
ctk_window->help_tag_table);
ctk_help_set_page(CTK_HELP(ctk_window->ctk_help),
ctk_window->help_text_buffer);
}
gtk_widget_show_all(ctk_window->ctk_help);
} else {
gtk_widget_hide(ctk_window->ctk_help);
}
}
* tree_selection_changed() - the selection in the tree changed;
* change which page is viewed accordingly.
*/
static void tree_selection_changed(GtkTreeSelection *selection,
gpointer user_data)
{
GtkTreeIter iter;
CtkWindow *ctk_window = CTK_WINDOW(user_data);
GtkTreeModel *model = GTK_TREE_MODEL(ctk_window->tree_store);
GtkWidget *widget;
GtkTextBuffer *help;
select_widget_func_t select_func;
unselect_widget_func_t unselect_func;
if (!gtk_tree_selection_get_selected(selection, &model, &iter))
return;
gtk_tree_model_get(model, &iter, CTK_WINDOW_WIDGET_COLUMN, &widget, -1);
gtk_tree_model_get(model, &iter, CTK_WINDOW_HELP_COLUMN, &help, -1);
gtk_tree_model_get(model, &iter, CTK_WINDOW_SELECT_WIDGET_FUNC_COLUMN,
&select_func, -1);
* remove the existing widget from the page viewer, if anything is
* presently there
*/
if (ctk_window->page) {
gtk_container_remove(GTK_CONTAINER(ctk_window->page_viewer),
ctk_window->page);
ctk_window->page = NULL;
}
if (ctk_window->widget) {
gtk_tree_model_get(model, &(ctk_window->iter),
CTK_WINDOW_UNSELECT_WIDGET_FUNC_COLUMN,
&unselect_func, -1);
if (unselect_func) {
(*unselect_func)(ctk_window->widget);
}
}
if (widget) {
ctk_window->page = widget;
gtk_box_pack_start(GTK_BOX(ctk_window->page_viewer), widget,
TRUE, TRUE, 2);
}
if (select_func) (*select_func)(widget);
if (ctk_window->ctk_help != NULL)
ctk_help_set_page(CTK_HELP(ctk_window->ctk_help), help);
ctk_window->help_text_buffer = help;
ctk_window->iter = iter;
ctk_window->widget = widget;
}
* row_activated_event() - callback for row-activated event
* - handles key presses automatically
* - allows the mouse to collapse/expand the menu even when the
* expand button/triangle is not working.
*/
static void row_activated_event(GtkTreeView *view,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer user_data)
{
CtkWindow *ctk_window = CTK_WINDOW(user_data);
if (gtk_tree_view_row_expanded(ctk_window->treeview, path)) {
gtk_tree_view_collapse_row(ctk_window->treeview, path);
} else {
gtk_tree_view_expand_row(ctk_window->treeview, path, FALSE);
}
}
static gboolean has_randr_gamma(CtrlTarget *target)
{
ReturnStatus ret;
int val;
ret = NvCtrlGetAttribute(target, NV_CTRL_ATTR_RANDR_GAMMA_AVAILABLE,
&val);
return ((ret == NvCtrlSuccess) && (val == 1));
}
* ctk_window_new() - create a new CtkWindow widget
*/
GtkWidget *ctk_window_new(ParsedAttribute *p, ConfigProperties *conf,
CtrlSystem *system)
{
GObject *object;
CtkWindow *ctk_window;
GtkWidget *sw;
GtkWidget *hpane;
GtkWidget *frame;
GtkWidget *vbox, *hbox;
GtkWidget *widget;
GtkWidget *button;
GtkWidget *toggle_button;
GtkWidget *statusbar;
GtkWidget *eventbox;
GtkWidget *label;
GtkRequisition req;
gint width;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTextTagTable *tag_table;
GtkTextBuffer *help;
CtrlTargetNode *node;
CtrlTarget *default_x_target = NULL;
CtrlTarget *default_gpu_target = NULL;
CtrlTarget *ctrl_target;
CtkEvent *ctk_event;
CtkConfig *ctk_config;
gint column_offset;
object = g_object_new(CTK_TYPE_WINDOW, NULL);
ctk_window = CTK_WINDOW(object);
gtk_container_set_border_width(GTK_CONTAINER(ctk_window), CTK_WINDOW_PAD);
ctk_window->attribute_list = p;
ctk_window->ctk_config = CTK_CONFIG(ctk_config_new(conf, system));
ctk_config = ctk_window->ctk_config;
ctk_window->quit_dialog = create_quit_dialog(ctk_window);
hbox = gtk_hbox_new(FALSE, 5);
vbox = gtk_vbox_new(FALSE, 5);
gtk_container_add(GTK_CONTAINER(ctk_window), vbox);
gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
statusbar = ctk_config_get_statusbar(ctk_config);
eventbox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(eventbox), statusbar);
gtk_box_pack_start(GTK_BOX(hbox), eventbox, TRUE, TRUE, 0);
ctk_config_set_tooltip(ctk_config, eventbox, "The status bar displays "
"the most recent change that has been sent to the "
"X server.");
toggle_button = gtk_toggle_button_new();
g_object_set(G_OBJECT(toggle_button),
"label", GTK_STOCK_HELP,
"use_stock", GINT_TO_POINTER(TRUE), NULL);
gtk_widget_set_size_request(toggle_button, 100, -1);
gtk_box_pack_start(GTK_BOX(hbox), toggle_button, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(toggle_button), "toggled",
G_CALLBACK(help_button_toggled),
(gpointer) ctk_window);
ctk_window->ctk_help = NULL;
tag_table = ctk_help_create_tag_table();
ctk_window->help_tag_table = tag_table;
ctk_config_set_tooltip(ctk_config, toggle_button, "The Help button "
"toggles the display of a help window which "
"provides a detailed explanation of the available "
"options in the current page.");
button = gtk_button_new_from_stock(GTK_STOCK_QUIT);
gtk_widget_set_size_request(button, 100, -1);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(close_button_clicked),
(gpointer) ctk_window);
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
ctk_config_set_tooltip(ctk_config, button, "The Quit button causes the "
"current settings to be saved to the configuration "
"file (~/.nvidia-settings-rc), and nvidia-settings "
"to exit.");
hpane = gtk_hpaned_new();
gtk_box_pack_start(GTK_BOX(vbox), hpane, TRUE, TRUE, 0);
frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
gtk_paned_pack1(GTK_PANED(hpane), frame, FALSE, FALSE);
sw = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(frame), sw);
ctk_window->tree_store =
gtk_tree_store_new(CTK_WINDOW_NUM_COLUMNS,
G_TYPE_STRING,
G_TYPE_POINTER,
G_TYPE_POINTER,
G_TYPE_POINTER,
G_TYPE_POINTER,
G_TYPE_POINTER);
model = GTK_TREE_MODEL(ctk_window->tree_store);
ctk_window->treeview = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
g_object_unref(ctk_window->tree_store);
g_signal_connect(ctk_window->treeview, "row_activated",
G_CALLBACK(row_activated_event), GTK_WIDGET(ctk_window));
selection = gtk_tree_view_get_selection(ctk_window->treeview);
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
g_object_set(G_OBJECT(ctk_window->treeview), "headers-visible",
FALSE, NULL);
renderer = gtk_cell_renderer_text_new();
column_offset = gtk_tree_view_insert_column_with_attributes
(ctk_window->treeview, -1, NULL, renderer, "text",
CTK_WINDOW_LABEL_COLUMN, NULL);
column = gtk_tree_view_get_column(ctk_window->treeview, column_offset - 1);
gtk_tree_view_column_set_clickable(GTK_TREE_VIEW_COLUMN(column), TRUE);
gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(ctk_window->treeview));
hbox = gtk_hbox_new(FALSE, 0);
gtk_paned_pack2(GTK_PANED(hpane), hbox, TRUE, FALSE);
ctk_window->page_viewer = hbox;
ctk_window->page = NULL;
* Create generic and specific default system targets. X Screen target
* will only exist if the X Server is available. In that case, the generic
* default target will be the default gpu target instead.
*/
default_x_target = NvCtrlGetDefaultTargetByType(system, X_SCREEN_TARGET);
default_gpu_target = NvCtrlGetDefaultTargetByType(system, GPU_TARGET);
ctrl_target = default_x_target;
if (!ctrl_target) {
ctrl_target = default_gpu_target;
}
ctk_event = CTK_EVENT(ctk_event_new(default_gpu_target));
if (ctrl_target) {
GtkWidget *child;
child = ctk_server_new(ctrl_target, ctk_config);
if (system->has_nv_control) {
add_page(child,
ctk_server_create_help(tag_table, CTK_SERVER(child)),
ctk_window, NULL, NULL, "System Information",
NULL, NULL, NULL);
} else if (child) {
gtk_tree_store_append(ctk_window->tree_store, &iter, NULL);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_LABEL_COLUMN,
"System Information", -1);
g_object_ref(G_OBJECT(child));
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_WIDGET_COLUMN, child, -1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_HELP_COLUMN,
ctk_server_create_help(tag_table,
CTK_SERVER(child)),
-1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_CONFIG_FILE_ATTRIBUTES_FUNC_COLUMN,
NULL, -1);
child = ctk_glx_new(default_gpu_target, ctk_config, ctk_event);
if (child) {
help = ctk_glx_create_help(tag_table, CTK_GLX(child));
add_page(child, help, ctk_window, &iter, NULL,
"Graphics Information", NULL, ctk_glx_probe_info, NULL);
}
}
}
if (default_x_target) {
GtkWidget *child;
child = ctk_display_config_new(default_x_target, ctk_config);
if (child) {
ctk_window->display_config_widget = child;
add_page(child,
ctk_display_config_create_help(tag_table,
CTK_DISPLAY_CONFIG(child)),
ctk_window, NULL, NULL,
"X Server Display Configuration",
NULL, ctk_display_config_selected,
ctk_display_config_unselected);
}
}
widget = ctk_powermode_new(default_gpu_target, ctk_config, ctk_event);
if (widget) {
help = ctk_powermode_create_help(tag_table, CTK_POWERMODE(widget));
add_page(widget, help, ctk_window, NULL, NULL,
"Platform Power Mode",
NULL, ctk_powermode_start_timer, ctk_powermode_stop_timer);
}
for (node = system->targets[X_SCREEN_TARGET]; node; node = node->next) {
gchar *screen_name;
GtkWidget *child;
CtrlTarget *screen_target = node->t;
if (screen_target == NULL || screen_target->h == NULL) {
continue;
}
ctk_event = CTK_EVENT(ctk_event_new(screen_target));
screen_name = g_strdup_printf("X Screen %d",
NvCtrlGetTargetId(screen_target));
gtk_tree_store_append(ctk_window->tree_store, &iter, NULL);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_LABEL_COLUMN, screen_name, -1);
screen_name = NvCtrlGetDisplayName(screen_target);
child = ctk_screen_new(screen_target, ctk_event);
g_object_ref(G_OBJECT(child));
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_WIDGET_COLUMN, child, -1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_HELP_COLUMN,
ctk_screen_create_help(tag_table, CTK_SCREEN(child),
screen_name),
-1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_CONFIG_FILE_ATTRIBUTES_FUNC_COLUMN,
NULL, -1);
* color correction, if RandR per-CRTC color correction is not
* available
*/
if (!has_randr_gamma(screen_target)) {
child = ctk_color_correction_page_new(screen_target, ctk_config,
ctk_window->attribute_list,
ctk_event);
if (child) {
help = ctk_color_correction_page_create_help(tag_table);
add_page(child, help, ctk_window, &iter, NULL,
"X Server Color Correction", NULL, NULL, NULL);
}
}
child = ctk_xvideo_new(screen_target, ctk_config, ctk_event);
if (child) {
help = ctk_xvideo_create_help(tag_table, CTK_XVIDEO(child));
add_page(child, help, ctk_window, &iter, NULL,
"X Server XVideo Settings", NULL, NULL, NULL);
}
child = ctk_opengl_new(screen_target, ctk_config, ctk_event);
if (child) {
help = ctk_opengl_create_help(tag_table, CTK_OPENGL(child));
add_page(child, help, ctk_window, &iter, NULL, "OpenGL Settings",
NULL, NULL, NULL);
}
if (system->has_nv_control) {
child = ctk_glx_new(screen_target, ctk_config, ctk_event);
if (child) {
help = ctk_glx_create_help(tag_table, CTK_GLX(child));
add_page(child, help, ctk_window, &iter, NULL,
"Graphics Information", NULL, ctk_glx_probe_info, NULL);
}
}
child = ctk_multisample_new(screen_target, ctk_config, ctk_event);
if (child) {
help = ctk_multisample_create_help(tag_table,
CTK_MULTISAMPLE(child));
add_page(child, help, ctk_window, &iter, NULL,
"Antialiasing Settings", NULL, NULL, NULL);
}
child = ctk_vdpau_new(screen_target, ctk_config, ctk_event);
if (child) {
help = ctk_vdpau_create_help(tag_table, CTK_VDPAU(child));
add_page(child, help, ctk_window, &iter, NULL, "VDPAU Information",
NULL, NULL, NULL);
}
}
for (node = system->targets[GPU_TARGET]; node; node = node->next) {
gchar *gpu_name;
GtkWidget *child;
CtrlTarget *gpu_target = node->t;
UpdateDisplaysData *data;
if (gpu_target == NULL || gpu_target->h == NULL) {
continue;
}
gpu_name = create_gpu_name_string(gpu_target);
if (!gpu_name) continue;
ctk_event = CTK_EVENT(ctk_event_new(gpu_target));
gtk_tree_store_append(ctk_window->tree_store, &iter, NULL);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_LABEL_COLUMN, gpu_name, -1);
child = ctk_gpu_new(gpu_target, ctk_event, ctk_config);
g_object_ref(G_OBJECT(child));
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_WIDGET_COLUMN, child, -1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_HELP_COLUMN,
ctk_gpu_create_help(tag_table, CTK_GPU(child)), -1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_CONFIG_FILE_ATTRIBUTES_FUNC_COLUMN,
NULL, -1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_SELECT_WIDGET_FUNC_COLUMN,
ctk_gpu_page_select, -1);
gtk_tree_store_set(ctk_window->tree_store, &iter,
CTK_WINDOW_UNSELECT_WIDGET_FUNC_COLUMN,
ctk_gpu_page_unselect, -1);
child = ctk_thermal_new(gpu_target, ctk_config, ctk_event);
if (child) {
help = ctk_thermal_create_help(tag_table, CTK_THERMAL(child));
add_page(child, help, ctk_window, &iter, NULL, "Thermal Settings",
NULL, ctk_thermal_start_timer, ctk_thermal_stop_timer);
}
child = ctk_powermizer_new(gpu_target, ctk_config, ctk_event);
if (child) {
help = ctk_powermizer_create_help(tag_table, CTK_POWERMIZER(child));
add_page(child, help, ctk_window, &iter, NULL, "PowerMizer",
NULL, ctk_powermizer_start_timer,
ctk_powermizer_stop_timer);
}
child = ctk_ecc_new(gpu_target, ctk_config, ctk_event);
if (child) {
help = ctk_ecc_create_help(tag_table, CTK_ECC(child));
add_page(child, help, ctk_window, &iter, NULL, "ECC Settings",
NULL, ctk_ecc_start_timer, ctk_ecc_stop_timer);
}
data = calloc(1, sizeof(*data));
data->window = ctk_window;
data->gpu_target = gpu_target;
data->parent_iter = iter;
data->tag_table = tag_table;
g_signal_connect(G_OBJECT(ctk_event),
CTK_EVENT_NAME(NV_CTRL_PROBE_DISPLAYS),
G_CALLBACK(update_display_devices),
(gpointer) data);
g_signal_connect(G_OBJECT(ctk_event),
CTK_EVENT_NAME(NV_CTRL_MODE_SET_EVENT),
G_CALLBACK(update_display_devices),
(gpointer) data);
add_display_devices(ctk_window, &iter, gpu_target, ctk_event, tag_table,
data, ctk_window->attribute_list);
}
* add the frame lock page, if any of the X screens support
* frame lock
*/
for (node = system->targets[X_SCREEN_TARGET]; node; node = node->next) {
CtrlTarget *screen_target = node->t;
if (screen_target == NULL || screen_target->h == NULL) {
continue;
}
widget = ctk_framelock_new(screen_target, GTK_WIDGET(ctk_window),
ctk_config, ctk_window->attribute_list);
if (!widget) continue;
add_page(widget, ctk_framelock_create_help(tag_table),
ctk_window, NULL, NULL, "Frame Lock",
ctk_framelock_config_file_attributes,
ctk_framelock_select,
ctk_framelock_unselect);
break;
}
for (node = system->targets[NVIDIA_3D_VISION_PRO_TRANSCEIVER_TARGET];
node;
node = node->next) {
CtrlTarget *svp_target = node->t;
if (svp_target == NULL || svp_target->h == NULL) {
continue;
}
ctk_event = CTK_EVENT(ctk_event_new(svp_target));
widget = ctk_3d_vision_pro_new(svp_target, ctk_config,
ctk_window->attribute_list,
ctk_event);
if (!widget) continue;
help = ctk_3d_vision_pro_create_help(tag_table);
add_page(widget, help, ctk_window, NULL, NULL,
"NVIDIA 3D VisionPro", ctk_3d_vision_pro_config_file_attributes,
ctk_3d_vision_pro_select, ctk_3d_vision_pro_unselect);
}
widget = ctk_app_profile_new(ctrl_target, ctk_config);
if (widget) {
add_page(widget, ctk_app_profile_create_help(CTK_APP_PROFILE(widget), tag_table),
ctk_window, NULL, NULL, "Application Profiles",
NULL, NULL, NULL);
}
for (node = system->targets[GPU_TARGET]; node; node = node->next) {
widget = ctk_manage_grid_license_new(node->t, ctk_config);
if (widget) {
help = ctk_manage_grid_license_create_help(tag_table,
CTK_MANAGE_GRID_LICENSE(widget));
add_page(widget, help, ctk_window, NULL, NULL, "Manage License",
NULL, ctk_manage_grid_license_start_timer,
ctk_manage_grid_license_stop_timer);
break;
}
}
add_page(GTK_WIDGET(ctk_window->ctk_config),
ctk_config_create_help(ctk_config, tag_table),
ctk_window, NULL, NULL, "nvidia-settings Configuration",
NULL, NULL, NULL);
* we're done with the current data in the parsed attribute list,
* so clean it out
*/
nv_parsed_attribute_clean(ctk_window->attribute_list);
* now that everything is packed in the treeview, connect the
* signal handler, and autosize the columns in the treeview
*/
g_signal_connect(selection, "changed", G_CALLBACK(tree_selection_changed),
GTK_WIDGET(ctk_window));
gtk_widget_show_all(GTK_WIDGET(ctk_window->treeview));
gtk_tree_view_expand_all(ctk_window->treeview);
gtk_tree_view_columns_autosize(ctk_window->treeview);
if ( gtk_tree_model_get_iter_first(model, &iter) ) {
gtk_tree_selection_select_iter(selection, &iter);
}
gtk_window_set_title(GTK_WINDOW(object), "NVIDIA Settings");
gtk_widget_show_all(GTK_WIDGET(object));
* reasonable. To do that, check the size of a label widget
* with a reasonably long string and limit the tree view scroll
* window's initial width to not extent past this.
*/
label = gtk_label_new("XXXXXX Server Display ConfigurationXXXX");
gtk_widget_show(label);
ctk_widget_get_preferred_size(label, &req);
width = req.width;
gtk_widget_destroy(label);
ctk_widget_get_preferred_size(sw, &req);
* allow users to scroll horizontally (also allow resizing).
*/
if (width < req.width && width > 0) {
gtk_widget_set_size_request(sw, width, -1);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
}
g_signal_connect(G_OBJECT(ctk_window), "delete-event",
G_CALLBACK(ctk_window_delete_event), (gpointer) ctk_window);
return GTK_WIDGET(object);
}
* Set the currently active page to be the page that matches the
* specified label.
*
* Note that child pages of X screens and GPUs cannot be uniquely
* identified by their label alone (e.g., each GPU has a "PowerMizer"
* page). To allow explicit control over which instance of a child
* page is selected, the label can have the optional format
*
* "[PARENT LABEL], [CHILD LABEL]"
*
* E.g.,
*
* "GPU 0 - (GeForce 7600 GT), PowerMizer"
*/
typedef struct {
CtkWindow *ctk_window;
const gchar *label;
} ctk_window_set_active_page_args;
static gboolean ctk_window_set_active_page_callback(GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
GtkTreeIter parent;
gboolean valid;
gchar *iter_label;
gchar *parent_label = "no parent";
gchar *qualified_label;
ctk_window_set_active_page_args *args = data;
valid = gtk_tree_model_iter_parent(model, &parent, iter);
if (valid) {
gtk_tree_model_get(model, &parent,
CTK_WINDOW_LABEL_COLUMN, &parent_label, -1);
}
gtk_tree_model_get(model, iter, CTK_WINDOW_LABEL_COLUMN, &iter_label, -1);
qualified_label = nvstrcat(parent_label, ", ", iter_label, NULL);
if ((strcmp(iter_label, args->label) == 0) ||
(strcmp(qualified_label, args->label) == 0)) {
GtkTreeSelection* selection;
selection = gtk_tree_view_get_selection(args->ctk_window->treeview);
gtk_tree_selection_select_iter(selection, iter);
free(qualified_label);
return TRUE;
}
free(qualified_label);
return FALSE;
}
void ctk_window_set_active_page(CtkWindow *ctk_window, const gchar *label)
{
GtkTreeModel *model = GTK_TREE_MODEL(ctk_window->tree_store);
ctk_window_set_active_page_args args;
if (!label) return;
args.ctk_window = ctk_window;
args.label = label;
gtk_tree_model_foreach(model,
ctk_window_set_active_page_callback,
&args);
}
* add_page() - add a new page to ctk_window's tree_store, using iter
* as a parent; the new child iter is written in pchild_iter, if
* provided.
*/
static void add_page(GtkWidget *widget, GtkTextBuffer *help,
CtkWindow *ctk_window, GtkTreeIter *iter,
GtkTreeIter *child_iter,
const gchar *label, config_file_attributes_func_t func,
select_widget_func_t select_func,
unselect_widget_func_t unselect_func)
{
GtkTreeIter tmp_child_iter;
if (!widget)
return;
if (!child_iter) child_iter = &tmp_child_iter;
* Add a reference to the object and sink (remove) the floating (gtk)
* reference. This sink needs to happen before the page gets packed
* to ensure that we become the proper owner of these widgets. This way,
* page will not be destroyed when they are hidden (removed from the page
* viewer), and we can properly destroy them when needed (for example, the
* display device pages are destroyed/recreated on hotplug events.)
*/
g_object_ref(G_OBJECT(widget));
ctk_g_object_ref_sink(G_OBJECT(widget));
gtk_tree_store_append(ctk_window->tree_store, child_iter, iter);
gtk_tree_store_set(ctk_window->tree_store, child_iter,
CTK_WINDOW_LABEL_COLUMN, label, -1);
gtk_tree_store_set(ctk_window->tree_store, child_iter,
CTK_WINDOW_WIDGET_COLUMN, widget, -1);
gtk_tree_store_set(ctk_window->tree_store, child_iter,
CTK_WINDOW_HELP_COLUMN, help, -1);
gtk_tree_store_set(ctk_window->tree_store, child_iter,
CTK_WINDOW_CONFIG_FILE_ATTRIBUTES_FUNC_COLUMN,
func, -1);
gtk_tree_store_set(ctk_window->tree_store, child_iter,
CTK_WINDOW_SELECT_WIDGET_FUNC_COLUMN,
select_func, -1);
gtk_tree_store_set(ctk_window->tree_store, child_iter,
CTK_WINDOW_UNSELECT_WIDGET_FUNC_COLUMN,
unselect_func, -1);
}
* create_quit_dialog() - create a dialog box to prompt the user
* whether they really want to quit.
*/
static GtkWidget *create_quit_dialog(CtkWindow *ctk_window)
{
GtkWidget *dialog;
GtkWidget *hbox;
GtkWidget *image;
GdkPixbuf *pixbuf;
GtkWidget *alignment;
GtkWidget *label;
dialog = gtk_dialog_new_with_buttons("Really quit?",
GTK_WINDOW(ctk_window),
GTK_DIALOG_MODAL |
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_QUIT,
GTK_RESPONSE_OK,
NULL);
g_signal_connect(GTK_WIDGET(dialog), "response",
G_CALLBACK(quit_response),
GTK_WIDGET(ctk_window));
gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
hbox = gtk_hbox_new(FALSE, 5);
gtk_container_set_border_width(GTK_CONTAINER(hbox), 6);
gtk_container_add(GTK_CONTAINER(ctk_dialog_get_content_area(GTK_DIALOG(dialog))), hbox);
pixbuf = ctk_widget_render_icon(dialog, CTK_STOCK_DIALOG_QUESTION,
GTK_ICON_SIZE_DIALOG, NULL);
image = gtk_image_new_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
alignment = gtk_alignment_new(0.0, 0.0, 0, 0);
gtk_container_add(GTK_CONTAINER(alignment), image);
gtk_box_pack_start(GTK_BOX(hbox), alignment, FALSE, FALSE, 2);
label = gtk_label_new("Do you really want to quit?");
ctk_window->quit_dialog_pending_label = label;
alignment = gtk_alignment_new(0.0, 0.0, 0, 0);
gtk_container_add(GTK_CONTAINER(alignment), label);
gtk_box_pack_start(GTK_BOX(hbox), alignment, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(dialog), "delete-event",
G_CALLBACK(gtk_widget_hide_on_delete),
(gpointer) dialog);
return dialog;
}
* save_settings_and_exit() - save settings, perform cleanups, if
* necessary, and terminate nvidia-settings.
*/
static void save_settings_and_exit(CtkWindow *ctk_window)
{
#ifdef CTK_GTK3
GdkDisplay *gdk_display;
Display *display;
GdkScreen *gdk_screen;
if (dGL.glXMakeContextCurrent) {
gdk_screen = gtk_window_get_screen(GTK_WINDOW(ctk_window));
gdk_display = gdk_screen_get_display(gdk_screen);
display = gdk_x11_display_get_xdisplay(gdk_display);
dGL.glXMakeContextCurrent(display, 0, 0, 0);
}
#endif
if (ctk_window->display_config_widget) {
CtkDisplayConfig *display_config =
CTK_DISPLAY_CONFIG(ctk_window->display_config_widget);
layout_free(display_config->layout);
}
add_special_config_file_attributes(ctk_window);
gtk_main_quit();
}
* quit_response() - handle the response from the "really quit?"
* dialog.
*/
static void quit_response(GtkWidget *button, gint response, gpointer user_data)
{
CtkWindow *ctk_window = CTK_WINDOW(user_data);
if (response == GTK_RESPONSE_OK) {
save_settings_and_exit(ctk_window);
}
gtk_widget_hide(ctk_window->quit_dialog);
}
* Ask all child widgets for any special attributes that should be
* saved to the config file.
*/
static gboolean add_special_config_file_attributes_callback(GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
GtkWidget *widget;
config_file_attributes_func_t func;
CtkWindow *ctk_window = data;
gtk_tree_model_get(model, iter,
CTK_WINDOW_WIDGET_COLUMN, &widget,
CTK_WINDOW_CONFIG_FILE_ATTRIBUTES_FUNC_COLUMN,
&func, -1);
if (func) (*func)(widget, ctk_window->attribute_list);
return FALSE;
}
void add_special_config_file_attributes(CtkWindow *ctk_window)
{
GtkTreeModel *model = GTK_TREE_MODEL(ctk_window->tree_store);
gtk_tree_model_foreach(model,
add_special_config_file_attributes_callback,
ctk_window);
}
* add_display_devices() - add the display device pages
*/
static void add_display_devices(CtkWindow *ctk_window, GtkTreeIter *iter,
CtrlTarget *gpu_target,
CtkEvent *ctk_event_gpu,
GtkTextTagTable *tag_table,
UpdateDisplaysData *data,
ParsedAttribute *p)
{
GtkTextBuffer *help;
ReturnStatus ret;
int *pData = NULL;
int len;
int i;
ret = NvCtrlGetBinaryAttribute(gpu_target, 0,
NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU,
(unsigned char **)(&pData), &len);
if ((ret != NvCtrlSuccess) || (pData[0] <= 0)) {
goto done;
}
nvfree(data->display_iters);
nvfree(data->display_events);
data->display_iters = calloc(pData[0], sizeof(GtkTreeIter));
if (!data->display_iters) {
goto done;
}
data->display_events = calloc(pData[0], sizeof(CtkEvent*));
if (!data->display_events) {
nvfree(data->display_iters);
goto done;
}
data->num_displays = 0;
* create pages for each of the display devices driven by this (gpu)
* handle.
*/
for (i = 0; i < pData[0]; i++) {
int display_id = pData[i+1];
char *logName;
char *typeBaseName;
char *randrName;
GtkWidget *widget;
gchar *title;
CtkEvent *ctk_event;
CtrlSystem *system = ctk_window->ctk_config->pCtrlSystem;
CtrlTarget *target;
* Get the ctrl handle that was passed into ctk_main so that updated
* backend color slider values, cached in the handle itself, can be
* saved to the RC file when the UI is closed.
*/
target = NvCtrlGetTarget(system, DISPLAY_TARGET, display_id);
if (!target) {
target = nv_add_target(system, DISPLAY_TARGET, display_id);
if (!target) {
continue;
}
}
* Rebuild Sub-systems of display handle
*/
NvCtrlRebuildSubsystems(target, NV_CTRL_ATTRIBUTES_ALL_SUBSYSTEMS);
ret = NvCtrlGetStringAttribute(target,
NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME,
&typeBaseName);
if (ret != NvCtrlSuccess) {
continue;
}
ret = NvCtrlGetStringAttribute(target,
NV_CTRL_STRING_DISPLAY_DEVICE_NAME,
&logName);
if (ret != NvCtrlSuccess) {
logName = NULL;
}
ret = NvCtrlGetStringAttribute(target,
NV_CTRL_STRING_DISPLAY_NAME_RANDR,
&randrName);
if (ret != NvCtrlSuccess) {
randrName = NULL;
}
if (!logName && !randrName) {
title = g_strdup_printf("DPY-%d - (Unknown)", display_id);
} else {
title = g_strdup_printf("%s - (%s)", randrName, logName);
}
free(logName);
free(randrName);
ctk_event = CTK_EVENT(ctk_event_new(target));
widget = ctk_display_device_new(target,
ctk_window->ctk_config, ctk_event,
ctk_event_gpu,
title, typeBaseName, p);
if (widget != NULL) {
help = ctk_display_device_create_help(tag_table,
CTK_DISPLAY_DEVICE(widget));
add_page(widget, help, ctk_window, iter,
&(data->display_iters[data->num_displays]), title,
NULL, NULL, NULL);
data->display_events[data->num_displays] = ctk_event;
data->num_displays++;
}
else {
ctk_event_destroy(G_OBJECT(ctk_event));
}
free(typeBaseName);
g_free(title);
}
done:
free(pData);
}
* Select display page whose name is passed through 'name' parameter
*/
static void select_display_page(UpdateDisplaysData *data, gchar *name)
{
CtkWindow *ctk_window = data->window;
GtkTreeSelection *tree_selection =
gtk_tree_view_get_selection(ctk_window->treeview);
gint num_displays = data->num_displays;
while (num_displays) {
GtkTreeIter *iter = &(data->display_iters[num_displays -1]);
CtkDisplayDevice *ctk_obj = NULL;
GtkWidget *widget = NULL;
gtk_tree_model_get(GTK_TREE_MODEL(ctk_window->tree_store), iter,
CTK_WINDOW_WIDGET_COLUMN, &widget, -1);
ctk_obj = CTK_DISPLAY_DEVICE(widget);
if (strcmp(name, ctk_obj->name) == 0) {
gtk_tree_selection_select_iter(tree_selection, iter);
break;
}
num_displays--;
}
}
* update_display_devices() - Callback handler for the NV_CTRL_PROBE_DISPLAYS
* NV-CONTROL event. Updates the list of display devices connected to the
* GPU for which the event happened.
*
*/
static void update_display_devices(GtkWidget *object,
CtrlEvent *event,
gpointer user_data)
{
UpdateDisplaysData *data = (UpdateDisplaysData *) user_data;
CtkWindow *ctk_window = data->window;
CtrlTarget *gpu_target = data->gpu_target;
GtkTreeIter parent_iter = data->parent_iter;
GtkTextTagTable *tag_table = data->tag_table;
GtkTreePath* parent_path;
gboolean parent_expanded;
GtkTreeSelection *tree_selection =
gtk_tree_view_get_selection(ctk_window->treeview);
GtkWidget *widget;
gchar *selected_display_name = NULL;
parent_path =
gtk_tree_model_get_path(GTK_TREE_MODEL(ctk_window->tree_store),
&parent_iter);
parent_expanded =
gtk_tree_view_row_expanded(ctk_window->treeview, parent_path);
while (data->num_displays) {
GtkTreeIter *iter = &(data->display_iters[data->num_displays -1]);
gboolean is_selected = FALSE;
if (gtk_tree_selection_iter_is_selected(tree_selection, iter)) {
is_selected = TRUE;
gtk_tree_selection_select_iter(tree_selection, &parent_iter);
}
gtk_tree_model_get(GTK_TREE_MODEL(ctk_window->tree_store), iter,
CTK_WINDOW_WIDGET_COLUMN, &widget, -1);
if (is_selected) {
CtkDisplayDevice *ctk_obj = CTK_DISPLAY_DEVICE(widget);
selected_display_name = g_strdup(ctk_obj->name);
}
gtk_tree_store_remove(ctk_window->tree_store, iter);
g_object_unref(G_OBJECT(widget));
ctk_event_destroy(G_OBJECT(
data->display_events[data->num_displays - 1]));
data->num_displays--;
}
gtk_tree_model_get(GTK_TREE_MODEL(ctk_window->tree_store), &parent_iter,
CTK_WINDOW_WIDGET_COLUMN, &widget, -1);
add_display_devices(ctk_window, &parent_iter, gpu_target,
CTK_GPU(widget)->ctk_event,
tag_table, data, ctk_window->attribute_list);
if (parent_expanded) {
gtk_tree_view_expand_row(ctk_window->treeview, parent_path, TRUE);
}
if (selected_display_name) {
select_display_page(data, selected_display_name);
g_free(selected_display_name);
}
}