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