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 <stdlib.h>
21 #include <math.h>
22 
23 #include <cairo.h>
24 #include <gtk/gtk.h>
25 #include <GL/gl.h>
26 
27 #include "cairo-dock-icon-facility.h"  // cairo_dock_compute_icon_area
28 #include "cairo-dock-dock-facility.h"  // cairo_dock_is_hidden
29 #include "cairo-dock-dock-manager.h"  // gldi_dock_get
30 #include "cairo-dock-dialog-manager.h"
31 #include "cairo-dock-log.h"
32 #include "cairo-dock-config.h"
33 #include "cairo-dock-utils.h"  // cairo_dock_string_is_address
34 #include "cairo-dock-windows-manager.h"  // gldi_windows_get_active
35 #include "cairo-dock-opengl.h"
36 #include "cairo-dock-animations.h"  // cairo_dock_animation_will_be_visible
37 #include "cairo-dock-desktop-manager.h"  // gldi_desktop_get_width
38 #include "cairo-dock-menu.h"  // gldi_menu_new
39 #define _MANAGER_DEF_
40 #include "cairo-dock-container.h"
41 
42 // public (manager, config, data)
43 GldiContainersParam myContainersParam;
44 GldiManager myContainersMgr;
45 GldiObjectManager myContainerObjectMgr;
46 GldiContainer *g_pPrimaryContainer = NULL;
47 GldiDesktopBackground *g_pFakeTransparencyDesktopBg = NULL;
48 
49 // dependancies
50 extern CairoDockGLConfig g_openglConfig;
51 extern gboolean g_bUseOpenGL;
52 extern CairoDockHidingEffect *g_pHidingBackend;  // cairo_dock_is_hidden
53 extern CairoDock *g_pMainDock;  // for the default dock visibility when composite goes off->on
54 
55 // private
56 static gboolean s_bSticky = TRUE;
57 static gboolean s_bInitialOpacity0 = TRUE;  // set initial window opacity to 0, to avoid grey rectangles.
58 static gboolean s_bNoComposite = FALSE;
59 static GldiContainerManagerBackend s_backend;
60 
61 
cairo_dock_set_containers_non_sticky(void)62 void cairo_dock_set_containers_non_sticky (void)
63 {
64 	if (g_pPrimaryContainer != NULL)
65 	{
66 		cd_warning ("this function has to be called before any container is created.");
67 		return;
68 	}
69 	s_bSticky = FALSE;
70 }
71 
cairo_dock_disable_containers_opacity(void)72 void cairo_dock_disable_containers_opacity (void)
73 {
74 	if (g_pPrimaryContainer != NULL)
75 	{
76 		cd_warning ("this function has to be called before any container is created.");
77 		return;
78 	}
79 	s_bInitialOpacity0 = FALSE;
80 }
81 
82 
_prevent_delete(G_GNUC_UNUSED GtkWidget * pWidget,G_GNUC_UNUSED GdkEvent * event,G_GNUC_UNUSED gpointer data)83 static gboolean _prevent_delete (G_GNUC_UNUSED GtkWidget *pWidget, G_GNUC_UNUSED GdkEvent *event, G_GNUC_UNUSED gpointer data)
84 {
85 	cd_debug ("No alt+f4");
86 	return TRUE;  // on empeche les ALT+F4 malheureux.
87 }
88 
cairo_dock_set_default_rgba_visual(GtkWidget * pWidget)89 void cairo_dock_set_default_rgba_visual (GtkWidget *pWidget)
90 {
91 	GdkScreen* pScreen = gtk_widget_get_screen (pWidget);
92 
93 	GdkVisual *pGdkVisual = gdk_screen_get_rgba_visual (pScreen);
94 	if (pGdkVisual == NULL)
95 		pGdkVisual = gdk_screen_get_system_visual (pScreen);
96 
97 	gtk_widget_set_visual (pWidget, pGdkVisual);
98 }
99 
_cairo_default_container_animation_loop(GldiContainer * pContainer)100 static gboolean _cairo_default_container_animation_loop (GldiContainer *pContainer)
101 {
102 	gboolean bContinue = FALSE;
103 
104 	gboolean bUpdateSlowAnimation = FALSE;
105 	pContainer->iAnimationStep ++;
106 	if (pContainer->iAnimationStep * pContainer->iAnimationDeltaT >= CAIRO_DOCK_MIN_SLOW_DELTA_T)
107 	{
108 		bUpdateSlowAnimation = TRUE;
109 		pContainer->iAnimationStep = 0;
110 		pContainer->bKeepSlowAnimation = FALSE;
111 	}
112 
113 	if (bUpdateSlowAnimation)
114 	{
115 		gldi_object_notify (pContainer, NOTIFICATION_UPDATE_SLOW, pContainer, &pContainer->bKeepSlowAnimation);
116 	}
117 
118 	gldi_object_notify (pContainer, NOTIFICATION_UPDATE, pContainer, &bContinue);
119 
120 	if (! bContinue && ! pContainer->bKeepSlowAnimation)
121 	{
122 		pContainer->iSidGLAnimation = 0;
123 		return FALSE;
124 	}
125 	else
126 		return TRUE;
127 }
128 
_set_opacity(GtkWidget * pWidget,G_GNUC_UNUSED cairo_t * ctx,GldiContainer * pContainer)129 static gboolean _set_opacity (GtkWidget *pWidget, G_GNUC_UNUSED cairo_t *ctx, GldiContainer *pContainer)
130 {
131 	if (pContainer->iWidth != 1 ||pContainer->iHeight != 1)
132 	{
133 		g_signal_handlers_disconnect_by_func (pWidget, _set_opacity, pContainer);  // we'll never need to pass here any more, so simply disconnect ourselves.
134 		//g_print ("____OPACITY 1 (%dx%d)\n", pContainer->iWidth, pContainer->iHeight);
135 		#if GTK_CHECK_VERSION (3, 8, 0)
136 		gtk_widget_set_opacity (pWidget, 1.);
137 		#else
138 		gtk_window_set_opacity (GTK_WINDOW (pWidget), 1.);
139 		#endif
140 	}
141 	return FALSE ;
142 }
143 
_remove_background(G_GNUC_UNUSED GtkWidget * pWidget,GldiContainer * pContainer)144 static void _remove_background (G_GNUC_UNUSED GtkWidget *pWidget, GldiContainer *pContainer)
145 {
146 	gdk_window_set_background_pattern (gldi_container_get_gdk_window (pContainer), NULL);  // window must be realized (shown)
147 }
148 
cairo_dock_redraw_container(GldiContainer * pContainer)149 void cairo_dock_redraw_container (GldiContainer *pContainer)
150 {
151 	g_return_if_fail (pContainer != NULL);
152 	GdkRectangle rect = {0, 0, pContainer->iWidth, pContainer->iHeight};
153 	if (! pContainer->bIsHorizontal)
154 	{
155 		rect.width = pContainer->iHeight;
156 		rect.height = pContainer->iWidth;
157 	}
158 	cairo_dock_redraw_container_area (pContainer, &rect);
159 }
160 
_redraw_container_area(GldiContainer * pContainer,GdkRectangle * pArea)161 static inline void _redraw_container_area (GldiContainer *pContainer, GdkRectangle *pArea)
162 {
163 	g_return_if_fail (pContainer != NULL);
164 	if (! gldi_container_is_visible (pContainer))
165 		return ;
166 
167 	if (pArea->y < 0)
168 		pArea->y = 0;
169 	if (pContainer->bIsHorizontal && pArea->y + pArea->height > pContainer->iHeight)
170 		pArea->height = pContainer->iHeight - pArea->y;
171 	else if (! pContainer->bIsHorizontal && pArea->x + pArea->width > pContainer->iHeight)
172 		pArea->width = pContainer->iHeight - pArea->x;
173 
174 	if (pArea->width > 0 && pArea->height > 0)
175 		gdk_window_invalidate_rect (gldi_container_get_gdk_window (pContainer), pArea, FALSE);
176 }
177 
cairo_dock_redraw_container_area(GldiContainer * pContainer,GdkRectangle * pArea)178 void cairo_dock_redraw_container_area (GldiContainer *pContainer, GdkRectangle *pArea)
179 {
180 	if (CAIRO_DOCK_IS_DOCK (pContainer) && ! cairo_dock_animation_will_be_visible (CAIRO_DOCK (pContainer)))  // inutile de redessiner.
181 		return ;
182 	_redraw_container_area (pContainer, pArea);
183 }
184 
cairo_dock_redraw_icon(Icon * icon)185 void cairo_dock_redraw_icon (Icon *icon)
186 {
187 	g_return_if_fail (icon != NULL);
188 	GldiContainer *pContainer = cairo_dock_get_icon_container (icon);
189 	g_return_if_fail (pContainer != NULL);
190 	GdkRectangle rect;
191 	cairo_dock_compute_icon_area (icon, pContainer, &rect);
192 
193 	if (CAIRO_DOCK_IS_DOCK (pContainer) &&
194 		( (cairo_dock_is_hidden (CAIRO_DOCK (pContainer)) && ! icon->bIsDemandingAttention && ! icon->bAlwaysVisible)
195 		|| (CAIRO_DOCK (pContainer)->iRefCount != 0 && ! gldi_container_is_visible (pContainer)) ) )  // inutile de redessiner.
196 		return ;
197 	_redraw_container_area (pContainer, &rect);
198 }
199 
200 
cairo_dock_allow_widget_to_receive_data(GtkWidget * pWidget,GCallback pCallBack,gpointer data)201 void cairo_dock_allow_widget_to_receive_data (GtkWidget *pWidget, GCallback pCallBack, gpointer data)
202 {
203 	// /*GtkTargetEntry pTargetEntry[6] = {0};
204 	// pTargetEntry[0].target = (gchar*)"text/*";
205 	/* pTargetEntry[0].flags = (GtkTargetFlags) 0;
206 	pTargetEntry[0].info = 0;
207 	pTargetEntry[1].target = (gchar*)"text/uri-list";
208 	pTargetEntry[2].target = (gchar*)"text/plain";
209 	pTargetEntry[3].target = (gchar*)"text/plain;charset=UTF-8";
210 	pTargetEntry[4].target = (gchar*)"text/directory";
211 	pTargetEntry[5].target = (gchar*)"text/html";
212 	gtk_drag_dest_set (pWidget,
213 		GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION,  // GTK_DEST_DEFAULT_HIGHLIGHT ne rend pas joli je trouve.
214 		pTargetEntry,
215 		6,
216 		GDK_ACTION_COPY | GDK_ACTION_MOVE);  // le 'GDK_ACTION_MOVE' c'est pour KDE.*/
217 	gtk_drag_dest_set (pWidget,
218 		GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION,  // GTK_DEST_DEFAULT_HIGHLIGHT ne rend pas joli je trouve.
219 		NULL,
220 		0,
221 		GDK_ACTION_COPY | GDK_ACTION_MOVE);  // le 'GDK_ACTION_MOVE' c'est pour KDE.
222 	gtk_drag_dest_add_uri_targets (pWidget);
223 	gtk_drag_dest_add_text_targets (pWidget);
224 
225 	g_signal_connect (G_OBJECT (pWidget),
226 		"drag_data_received",
227 		pCallBack,
228 		data);
229 }
230 
gldi_container_disable_drop(GldiContainer * pContainer)231 void gldi_container_disable_drop (GldiContainer *pContainer)
232 {
233 	gtk_drag_dest_set_target_list (pContainer->pWidget, NULL);
234 }
235 
gldi_container_notify_drop_data(GldiContainer * pContainer,gchar * cReceivedData,Icon * pPointedIcon,double fOrder)236 void gldi_container_notify_drop_data (GldiContainer *pContainer, gchar *cReceivedData, Icon *pPointedIcon, double fOrder)
237 {
238 	g_return_if_fail (cReceivedData != NULL);
239 	gchar *cData = NULL;
240 
241 	gchar **cStringList = g_strsplit (cReceivedData, "\n", -1);
242 	GString *sArg = g_string_new ("");
243 	int i=0, j;
244 	while (cStringList[i] != NULL)
245 	{
246 		g_string_assign (sArg, cStringList[i]);
247 
248 		if (! cairo_dock_string_is_address (cStringList[i]))
249 		{
250 			j = i + 1;
251 			while (cStringList[j] != NULL)
252 			{
253 				if (cairo_dock_string_is_address (cStringList[j]))
254 					break ;
255 				g_string_append_printf (sArg, "\n%s", cStringList[j]);
256 				j ++;
257 			}
258 			i = j;
259 		}
260 		else
261 		{
262 			cd_debug (" + adresse");
263 			if (sArg->str[sArg->len-1] == '\r')
264 			{
265 				cd_debug ("retour charriot");
266 				sArg->str[sArg->len-1] = '\0';
267 			}
268 			i ++;
269 		}
270 
271 		cData = sArg->str;
272 		cd_debug (" notification de drop '%s'", cData);
273 		gldi_object_notify (pContainer, NOTIFICATION_DROP_DATA, cData, pPointedIcon, fOrder, pContainer);
274 	}
275 
276 	g_strfreev (cStringList);
277 	g_string_free (sArg, TRUE);
278 }
279 
280 
gldi_container_reserve_space(GldiContainer * pContainer,int left,int right,int top,int bottom,int left_start_y,int left_end_y,int right_start_y,int right_end_y,int top_start_x,int top_end_x,int bottom_start_x,int bottom_end_x)281 void gldi_container_reserve_space (GldiContainer *pContainer, int left, int right, int top, int bottom, int left_start_y, int left_end_y, int right_start_y, int right_end_y, int top_start_x, int top_end_x, int bottom_start_x, int bottom_end_x)
282 {
283 	if (s_backend.reserve_space)
284 		s_backend.reserve_space (pContainer, left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x);
285 }
286 
gldi_container_get_current_desktop_index(GldiContainer * pContainer)287 int gldi_container_get_current_desktop_index (GldiContainer *pContainer)
288 {
289 	if (s_backend.get_current_desktop_index)
290 		return s_backend.get_current_desktop_index (pContainer);
291 	return 0;
292 }
293 
gldi_container_move(GldiContainer * pContainer,int iNumDesktop,int iAbsolutePositionX,int iAbsolutePositionY)294 void gldi_container_move (GldiContainer *pContainer, int iNumDesktop, int iAbsolutePositionX, int iAbsolutePositionY)
295 {
296 	if (s_backend.move)
297 		s_backend.move (pContainer, iNumDesktop, iAbsolutePositionX, iAbsolutePositionY);
298 }
299 
gldi_container_is_active(GldiContainer * pContainer)300 gboolean gldi_container_is_active (GldiContainer *pContainer)
301 {
302 	if (s_backend.is_active)
303 		return s_backend.is_active (pContainer);
304 	return FALSE;
305 }
306 
gldi_container_present(GldiContainer * pContainer)307 void gldi_container_present (GldiContainer *pContainer)
308 {
309 	if (s_backend.present)
310 		s_backend.present (pContainer);
311 }
312 
gldi_container_manager_register_backend(GldiContainerManagerBackend * pBackend)313 void gldi_container_manager_register_backend (GldiContainerManagerBackend *pBackend)
314 {
315 	gpointer *ptr = (gpointer*)&s_backend;
316 	gpointer *src = (gpointer*)pBackend;
317 	gpointer *src_end = (gpointer*)(pBackend + 1);
318 	while (src != src_end)
319 	{
320 		if (*src != NULL)
321 			*ptr = *src;
322 		src ++;
323 		ptr ++;
324 	}
325 }
326 
327 
cairo_dock_emit_signal_on_container(GldiContainer * pContainer,const gchar * cSignal)328 gboolean cairo_dock_emit_signal_on_container (GldiContainer *pContainer, const gchar *cSignal)
329 {
330 	static gboolean bReturn;
331 	g_signal_emit_by_name (pContainer->pWidget, cSignal, NULL, &bReturn);
332 	return FALSE;
333 }
cairo_dock_emit_leave_signal(GldiContainer * pContainer)334 gboolean cairo_dock_emit_leave_signal (GldiContainer *pContainer)
335 {
336 	// actualize the coordinates of the pointer, since they are most probably out-dated (because the mouse has left the dock, or because a right-click generates an event with (0;0) coordinates)
337 	gldi_container_update_mouse_position (pContainer);
338 	return cairo_dock_emit_signal_on_container (pContainer, "leave-notify-event");
339 }
cairo_dock_emit_enter_signal(GldiContainer * pContainer)340 gboolean cairo_dock_emit_enter_signal (GldiContainer *pContainer)
341 {
342 	return cairo_dock_emit_signal_on_container (pContainer, "enter-notify-event");
343 }
344 
345 
346 static GtkWidget *s_pMenu = NULL;  // right-click menu
gldi_container_build_menu(GldiContainer * pContainer,Icon * icon)347 GtkWidget *gldi_container_build_menu (GldiContainer *pContainer, Icon *icon)
348 {
349 	if (s_pMenu != NULL)
350 	{
351 		//g_print ("previous menu still alive\n");
352 		gtk_widget_destroy (GTK_WIDGET (s_pMenu));  // -> 's_pMenu' becomes NULL thanks to the weak pointer.
353 	}
354 	g_return_val_if_fail (pContainer != NULL, NULL);
355 
356 	//\_________________________ On construit le menu.
357 	GtkWidget *menu = gldi_menu_new (icon);
358 
359 	//\_________________________ On passe la main a ceux qui veulent y rajouter des choses.
360 	gboolean bDiscardMenu = FALSE;
361 	gldi_object_notify (pContainer, NOTIFICATION_BUILD_CONTAINER_MENU, icon, pContainer, menu, &bDiscardMenu);
362 	if (bDiscardMenu)
363 	{
364 		gtk_widget_destroy (menu);
365 		return NULL;
366 	}
367 
368 	gldi_object_notify (pContainer, NOTIFICATION_BUILD_ICON_MENU, icon, pContainer, menu);
369 
370 	s_pMenu = menu;
371 	g_object_add_weak_pointer (G_OBJECT (menu), (gpointer*)&s_pMenu);  // will nullify 's_pMenu' as soon as the menu is destroyed.
372 	return menu;
373 }
374 
375 
gldi_container_create_input_shape(GldiContainer * pContainer,int x,int y,int w,int h)376 cairo_region_t *gldi_container_create_input_shape (GldiContainer *pContainer, int x, int y, int w, int h)
377 {
378 	if (pContainer->iWidth == 0 || pContainer->iHeight == 0)  // very unlikely to happen, but anyway avoid this case.
379 		return NULL;
380 
381 	cairo_rectangle_int_t rect = {x, y, w, h};
382 	cairo_region_t *pShapeBitmap = cairo_region_create_rectangle (&rect);  // for a more complex shape, we would need to draw it on a cairo_surface_t, and then make it a region with gdk_cairo_region_from_surface().
383 
384 	return pShapeBitmap;
385 }
386 
387 
388   ////////////
389  /// INIT ///
390 ////////////
391 
392 static CairoDockVisibility s_iPrevVisibility = CAIRO_DOCK_NB_VISI;
_set_visibility(CairoDock * pDock,gpointer data)393 static void _set_visibility (CairoDock *pDock, gpointer data)
394 {
395 	gldi_dock_set_visibility (pDock, GPOINTER_TO_INT (data));
396 }
_enable_fake_transparency(void)397 static void _enable_fake_transparency (void)
398 {
399 	g_pFakeTransparencyDesktopBg = gldi_desktop_background_get (g_bUseOpenGL);
400 	s_bNoComposite = TRUE;
401 	s_iPrevVisibility = g_pMainDock->iVisibility;
402 	gldi_docks_foreach_root ((GFunc)_set_visibility, GINT_TO_POINTER (CAIRO_DOCK_VISI_KEEP_BELOW));  // set the visibility to 'keep below'; that's the best compromise between accessibility and visual annoyance.
403 }
_on_composited_changed(GdkScreen * pScreen,G_GNUC_UNUSED gpointer data)404 static void _on_composited_changed (GdkScreen *pScreen, G_GNUC_UNUSED gpointer data)
405 {
406 	if (!gdk_screen_is_composited (pScreen) || (g_bUseOpenGL && ! g_openglConfig.bAlphaAvailable))
407 	{
408 		_enable_fake_transparency ();
409 	}
410 	else  // composite is now ON => disable fake transparency
411 	{
412 		gldi_desktop_background_destroy (g_pFakeTransparencyDesktopBg);
413 		s_bNoComposite = FALSE;
414 		g_pFakeTransparencyDesktopBg = NULL;
415 		if (s_iPrevVisibility < CAIRO_DOCK_NB_VISI)
416 			gldi_docks_foreach_root ((GFunc)_set_visibility, GINT_TO_POINTER (s_iPrevVisibility));  // restore the previous visibility.
417 	}
418 }
_check_composite_delayed(G_GNUC_UNUSED gpointer data)419 static gboolean _check_composite_delayed (G_GNUC_UNUSED gpointer data)
420 {
421 	// if there is a dialogue at startup, there is no main dock, wait a bit more.
422 	if (g_pMainDock == NULL)
423 		return TRUE;
424 
425 	GdkScreen *pScreen = gdk_screen_get_default ();
426 	if (!gdk_screen_is_composited (pScreen) || (g_bUseOpenGL && ! g_openglConfig.bAlphaAvailable))  // no composite available -> load the desktop background
427 	{
428 		cd_message ("Composite is not available");
429 		/**g_pFakeTransparencyDesktopBg = gldi_desktop_background_get (g_bUseOpenGL);  // we don't modify the visibility on startup; if it's the first launch, the user has to notice the problem. and if it's not, just respect his configuration.
430 		s_bNoComposite = TRUE;*/
431 		_enable_fake_transparency ();  // modify the visibility even on startup, because there is no configuration that is really usable except for 'keep-below'
432 	}
433 	g_signal_connect (pScreen, "composited-changed", G_CALLBACK (_on_composited_changed), NULL);
434 	return FALSE;
435 }
init(void)436 static void init (void)
437 {
438 	g_timeout_add_seconds (4, _check_composite_delayed, NULL);  // we don't want to be annoyed by the activation of the composite on startup
439 }
440 
441   //////////////////
442  /// GET CONFIG ///
443 //////////////////
444 
get_config(GKeyFile * pKeyFile,GldiContainersParam * pContainersParam)445 static gboolean get_config (GKeyFile *pKeyFile, GldiContainersParam *pContainersParam)
446 {
447 	gboolean bFlushConfFileNeeded = FALSE;
448 
449 	int iRefreshFrequency = cairo_dock_get_integer_key_value (pKeyFile, "System", "opengl anim freq", &bFlushConfFileNeeded, 33, NULL, NULL);
450 	pContainersParam->iGLAnimationDeltaT = 1000. / iRefreshFrequency;
451 
452 	iRefreshFrequency = cairo_dock_get_integer_key_value (pKeyFile, "System", "cairo anim freq", &bFlushConfFileNeeded, 25, NULL, NULL);
453 	pContainersParam->iCairoAnimationDeltaT = 1000. / iRefreshFrequency;
454 
455 	return bFlushConfFileNeeded;
456 }
457 
458   ////////////
459  /// LOAD ///
460 ////////////
461 
load(void)462 static void load (void)
463 {
464 	if (s_bNoComposite)
465 	{
466 		g_pFakeTransparencyDesktopBg = gldi_desktop_background_get (g_bUseOpenGL);
467 	}
468 }
469 
470   //////////////
471  /// UNLOAD ///
472 //////////////
473 
unload(void)474 static void unload (void)
475 {
476 	gldi_desktop_background_destroy (g_pFakeTransparencyDesktopBg);  // destroy it, since it will be unloaded anyway by the desktop-manager
477 	g_pFakeTransparencyDesktopBg = NULL;
478 }
479 
480   ///////////////
481  /// MANAGER ///
482 ///////////////
483 
init_object(GldiObject * obj,gpointer attr)484 static void init_object (GldiObject *obj, gpointer attr)
485 {
486 	GldiContainer *pContainer = (GldiContainer*)obj;
487 	GldiContainerAttr *cattr = (GldiContainerAttr*)attr;
488 
489 	pContainer->iface.animation_loop = _cairo_default_container_animation_loop;
490 	pContainer->fRatio = 1;
491 	pContainer->bIsHorizontal = TRUE;
492 	pContainer->bDirectionUp = TRUE;
493 
494 	// create a window
495 	GtkWidget* pWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
496 	pContainer->pWidget = pWindow;
497 	gtk_window_set_default_size (GTK_WINDOW (pWindow), 1, 1);  // this should prevent having grey rectangles during the loading, when the window is mapped and rendered by the WM but not yet by us.
498 	gtk_window_resize (GTK_WINDOW (pWindow), 1, 1);
499 	gtk_widget_set_app_paintable (pWindow, TRUE);
500 	gtk_window_set_decorated (GTK_WINDOW (pWindow), FALSE);
501 	gtk_window_set_skip_pager_hint (GTK_WINDOW(pWindow), TRUE);
502 	gtk_window_set_skip_taskbar_hint (GTK_WINDOW(pWindow), TRUE);
503 	if (s_bSticky)
504 		gtk_window_stick (GTK_WINDOW (pWindow));
505 	g_signal_connect (G_OBJECT (pWindow),
506 		"delete-event",
507 		G_CALLBACK (_prevent_delete),
508 		NULL);
509 	gtk_window_get_size (GTK_WINDOW (pWindow), &pContainer->iWidth, &pContainer->iHeight);  // it's only the initial size allocated by GTK.
510 
511 	// set an RGBA visual for cairo or opengl
512 	if (g_bUseOpenGL && ! cattr->bNoOpengl)
513 	{
514 		gldi_gl_container_init (pContainer);
515 		pContainer->iAnimationDeltaT = myContainersParam.iGLAnimationDeltaT;
516 	}
517 	else
518 	{
519 		cairo_dock_set_default_rgba_visual (pWindow);
520 		pContainer->iAnimationDeltaT = myContainersParam.iCairoAnimationDeltaT;
521 	}
522 	if (pContainer->iAnimationDeltaT == 0)
523 		pContainer->iAnimationDeltaT = 30;
524 
525 	// set the opacity to 0 to avoid seeing grey rectangles until the window is ready to be painted by us.
526 	if (s_bInitialOpacity0)
527 	{
528 		#if GTK_CHECK_VERSION (3, 8, 0)
529 		gtk_widget_set_opacity (pWindow, 0.);
530 		#else
531 		gtk_window_set_opacity (GTK_WINDOW (pWindow), 0.);
532 		#endif
533 		g_signal_connect (G_OBJECT (pWindow),
534 			"draw",
535 			G_CALLBACK (_set_opacity),
536 			pContainer);  // the callback will be removed once it has done its job.
537 	}
538 	g_signal_connect (G_OBJECT (pWindow),
539 		"realize",
540 		G_CALLBACK (_remove_background),
541 		pContainer);
542 
543 	// remove the resize grip added by gtk3
544 	gtk_window_set_has_resize_grip (GTK_WINDOW(pWindow), FALSE);
545 
546 	// make it the primary container if it's the first
547 	if (g_pPrimaryContainer == NULL)
548 		g_pPrimaryContainer = pContainer;
549 }
550 
reset_object(GldiObject * obj)551 static void reset_object (GldiObject *obj)
552 {
553 	GldiContainer *pContainer = (GldiContainer*)obj;
554 
555 	// destroy the opengl context
556 	gldi_gl_container_finish (pContainer);
557 
558 	// destroy the window (will remove all signals)
559 	gtk_widget_destroy (pContainer->pWidget);
560 	pContainer->pWidget = NULL;
561 
562 	// stop the animation loop
563 	if (pContainer->iSidGLAnimation != 0)
564 	{
565 		g_source_remove (pContainer->iSidGLAnimation);
566 		pContainer->iSidGLAnimation = 0;
567 	}
568 
569 	if (g_pPrimaryContainer == pContainer)
570 		g_pPrimaryContainer = NULL;
571 }
572 
gldi_register_containers_manager(void)573 void gldi_register_containers_manager (void)
574 {
575 	// Manager
576 	memset (&myContainersMgr, 0, sizeof (GldiManager));
577 	gldi_object_init (GLDI_OBJECT(&myContainersMgr), &myManagerObjectMgr, NULL);
578 	myContainersMgr.cModuleName  = "Containers";
579 	// interface
580 	myContainersMgr.init         = init;
581 	myContainersMgr.load         = load;
582 	myContainersMgr.unload       = unload;
583 	myContainersMgr.reload       = (GldiManagerReloadFunc)NULL;
584 	myContainersMgr.get_config   = (GldiManagerGetConfigFunc)get_config;
585 	myContainersMgr.reset_config = (GldiManagerResetConfigFunc)NULL;
586 	// Config
587 	myContainersMgr.pConfig = (GldiManagerConfigPtr)&myContainersParam;
588 	myContainersMgr.iSizeOfConfig = sizeof (GldiContainersParam);
589 	// data
590 	myContainersMgr.pData = (GldiManagerDataPtr)NULL;
591 	myContainersMgr.iSizeOfData = 0;
592 
593 	// Object Manager
594 	memset (&myContainerObjectMgr, 0, sizeof (GldiObjectManager));
595 	myContainerObjectMgr.cName        = "Container";
596 	myContainerObjectMgr.iObjectSize  = sizeof (GldiContainer);
597 	// interface
598 	myContainerObjectMgr.init_object  = init_object;
599 	myContainerObjectMgr.reset_object = reset_object;
600 	// signals
601 	gldi_object_install_notifications (&myContainerObjectMgr, NB_NOTIFICATIONS_CONTAINER);
602 }
603