1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail    : see the 'copyright' file.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <math.h>
21 #include <GL/gl.h>
22 #include <GL/glu.h>  // gluLookAt
23 
24 #include "cairo-dock-log.h"
25 #include "cairo-dock-utils.h"  // cairo_dock_string_contains
26 #include "cairo-dock-icon-facility.h"  // cairo_dock_get_icon_extent
27 #include "cairo-dock-draw-opengl.h"
28 #include "cairo-dock-desktop-manager.h"  // desktop dimensions
29 
30 #include "cairo-dock-opengl.h"
31 
32 // public (manager, config, data)
33 CairoDockGLConfig g_openglConfig;
34 gboolean g_bUseOpenGL = FALSE;
35 
36 // dependencies
37 extern GldiDesktopBackground *g_pFakeTransparencyDesktopBg;
38 extern gboolean g_bEasterEggs;
39 
40 // private
41 static GldiGLManagerBackend s_backend;
42 static gboolean s_bInitialized = FALSE;
43 static gboolean s_bForceOpenGL = FALSE;
44 
45 
gldi_gl_backend_init(gboolean bForceOpenGL)46 gboolean gldi_gl_backend_init (gboolean bForceOpenGL)
47 {
48 	memset (&g_openglConfig, 0, sizeof (CairoDockGLConfig));
49 	g_bUseOpenGL = FALSE;
50 	s_bForceOpenGL = bForceOpenGL;  // remember it, in case later we can't post-initialize the opengl context
51 
52 	if (s_backend.init)
53 		g_bUseOpenGL = s_backend.init (bForceOpenGL);
54 
55 	return g_bUseOpenGL;
56 }
57 
gldi_gl_backend_deactivate(void)58 void gldi_gl_backend_deactivate (void)
59 {
60 	if (g_bUseOpenGL && s_backend.stop)
61 		s_backend.stop ();
62 	g_bUseOpenGL = FALSE;
63 }
64 
gldi_gl_backend_force_indirect_rendering(void)65 void gldi_gl_backend_force_indirect_rendering (void)
66 {
67 	if (g_bUseOpenGL)
68 		g_openglConfig.bIndirectRendering = TRUE;
69 }
70 
71 
_set_perspective_view(int iWidth,int iHeight)72 static inline void _set_perspective_view (int iWidth, int iHeight)
73 {
74 	glMatrixMode(GL_PROJECTION);
75 	glLoadIdentity();
76 	gluPerspective(60.0, 1.0*(GLfloat)iWidth/(GLfloat)iHeight, 1., 4*iHeight);
77 	glViewport(0, 0, iWidth, iHeight);
78 	glMatrixMode (GL_MODELVIEW);
79 
80 	glLoadIdentity ();
81 	///gluLookAt (0, 0, 3., 0, 0, 0., 0.0f, 1.0f, 0.0f);
82 	///glTranslatef (0., 0., -iHeight*(sqrt(3)/2) - 1);
83 	gluLookAt (iWidth/2, iHeight/2, 3.,  // eye position
84 		iWidth/2, iHeight/2, 0.,  // center position
85 		0.0f, 1.0f, 0.0f);  // up direction
86 	glTranslatef (iWidth/2, iHeight/2, -iHeight*(sqrt(3)/2) - 1);
87 }
gldi_gl_container_set_perspective_view(GldiContainer * pContainer)88 void gldi_gl_container_set_perspective_view (GldiContainer *pContainer)
89 {
90 	int w, h;
91 	if (pContainer->bIsHorizontal)
92 	{
93 		w = pContainer->iWidth;
94 		h = pContainer->iHeight;
95 	}
96 	else
97 	{
98 		w = pContainer->iHeight;
99 		h = pContainer->iWidth;
100 	}
101 	_set_perspective_view (w, h);
102 	pContainer->bPerspectiveView = TRUE;
103 }
104 
gldi_gl_container_set_perspective_view_for_icon(Icon * pIcon)105 void gldi_gl_container_set_perspective_view_for_icon (Icon *pIcon)
106 {
107 	int w, h;
108 	cairo_dock_get_icon_extent (pIcon, &w, &h);
109 	_set_perspective_view (w, h);
110 }
111 
_set_ortho_view(int iWidth,int iHeight)112 static inline void _set_ortho_view (int iWidth, int iHeight)
113 {
114 	glMatrixMode(GL_PROJECTION);
115 	glLoadIdentity();
116 	glOrtho(0, iWidth, 0, iHeight, 0.0, 500.0);
117 	glViewport(0, 0, iWidth, iHeight);
118 	glMatrixMode (GL_MODELVIEW);
119 
120 	glLoadIdentity ();
121 	gluLookAt (iWidth/2, iHeight/2, 3.,  // eye position
122 		iWidth/2, iHeight/2, 0.,  // center position
123 		0.0f, 1.0f, 0.0f);  // up direction
124 	glTranslatef (iWidth/2, iHeight/2, - iHeight/2);
125 }
gldi_gl_container_set_ortho_view(GldiContainer * pContainer)126 void gldi_gl_container_set_ortho_view (GldiContainer *pContainer)
127 {
128 	int w, h;
129 	if (pContainer->bIsHorizontal)
130 	{
131 		w = pContainer->iWidth;
132 		h = pContainer->iHeight;
133 	}
134 	else
135 	{
136 		w = pContainer->iHeight;
137 		h = pContainer->iWidth;
138 	}
139 	_set_ortho_view (w, h);
140 	pContainer->bPerspectiveView = FALSE;
141 }
142 
gldi_gl_container_set_ortho_view_for_icon(Icon * pIcon)143 void gldi_gl_container_set_ortho_view_for_icon (Icon *pIcon)
144 {
145 	int w, h;
146 	cairo_dock_get_icon_extent (pIcon, &w, &h);
147 	_set_ortho_view (w, h);
148 }
149 
150 
151 
gldi_gl_container_make_current(GldiContainer * pContainer)152 gboolean gldi_gl_container_make_current (GldiContainer *pContainer)
153 {
154 	if (s_backend.container_make_current)
155 		return s_backend.container_make_current (pContainer);
156 	return FALSE;
157 }
158 
_apply_desktop_background(GldiContainer * pContainer)159 static void _apply_desktop_background (GldiContainer *pContainer)
160 {
161 	if (! g_pFakeTransparencyDesktopBg || g_pFakeTransparencyDesktopBg->iTexture == 0)
162 		return ;
163 
164 	glPushMatrix ();
165 	gboolean bSetPerspective = pContainer->bPerspectiveView;
166 	if (bSetPerspective)
167 		gldi_gl_container_set_ortho_view (pContainer);
168 	glLoadIdentity ();
169 	_cairo_dock_enable_texture ();
170 	_cairo_dock_set_blend_source ();
171 	_cairo_dock_set_alpha (1.);
172 	glBindTexture (GL_TEXTURE_2D, g_pFakeTransparencyDesktopBg->iTexture);
173 
174 	double x, y, w, h, W, H;
175 	W = gldi_desktop_get_width();
176 	H = gldi_desktop_get_height();
177 	if (pContainer->bIsHorizontal)
178 	{
179 		w = pContainer->iWidth;
180 		h = pContainer->iHeight;
181 		x = pContainer->iWindowPositionX;
182 		y = pContainer->iWindowPositionY;
183 	}
184 	else
185 	{
186 		h = pContainer->iWidth;
187 		w = pContainer->iHeight;
188 		y = pContainer->iWindowPositionX;
189 		x = pContainer->iWindowPositionY;
190 	}
191 
192 	glBegin(GL_QUADS);
193 	glTexCoord2f ((x + 0) / W, (y + 0) / H);
194 	glVertex3f (0., h, 0.);  // Top Left.
195 
196 	glTexCoord2f ((x + w) / W, (y + 0) / H);
197 	glVertex3f (w, h, 0.);  // Top Right
198 
199 	glTexCoord2f ((x + w) / W, (y + h) / H);
200 	glVertex3f (w, 0., 0.);  // Bottom Right
201 
202 	glTexCoord2f ((x + 0.) / W, (y + h) / H);
203 	glVertex3f (0., 0., 0.);  // Bottom Left
204 	glEnd();
205 
206 	_cairo_dock_disable_texture ();
207 	if (bSetPerspective)
208 		gldi_gl_container_set_perspective_view (pContainer);
209 	glPopMatrix ();
210 }
211 
gldi_gl_container_begin_draw_full(GldiContainer * pContainer,GdkRectangle * pArea,gboolean bClear)212 gboolean gldi_gl_container_begin_draw_full (GldiContainer *pContainer, GdkRectangle *pArea, gboolean bClear)
213 {
214 	if (! gldi_gl_container_make_current (pContainer))
215 		return FALSE;
216 
217 	glLoadIdentity ();
218 
219 	if (pArea != NULL)
220 	{
221 		glEnable (GL_SCISSOR_TEST);  // ou comment diviser par 4 l'occupation CPU !
222 		glScissor ((int) pArea->x,
223 			(int) (pContainer->bIsHorizontal ? pContainer->iHeight : pContainer->iWidth) -
224 				pArea->y - pArea->height,  // lower left corner of the scissor box.
225 			(int) pArea->width,
226 			(int) pArea->height);
227 	}
228 
229 	if (bClear)
230 	{
231 		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
232 		_apply_desktop_background (pContainer);
233 	}
234 
235 	return TRUE;
236 }
237 
gldi_gl_container_end_draw(GldiContainer * pContainer)238 void gldi_gl_container_end_draw (GldiContainer *pContainer)
239 {
240 	glDisable (GL_SCISSOR_TEST);
241 	if (s_backend.container_end_draw)
242 		s_backend.container_end_draw (pContainer);
243 }
244 
245 
_init_opengl_context(G_GNUC_UNUSED GtkWidget * pWidget,GldiContainer * pContainer)246 static void _init_opengl_context (G_GNUC_UNUSED GtkWidget* pWidget, GldiContainer *pContainer)
247 {
248 	if (! gldi_gl_container_make_current (pContainer))
249 		return;
250 
251 	//g_print ("INIT OPENGL ctx\n");
252 	glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
253 	glClearDepth (1.0f);
254 	glClearStencil (0);
255 	glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
256 
257 	/// a tester ...
258 	///if (g_bEasterEggs)
259 	///	glEnable (GL_MULTISAMPLE_ARB);
260 
261 	// set once and for all
262 	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);  // GL_MODULATE / GL_DECAL /  GL_BLEND
263 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
264 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
265 	glTexParameteri (GL_TEXTURE_2D,
266 		GL_TEXTURE_MIN_FILTER,
267 		g_bEasterEggs ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
268 	if (g_bEasterEggs)
269 		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
270 	glTexParameteri (GL_TEXTURE_2D,
271 		GL_TEXTURE_MAG_FILTER,
272 		GL_LINEAR);
273 }
274 
275 // TODO: remove that when Mesa 10.1 will be used by most people
_is_blacklisted(const gchar * cVersion,const gchar * cVendor,const gchar * cRenderer)276 static gboolean _is_blacklisted (const gchar *cVersion, const gchar *cVendor, const gchar *cRenderer)
277 {
278 	g_return_val_if_fail (cVersion && cVendor && cRenderer, FALSE);
279 	if (strstr (cRenderer, "Mesa DRI Intel(R) Ivybridge Mobile") != NULL
280 	    && (strstr (cVersion, "Mesa 9") != NULL // affect all versions <= 10.0
281 	        || strstr (cVersion, "Mesa 10.0") != NULL)
282 	    && strstr (cVendor, "Intel Open Source Technology Center") != NULL)
283 	{
284 		cd_warning ("This card is blacklisted due to a bug with your video drivers: Intel 4000 HD Ivybridge Mobile.\n Please install Mesa >= 10.1");
285 		return TRUE;
286 	}
287 	return FALSE;
288 }
289 
_check_gl_extension(const char * extName)290 static gboolean _check_gl_extension (const char *extName)
291 {
292 	const char *glExtensions = (const char *) glGetString (GL_EXTENSIONS);
293 	return cairo_dock_string_contains (glExtensions, extName, " ");
294 }
295 
_post_initialize_opengl_backend(G_GNUC_UNUSED GtkWidget * pWidget,GldiContainer * pContainer)296 static void _post_initialize_opengl_backend (G_GNUC_UNUSED GtkWidget *pWidget, GldiContainer *pContainer)  // initialisation necessitant un contexte opengl.
297 {
298 	g_return_if_fail (!s_bInitialized);
299 
300 	if (! gldi_gl_container_make_current (pContainer))
301 		return ;
302 
303 	s_bInitialized = TRUE;
304 	g_openglConfig.bNonPowerOfTwoAvailable = _check_gl_extension ("GL_ARB_texture_non_power_of_two");
305 	g_openglConfig.bFboAvailable = _check_gl_extension ("GL_EXT_framebuffer_object");
306 	if (!g_openglConfig.bFboAvailable)
307 		cd_warning ("No FBO support, some applets will be invisible if placed inside the dock.");
308 
309 	g_openglConfig.bNonPowerOfTwoAvailable = _check_gl_extension ("GL_ARB_texture_non_power_of_two");
310 	g_openglConfig.bAccumBufferAvailable = _check_gl_extension ("GL_SUN_slice_accum");
311 
312 	GLfloat fMaximumAnistropy = 0.;
313 	if (_check_gl_extension ("GL_EXT_texture_filter_anisotropic"))
314 	{
315 		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fMaximumAnistropy);
316 		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fMaximumAnistropy);
317 	}
318 
319 	const gchar *cVersion  = (const gchar *) glGetString (GL_VERSION);
320 	const gchar *cVendor   = (const gchar *) glGetString (GL_VENDOR);
321 	const gchar *cRenderer = (const gchar *) glGetString (GL_RENDERER);
322 
323 	cd_message ("OpenGL config summary :\n - bNonPowerOfTwoAvailable : %d\n - bFboAvailable : %d\n - direct rendering : %d\n - bTextureFromPixmapAvailable : %d\n - bAccumBufferAvailable : %d\n - Anisotroy filtering level max : %.1f\n - OpenGL version: %s\n - OpenGL vendor: %s\n - OpenGL renderer: %s\n\n",
324 		g_openglConfig.bNonPowerOfTwoAvailable,
325 		g_openglConfig.bFboAvailable,
326 		!g_openglConfig.bIndirectRendering,
327 		g_openglConfig.bTextureFromPixmapAvailable,
328 		g_openglConfig.bAccumBufferAvailable,
329 		fMaximumAnistropy,
330 		cVersion,
331 		cVendor,
332 		cRenderer);
333 
334 	// we need a context to use glGetString, this is why we did it now
335 	if (! s_bForceOpenGL && _is_blacklisted (cVersion, cVendor, cRenderer))
336 	{
337 		cd_warning ("%s 'cairo-dock -o'\n"
338 			" OpenGL Version: %s\n OpenGL Vendor: %s\n OpenGL Renderer: %s",
339 			"The OpenGL backend will be deactivated. Note that you can force "
340 			"this OpenGL backend by launching the dock with this command:",
341 			cVersion, cVendor, cRenderer);
342 		gldi_gl_backend_deactivate ();
343 	}
344 }
345 
gldi_gl_container_init(GldiContainer * pContainer)346 void gldi_gl_container_init (GldiContainer *pContainer)
347 {
348 	if (g_bUseOpenGL && s_backend.container_init)
349 		s_backend.container_init (pContainer);
350 
351 	// finish the initialisation of the opengl backend, now that we have a window we can bind context to.
352 	if (! s_bInitialized)
353 		g_signal_connect (G_OBJECT (pContainer->pWidget),
354 			"realize",
355 			G_CALLBACK (_post_initialize_opengl_backend),
356 			pContainer);
357 
358 	// when the window will be realised, initialise its GL context.
359 	g_signal_connect (G_OBJECT (pContainer->pWidget),
360 		"realize",
361 		G_CALLBACK (_init_opengl_context),
362 		pContainer);
363 }
364 
gldi_gl_container_finish(GldiContainer * pContainer)365 void gldi_gl_container_finish (GldiContainer *pContainer)
366 {
367 	if (g_bUseOpenGL && s_backend.container_finish)
368 		s_backend.container_finish (pContainer);
369 }
370 
371 
372 
gldi_gl_manager_register_backend(GldiGLManagerBackend * pBackend)373 void gldi_gl_manager_register_backend (GldiGLManagerBackend *pBackend)
374 {
375 	gpointer *ptr = (gpointer*)&s_backend;
376 	gpointer *src = (gpointer*)pBackend;
377 	gpointer *src_end = (gpointer*)(pBackend + 1);
378 	while (src != src_end)
379 	{
380 		if (*src != NULL)
381 			*ptr = *src;
382 		src ++;
383 		ptr ++;
384 	}
385 }
386