Commit 635666ba authored by Pablo De La Garza's avatar Pablo De La Garza Committed by Aaron Plattner

Stereo Test

It had been requested to provide end users with a way to test if stereo mode has
been configured and is working properly.

To address such, it was decided to create a simple demo/test and to get such app
embedded in nvidia settings by creating a custom gtk widget on which to render
opengl. (Such widget seems to exist, but only in versions of gtk3 that are newer
to what nvidia-settings use, hence the need to create our own).

This implementation will work with gtk3 and ignored on gtk2.
parent 554b4714
/*
* nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
* and Linux systems.
*
* Copyright (C) 2017 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 "ctkutils.h"
#include "ctkglstereo.h"
#ifdef CTK_GTK3
#include <GL/gl.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "right.png.h"
#include "left.png.h"
#include "nvlogo.png.h"
#include "matrix_utils.h"
#include "opengl_loading.h"
#include "ctkglwidget.h"
#endif
#ifdef CTK_GTK3
const unsigned int animateDiv = 1000;
static void animate(StereoAppData *appData, int eye)
{
float x;
int op;
float delta = 0.2;
GLfloat tm[16]; // Temporary matrix
GLfloat *mvp; // Model-View-Projection matrix
mvp = appData->cube->mvp;
if (eye == GL_LEFT) {
op = 1;
} else {
op = -1;
}
x = 2 * M_PI / animateDiv * appData->animationCounter;
genIdentityMatrix(mvp);
genRotateMatrixX(M_PI / 4, tm);
matrixMult(mvp, tm);
genRotateMatrixZ(M_PI / 4, tm);
matrixMult(mvp, tm);
// Apply the calculated rotation for this
// frame along axis X and Y to generate the
// animated effect
genRotateMatrixX(x, tm);
matrixMult(mvp, tm);
genRotateMatrixY(x, tm);
matrixMult(mvp, tm);
// Translates depending on eye
// and locates the object away from zero in the z-axis so
// that is viewable in the fov we define in the next step.
genTranslateMatrix(op * delta, 0, -1.5, tm);
matrixMult(mvp, tm);
genPerspectiveMatrix(M_PI / 2 , 1, 0.5, 5, tm);
matrixMult(mvp, tm);
}
static void produceFrameStereoTest(void *_appData)
{
StereoAppData *appData = _appData;
dGL.glDrawBuffer(GL_BACK_LEFT);
dGL.glClearColor(206.0/255, 206.0/255, 206.0/255, 1);
dGL.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
animate(appData, GL_LEFT);
drawModel(appData->cube);
drawModel(appData->labelLeft);
dGL.glDrawBuffer(GL_BACK_RIGHT);
dGL.glClearColor(206.0/255, 206.0/255, 206.0/255, 1);
dGL.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
animate(appData, GL_RIGHT);
drawModel(appData->cube);
drawModel(appData->labelRight);
appData->animationCounter++;
appData->animationCounter %= animateDiv;
}
static int verifyOpenglForStereo(void)
{
GLint majorVer = 0;
dGL.glGetIntegerv(GL_MAJOR_VERSION, &majorVer);
if (majorVer < 3) {
return -1;
}
return 0;
}
static int setupStereoTest(void *_appData)
{
StereoAppData *appData = _appData;
GdkPixbuf *nvidiaLogo = CTK_LOAD_PIXBUF(nvlogo);
GdkPixbuf *imageLeft = CTK_LOAD_PIXBUF(left);
GdkPixbuf *imageRight = CTK_LOAD_PIXBUF(right);
if (verifyOpenglForStereo() != 0) {
return -2;
}
dGL.glViewport(0, 0, 200, 200);
dGL.glEnable(GL_DEPTH_TEST);
dGL.glEnable(GL_BLEND);
dGL.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
dGL.glEnable(GL_TEXTURE_2D);
dGL.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
appData->animationCounter = 0;
appData->cube = cubeSetup(nvidiaLogo);
appData->labelLeft = labelSetup(-0.75, 0.90, // x and y
0.5, 0.20, // width and height
1, 0, 0, // rgb
imageLeft // GdkPixbuf *
);
appData->labelRight = labelSetup(0.70, 0.85, // x and y
0.6, 0.20, // width and height
0, 0, 1, // rgb
imageRight // GdkPixbuf *
);
g_object_unref(nvidiaLogo);
g_object_unref(imageLeft);
g_object_unref(imageRight);
if (appData->cube == NULL ||
appData->labelLeft == NULL ||
appData->labelRight == NULL) {
return -1;
}
return 0;
}
#endif
GtkWidget *ctk_glstereo_new(void)
{
#ifdef CTK_GTK3
GtkWidget *gtk_widget;
CtkGLWidget *ctk_glwidget;
int glx_attributes[] = {
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, 1,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_ALPHA_SIZE, 1,
GLX_DEPTH_SIZE, 1,
GLX_STEREO, 1,
None
};
void *app_data;
app_data = malloc(sizeof(StereoAppData));
gtk_widget = ctk_glwidget_new(glx_attributes,
app_data,
setupStereoTest,
produceFrameStereoTest);
ctk_glwidget = CTK_GLWIDGET(gtk_widget);
if (ctk_glwidget) {
ctk_glwidget->timer_interval = 10; // In milliseconds
gtk_widget_set_size_request(GTK_WIDGET(ctk_glwidget), 200, 200);
return GTK_WIDGET(ctk_glwidget);
}
free(app_data);
#endif
return NULL;
}
/*
* nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
* and Linux systems.
*
* Copyright (C) 2017 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>.
*/
#ifndef __CTK_GLSTEREO_H__
#define __CTK_GLSTEREO_H__
#include "ctkutils.h"
#ifdef CTK_GTK3
#include "opengl_wrappers.h"
#endif
GtkWidget *ctk_glstereo_new(void);
#ifdef CTK_GTK3
typedef struct _StereoAppData StereoAppData;
struct _StereoAppData
{
OpenGLModelData *cube;
OpenGLModelData *labelLeft;
OpenGLModelData *labelRight;
unsigned int animationCounter;
};
#endif
#endif
/*
* nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
* and Linux systems.
*
* Copyright (C) 2017 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 "ctkutils.h"
#include "ctkglwidget.h"
#include <assert.h>
#include <gtk/gtk.h>
#include <GL/glx.h>
#include <gdk/gdkx.h>
#include <stdlib.h>
#include "opengl_loading.h"
GtkWidget *ctk_glwidget_new(int glx_attributes[],
void *app_data,
int (*app_setup_callback) (void *app_data),
void (*draw_frame_callback) (void *app_data) )
{
#ifdef CTK_GTK3
GObject *object;
CtkGLWidget *ctk_glwidget;
GLXFBConfig *fb_configs;
int n_elements;
int x_visual_id;
GdkDisplay *gdk_display;
Display *display;
GLXContext glx_context;
GdkVisual *gdk_visual;
gdk_display = gdk_display_get_default();
display = gdk_x11_display_get_xdisplay(gdk_display);
if (!loadGL()) {
return NULL;
}
fb_configs = dGL.glXChooseFBConfig(display,
DefaultScreen(display),
glx_attributes, &n_elements);
if (n_elements == 0) {
return NULL;
}
dGL.glXGetFBConfigAttrib(display,
fb_configs[0],
GLX_VISUAL_ID,
&x_visual_id);
gdk_visual = gdk_x11_screen_lookup_visual(gdk_screen_get_default(),
x_visual_id);
if (gdk_visual == NULL) {
XFree(fb_configs);
return NULL;
}
gdk_x11_display_error_trap_push(gdk_display);
glx_context = dGL.glXCreateNewContext(display,
fb_configs[0],
GLX_RGBA_TYPE,
NULL,
GL_TRUE);
if (gdk_x11_display_error_trap_pop(gdk_display) != 0) {
glx_context = NULL;
}
XFree(fb_configs);
if (glx_context == NULL) {
return NULL;
}
object = g_object_new(CTK_TYPE_GLWIDGET, NULL);
ctk_glwidget = CTK_GLWIDGET(object);
ctk_glwidget->app_data = app_data;
ctk_glwidget->app_setup_callback = app_setup_callback;
ctk_glwidget->draw_frame_callback = draw_frame_callback;
ctk_glwidget->gdk_display = gdk_display;
ctk_glwidget->gdk_window = NULL;
ctk_glwidget->display = display;
ctk_glwidget->is_error = FALSE;
ctk_glwidget->timer_interval = 100;
ctk_glwidget->glx_context = glx_context;
ctk_glwidget->gdk_visual = gdk_visual;
return GTK_WIDGET(ctk_glwidget);
#else
return NULL;
#endif
}
#ifdef CTK_GTK3
static void on_error(CtkGLWidget *ctk_glwidget);
static gboolean draw_frame_in_glwidget(gpointer p);
static void ctk_glwidget_realize(GtkWidget *gtk_widget)
{
CtkGLWidget *ctk_glwidget;
GdkWindowAttr gdk_attributes;
gint attributes_mask;
GtkAllocation allocation;
ctk_glwidget = CTK_GLWIDGET(gtk_widget);
gtk_widget_set_realized(gtk_widget, TRUE);
gtk_widget_set_has_window(gtk_widget, TRUE);
if (ctk_glwidget->is_error) {
return;
}
if (ctk_glwidget->gdk_window) {
gdk_window_set_user_data(ctk_glwidget->gdk_window,
gtk_widget);
return;
}
gtk_widget_get_allocation(gtk_widget, &allocation);
gdk_attributes.x = allocation.x;
gdk_attributes.y = allocation.y;
gdk_attributes.width = allocation.width;
gdk_attributes.height = allocation.height;
gdk_attributes.wclass = GDK_INPUT_OUTPUT;
gdk_attributes.window_type = GDK_WINDOW_CHILD;
gdk_attributes.event_mask = gtk_widget_get_events(gtk_widget);
gdk_attributes.visual = ctk_glwidget->gdk_visual;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
ctk_glwidget->gdk_window =
gdk_window_new(gtk_widget_get_parent_window(gtk_widget),
&gdk_attributes, attributes_mask);
if (ctk_glwidget->gdk_window == NULL) {
on_error(ctk_glwidget);
return;
}
ctk_glwidget->window = gdk_x11_window_get_xid(ctk_glwidget->gdk_window);
gdk_window_set_user_data(ctk_glwidget->gdk_window, gtk_widget);
gtk_widget_set_window(gtk_widget, ctk_glwidget->gdk_window);
dGL.glXMakeContextCurrent(ctk_glwidget->display, ctk_glwidget->window,
ctk_glwidget->window, ctk_glwidget->glx_context);
if (ctk_glwidget->app_setup_callback(ctk_glwidget->app_data) != 0) {
on_error(ctk_glwidget);
return;
}
g_timeout_add_full(G_PRIORITY_LOW,
ctk_glwidget->timer_interval,
draw_frame_in_glwidget,
ctk_glwidget,
0);
}
static void ctk_glwidget_unrealize(GtkWidget *gtk_widget)
{
if (CTK_GLWIDGET(gtk_widget)->is_error) {
return;
}
if (gtk_widget_get_has_window(gtk_widget)) {
gdk_window_set_user_data(CTK_GLWIDGET(gtk_widget)->gdk_window, NULL);
}
gtk_selection_remove_all(gtk_widget);
gtk_widget_set_realized(gtk_widget, FALSE);
}
static void ctk_glwidget_finalize(GObject *gobject)
{
assert(!"unimplemented");
}
static void ctk_glwidget_class_init(CtkGLWidgetClass *klass)
{
GtkWidgetClass *widget_class;
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
widget_class = (GtkWidgetClass *) klass;
widget_class->realize = ctk_glwidget_realize;
widget_class->unrealize = ctk_glwidget_unrealize;
gobject_class->finalize = ctk_glwidget_finalize;
}
GType ctk_glwidget_get_type(void)
{
static GType ctk_glwidget_type = 0;
if (!ctk_glwidget_type) {
static const GTypeInfo info_ctk_glwidget = {
sizeof (CtkGLWidgetClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) ctk_glwidget_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (CtkGLWidget),
0, /* n_preallocs */
NULL, /* instance_init */
NULL /* value_table */
};
ctk_glwidget_type = g_type_register_static(GTK_TYPE_WIDGET,
"CtkGLWidget", &info_ctk_glwidget, 0);
}
return ctk_glwidget_type;
}
static gboolean draw_frame_in_glwidget(gpointer p)
{
CtkGLWidget *ctk_glwidget = CTK_GLWIDGET(p);
if (ctk_glwidget->is_error) {
return FALSE;
}
if (!gtk_widget_get_realized(GTK_WIDGET(p))) {
return TRUE;
}
if (ctk_widget_is_drawable(GTK_WIDGET(ctk_glwidget))) {
ctk_glwidget_make_current(ctk_glwidget);
ctk_glwidget->draw_frame_callback(ctk_glwidget->app_data);
ctk_glwidget_swap(ctk_glwidget);
}
return TRUE;
}
static void on_error(CtkGLWidget *ctk_glwidget)
{
ctk_glwidget->is_error = TRUE;
gtk_widget_set_size_request(GTK_WIDGET(ctk_glwidget), 0, 0);
if (ctk_glwidget->gdk_window) {
gdk_window_set_user_data(ctk_glwidget->gdk_window, NULL);
}
}
void ctk_glwidget_make_current(CtkGLWidget *ctk_glwidget)
{
dGL.glXMakeContextCurrent(ctk_glwidget->display,
ctk_glwidget->window,
ctk_glwidget->window,
ctk_glwidget->glx_context);
}
void ctk_glwidget_swap(CtkGLWidget *ctk_glwidget)
{
dGL.glXSwapBuffers(ctk_glwidget->display, ctk_glwidget->window);
}
#endif
/*
* nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
* and Linux systems.
*
* Copyright (C) 2017 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>.
*/
#ifndef __CTK_GLWIDGET_H__
#define __CTK_GLWIDGET_H__
#include <gtk/gtk.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <gdk/gdkx.h>
#include "ctkconfig.h"
G_BEGIN_DECLS
#define CTK_TYPE_GLWIDGET (ctk_glwidget_get_type())
#define CTK_GLWIDGET(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), CTK_TYPE_GLWIDGET, CtkGLWidget))
#define CTK_GLWIDGET_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_GLWIDGET, CtkGLWidgetClass))
#define CTK_IS_GLWIDGET(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), CTK_TYPE_GLWIDGET))
#define CTK_IS_GLWIDGET_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_GLWIDGET))
#define CTK_GLWIDGET_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_GLWIDGET, CtkGLWidgetClass))
typedef struct _CtkGLWidget CtkGLWidget;
typedef struct _CtkGLWidgetClass CtkGLWidgetClass;
struct _CtkGLWidget
{
GtkWidget parent;
GdkDisplay *gdk_display;
GdkWindow *gdk_window;
Display *display;
Window window;
GLXContext glx_context;
GdkVisual *gdk_visual;
gboolean is_error;
int timer_interval;
void *app_data;
int (*app_setup_callback) (void *app_data);
void (*draw_frame_callback) (void *app_data);
};
struct _CtkGLWidgetClass
{
GtkWidgetClass parent_class;
};
GType ctk_glwidget_get_type (void);
GtkWidget *ctk_glwidget_new(int glx_attributes[],
void *app_data,
int (*app_setup_callback) (void *app_data),
void (*draw_frame_callback) (void *app_data));
#ifdef CTK_GTK3
void ctk_glwidget_make_current(CtkGLWidget *ctk_glwidget);
void ctk_glwidget_swap(CtkGLWidget *ctk_glwidget);
#endif
G_END_DECLS
#endif
......@@ -34,6 +34,9 @@
#include "ctkutils.h"
#include "ctkbanner.h"
#include "ctkglwidget.h"
#include "ctkglstereo.h"
void ctk_screen_event_handler(GtkWidget *widget,
CtrlEvent *event,
gpointer data);
......@@ -131,6 +134,7 @@ GtkWidget* ctk_screen_new(CtrlTarget *ctrl_target, CtkEvent *ctk_event)
GtkWidget *banner;
GtkWidget *hseparator;
GtkWidget *table;
GtkWidget *ctk_glstereo;
ReturnStatus ret;
......@@ -311,6 +315,16 @@ GtkWidget* ctk_screen_new(CtrlTarget *ctrl_target, CtkEvent *ctk_event)
if (ctk_screen->stereo_available) {
add_table_row(table, 20, 0, 0, "Stereo Mode:", 0, 0,
NvCtrlGetStereoModeName(stereo_mode));
if (stereo_mode != NV_CTRL_STEREO_OFF) {
ctk_glstereo = ctk_glstereo_new();
if (ctk_glstereo) {
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), ctk_glstereo,
FALSE, FALSE, 0);
}
}
}
g_free(screen_number);
......
......@@ -26,6 +26,7 @@
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
......@@ -72,6 +73,7 @@
#include "common-utils.h"
#include "query-assign.h"
#include "opengl_loading.h"
/* column enumeration */
......@@ -1343,6 +1345,20 @@ static GtkWidget *create_quit_dialog(CtkWindow *ctk_window)
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
add_special_config_file_attributes(ctk_window);
gtk_main_quit();
}
......
/*
* nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
* and Linux systems.
*
* Copyright (C) 2017 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 <math.h>
#include <string.h>
#include "matrix_utils.h"
void matrixMult(float a[16], const float b[16])
{
int i, j, k;
float aa[16];
memcpy(aa, a, sizeof(aa));
memset(a, 0, 16 * sizeof(float));
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
for (k = 0; k < 4; k++) {
a[4 * j + i] += aa[4 * j + k] * b[4 * k + i];
}
}
}
}