hythmbox-2.98/plugins/visualizer/rb-visualizer-page.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2010  Jonathan Matthew <jonathan@d14n.org>
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2 of the License, or
  8  *  (at your option) any later version.
  9  *
 10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  */
 27 
 28 #include "config.h"
 29 
 30 #include <string.h>
 31 #include <glib.h>
 32 #include <glib/gi18n.h>
 33 
 34 #include "rb-visualizer-page.h"
 35 #include "rb-visualizer-fullscreen.h"
 36 
 37 #include <widgets/rb-dialog.h>
 38 #include <lib/rb-util.h>
 39 #include <lib/rb-debug.h>
 40 
 41 
 42 G_DEFINE_DYNAMIC_TYPE (RBVisualizerPage, rb_visualizer_page, RB_TYPE_DISPLAY_PAGE)
 43 
 44 static GtkWidget *create_embed (RBVisualizerPage *page);
 45 
 46 enum {
 47 	PROP_0,
 48 	PROP_SINK,
 49 	PROP_FULLSCREEN_ACTION,
 50 	PROP_POPUP
 51 };
 52 
 53 enum {
 54 	START,
 55 	STOP,
 56 	FULLSCREEN,
 57 	LAST_SIGNAL
 58 };
 59 
 60 static guint signals[LAST_SIGNAL] = {0,};
 61 
 62 RBVisualizerPage *
 63 rb_visualizer_page_new (GObject *plugin, RBShell *shell, GtkToggleAction *fullscreen, GtkWidget *popup)
 64 {
 65 	GObject *page;
 66 	GdkPixbuf *pixbuf;
 67 	gint size;
 68 
 69 	gtk_icon_size_lookup (RB_SOURCE_ICON_SIZE, &size, NULL);
 70 	pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
 71 					   "visualization",
 72 					   size,
 73 					   0, NULL);
 74 
 75 	page = g_object_new (RB_TYPE_VISUALIZER_PAGE,
 76 			     "plugin", plugin,
 77 			     "shell", shell,
 78 			     "name", _("Visual Effects"),
 79 			     "pixbuf", pixbuf,
 80 			     "fullscreen-action", fullscreen,
 81 			     "popup", popup,
 82 			     NULL);
 83 	if (pixbuf != NULL) {
 84 		g_object_unref (pixbuf);
 85 	}
 86 
 87 	return RB_VISUALIZER_PAGE (page);
 88 }
 89 
 90 static void
 91 set_action_state (RBVisualizerPage *page, gboolean active)
 92 {
 93 	page->setting_state = TRUE;
 94 	g_object_set (page->fullscreen_action, "active", active, NULL);
 95 	page->setting_state = FALSE;
 96 }
 97 
 98 static void
 99 start_fullscreen (RBVisualizerPage *page)
100 {
101 	if (page->fullscreen == NULL) {
102 		ClutterActor *stage;
103 		GtkWindow *main_window;
104 		RBShell *shell;
105 		int x, y;
106 
107 		rb_debug ("starting fullscreen display");
108 		g_object_get (page, "shell", &shell, NULL);
109 		g_object_get (shell, "window", &main_window, NULL);
110 
111 		page->fullscreen = gtk_window_new (GTK_WINDOW_TOPLEVEL);
112 		gtk_window_set_skip_taskbar_hint (GTK_WINDOW (page->fullscreen), TRUE);
113 
114 		/* move the texture from the page embed to the new fullscreen embed */
115 
116 		g_object_ref (page->texture);
117 
118 		stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (page->embed));
119 		clutter_container_remove_actor (CLUTTER_CONTAINER (stage), page->texture);
120 
121 		page->fullscreen_embed = create_embed (page);
122 		stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (page->fullscreen_embed));
123 		clutter_container_add (CLUTTER_CONTAINER (stage), page->texture, NULL);
124 		g_object_unref (page->texture);
125 
126 		gtk_container_add (GTK_CONTAINER (page->fullscreen), page->fullscreen_embed);
127 		gtk_widget_show_all (GTK_WIDGET (page->fullscreen));
128 
129 		gtk_window_get_position (main_window, &x, &y);
130 		gtk_window_move (GTK_WINDOW (page->fullscreen), x, y);
131 
132 		gtk_window_fullscreen (GTK_WINDOW (page->fullscreen));
133 		gtk_window_set_transient_for (GTK_WINDOW (page->fullscreen), main_window);
134 		g_object_unref (main_window);
135 
136 		rb_visualizer_fullscreen_add_widgets (page->fullscreen, stage, shell);
137 		g_object_unref (shell);
138 	}
139 
140 	set_action_state (page, TRUE);
141 }
142 
143 static void
144 stop_fullscreen (RBVisualizerPage *page)
145 {
146 	if (page->fullscreen != NULL) {
147 		ClutterActor *stage;
148 
149 		rb_debug ("stopping fullscreen display");
150 
151 		g_object_ref (page->texture);
152 		stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (page->fullscreen_embed));
153 		rb_visualizer_fullscreen_stop (stage);
154 		clutter_container_remove_actor (CLUTTER_CONTAINER (stage), page->texture);
155 
156 		stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (page->embed));
157 		clutter_container_add (CLUTTER_CONTAINER (stage), page->texture, NULL);
158 
159 		g_object_unref (page->texture);
160 
161 		gtk_widget_destroy (GTK_WIDGET (page->fullscreen));
162 		page->fullscreen = NULL;
163 
164 		page->fullscreen_embed = NULL;
165 	}
166 
167 	set_action_state (page, FALSE);
168 }
169 
170 static void
171 toggle_fullscreen (RBVisualizerPage *page)
172 {
173 	if (page->fullscreen != NULL) {
174 		stop_fullscreen (page);
175 	} else {
176 		start_fullscreen (page);
177 	}
178 }
179 
180 static void
181 toggle_fullscreen_cb (GtkAction *action, RBVisualizerPage *page)
182 {
183 	if (page->setting_state == FALSE) {
184 		toggle_fullscreen (page);
185 	}
186 }
187 
188 static gboolean
189 stage_button_press_cb (ClutterActor *stage, ClutterEvent *event, RBVisualizerPage *page)
190 {
191 	if (event->button.button == 1 && event->button.click_count == 2) {
192 		clutter_threads_leave ();
193 		toggle_fullscreen (page);
194 		clutter_threads_enter ();
195 	} else if (event->button.button == 3) {
196 		rb_display_page_show_popup (RB_DISPLAY_PAGE (page));
197 	}
198 
199 	return FALSE;
200 }
201 
202 static gboolean
203 stage_key_release_cb (ClutterActor *stage, ClutterEvent *event, RBVisualizerPage *page)
204 {
205 	if (event->key.keyval == CLUTTER_KEY_Escape) {
206 		clutter_threads_leave ();
207 		stop_fullscreen (page);
208 		clutter_threads_enter ();
209 	}
210 	return FALSE;
211 }
212 
213 static void
214 resize_sink_texture (ClutterActor *stage, ClutterActorBox *box, ClutterAllocationFlags flags, ClutterActor *texture)
215 {
216 	clutter_actor_set_size (texture, box->x2 - box->x1, box->y2 - box->y1);
217 }
218 
219 
220 static GtkWidget *
221 create_embed (RBVisualizerPage *page)
222 {
223 	ClutterActor *stage;
224 	GtkWidget *embed;
225 
226 	embed = gtk_clutter_embed_new ();
227 
228 	stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (embed));
229 	g_signal_connect_object (stage, "allocation-changed", G_CALLBACK (resize_sink_texture), page->texture, 0);
230 	g_signal_connect_object (stage, "button-press-event", G_CALLBACK (stage_button_press_cb), page, 0);
231 	g_signal_connect_object (stage, "key-release-event", G_CALLBACK (stage_key_release_cb), page, 0);
232 
233 	return embed;
234 }
235 
236 static gboolean
237 impl_show_popup (RBDisplayPage *page)
238 {
239 	RBVisualizerPage *vpage = RB_VISUALIZER_PAGE (page);
240 	gtk_menu_popup (GTK_MENU (vpage->popup), NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time ());
241 	return TRUE;
242 }
243 
244 static void
245 impl_selected (RBDisplayPage *bpage)
246 {
247 	RBVisualizerPage *page = RB_VISUALIZER_PAGE (bpage);
248 
249 	RB_DISPLAY_PAGE_CLASS (rb_visualizer_page_parent_class)->selected (bpage);
250 
251 	if (page->embed == NULL) {
252 		ClutterActor *stage;
253 
254 		page->embed = create_embed (page);
255 
256 		stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (page->embed));
257 
258 		clutter_container_add (CLUTTER_CONTAINER (stage), page->texture, NULL);
259 
260 		gtk_box_pack_start (GTK_BOX (page), page->embed, TRUE, TRUE, 0);
261 		gtk_widget_show_all (GTK_WIDGET (page));
262 	}
263 
264 	g_signal_emit (page, signals[START], 0);
265 }
266 
267 static void
268 impl_deselected (RBDisplayPage *bpage)
269 {
270 	RBVisualizerPage *page = RB_VISUALIZER_PAGE (bpage);
271 
272 	RB_DISPLAY_PAGE_CLASS (rb_visualizer_page_parent_class)->deselected (bpage);
273 
274 	if (page->fullscreen == NULL) {
275 		g_signal_emit (page, signals[STOP], 0);
276 	} else {
277 		/* might as well leave it running.. */
278 	}
279 }
280 
281 static void
282 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
283 {
284 	RBVisualizerPage *page = RB_VISUALIZER_PAGE (object);
285 
286 	switch (prop_id) {
287 	case PROP_SINK:
288 		g_value_set_object (value, page->sink);
289 		break;
290 	case PROP_POPUP:
291 		g_value_set_object (value, page->popup);
292 		break;
293 	case PROP_FULLSCREEN_ACTION:
294 		g_value_set_object (value, page->fullscreen_action);
295 		break;
296 	default:
297 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
298 		break;
299 	}
300 }
301 
302 static void
303 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
304 {
305 	RBVisualizerPage *page = RB_VISUALIZER_PAGE (object);
306 
307 	switch (prop_id) {
308 	case PROP_POPUP:
309 		page->popup = g_value_get_object (value);
310 		break;
311 	case PROP_FULLSCREEN_ACTION:
312 		page->fullscreen_action = g_value_get_object (value);
313 		break;
314 	default:
315 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
316 		break;
317 	}
318 }
319 
320 static void
321 impl_dispose (GObject *object)
322 {
323 	RBVisualizerPage *page = RB_VISUALIZER_PAGE (object);
324 
325 	if (page->embed != NULL) {
326 		gtk_container_remove (GTK_CONTAINER (page), page->embed);
327 		page->embed = NULL;
328 	}
329 	if (page->sink != NULL) {
330 		g_object_unref (page->sink);
331 		page->sink = NULL;
332 	}
333 	if (page->popup != NULL) {
334 		g_object_unref (page->popup);
335 		page->popup = NULL;
336 	}
337 
338 	G_OBJECT_CLASS (rb_visualizer_page_parent_class)->dispose (object);
339 }
340 
341 static void
342 impl_constructed (GObject *object)
343 {
344 	RBVisualizerPage *page;
345 	ClutterInitError err;
346 	GstElement *colorspace;
347 	GstElement *realsink;
348 	GstElement *capsfilter;
349 	GstCaps *caps;
350 	GstPad *pad;
351 
352 	RB_CHAIN_GOBJECT_METHOD (rb_visualizer_page_parent_class, constructed, object);
353 	page = RB_VISUALIZER_PAGE (object);
354 
355 	err = gtk_clutter_init (NULL, NULL);
356 	if (err != CLUTTER_INIT_SUCCESS) {
357 		/* maybe do something more sensible here.  not sure if there are any user-recoverable
358 		 * conditions that would cause clutter init to fail, though, so it may not be worth it.
359 		 * as it is, we just won't add the page to the page tree.
360 		 */
361 		g_warning ("Unable to display visual effects due to Clutter init failure");
362 		return;
363 	}
364 
365 	page->texture = clutter_texture_new ();
366 	clutter_texture_set_sync_size (CLUTTER_TEXTURE (page->texture), TRUE);
367 	clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (page->texture), TRUE);
368 
369 	page->sink = gst_bin_new (NULL);
370 	g_object_ref (page->sink);
371 
372 	/* actual sink */
373 	realsink = gst_element_factory_make ("cluttersink", NULL);
374 	g_object_set (realsink, "texture", page->texture, NULL);
375 
376 	colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL);
377 	/* capsfilter to force rgb format (without this we end up using ayuv) */
378 	capsfilter = gst_element_factory_make ("capsfilter", NULL);
379 	caps = gst_caps_from_string ("video/x-raw-rgb,bpp=(int)24,depth=(int)24,"
380 				     "endianness=(int)4321,red_mask=(int)16711680,"
381 				     "green_mask=(int)65280,blue_mask=(int)255");
382 	g_object_set (capsfilter, "caps", caps, NULL);
383 	gst_caps_unref (caps);
384 
385 	gst_bin_add_many (GST_BIN (page->sink), colorspace, capsfilter, realsink, NULL);
386 	gst_element_link (colorspace, capsfilter);
387 	gst_element_link (capsfilter, realsink);
388 
389 	pad = gst_element_get_static_pad (colorspace, "sink");
390 	gst_element_add_pad (page->sink, gst_ghost_pad_new ("sink", pad));
391 	gst_object_unref (pad);
392 
393 	g_signal_connect_object (page->fullscreen_action,
394 				 "toggled",
395 				 G_CALLBACK (toggle_fullscreen_cb),
396 				 page, 0);
397 }
398 
399 static void
400 rb_visualizer_page_init (RBVisualizerPage *page)
401 {
402 }
403 
404 static void
405 rb_visualizer_page_class_init (RBVisualizerPageClass *klass)
406 {
407 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
408 	RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
409 
410 	object_class->constructed = impl_constructed;
411 	object_class->get_property = impl_get_property;
412 	object_class->set_property = impl_set_property;
413 	object_class->dispose = impl_dispose;
414 
415 	page_class->selected = impl_selected;
416 	page_class->deselected = impl_deselected;
417 	page_class->show_popup = impl_show_popup;
418 
419 	g_object_class_install_property (object_class,
420 					 PROP_SINK,
421 					 g_param_spec_object ("sink",
422 							      "sink",
423 							      "gstreamer sink element",
424 							      GST_TYPE_ELEMENT,
425 							      G_PARAM_READABLE));
426 	g_object_class_install_property (object_class,
427 					 PROP_POPUP,
428 					 g_param_spec_object ("popup",
429 							      "popup",
430 							      "popup menu",
431 							      GTK_TYPE_WIDGET,
432 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
433 	g_object_class_install_property (object_class,
434 					 PROP_FULLSCREEN_ACTION,
435 					 g_param_spec_object ("fullscreen-action",
436 							      "fullscreen action",
437 							      "GtkToggleAction for fullscreen",
438 							      GTK_TYPE_TOGGLE_ACTION,
439 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
440 
441 	signals[START] = g_signal_new ("start",
442 				       RB_TYPE_VISUALIZER_PAGE,
443 				       G_SIGNAL_RUN_LAST,
444 				       0,
445 				       NULL, NULL,
446 				       g_cclosure_marshal_VOID__VOID,
447 				       G_TYPE_NONE,
448 				       0);
449 	signals[STOP] = g_signal_new ("stop",
450 				      RB_TYPE_VISUALIZER_PAGE,
451 				      G_SIGNAL_RUN_LAST,
452 				      0,
453 				      NULL, NULL,
454 				      g_cclosure_marshal_VOID__VOID,
455 				      G_TYPE_NONE,
456 				      0);
457 	signals[FULLSCREEN] = g_signal_new_class_handler ("toggle-fullscreen",
458 							  RB_TYPE_VISUALIZER_PAGE,
459 							  G_SIGNAL_RUN_LAST,
460 							  G_CALLBACK (toggle_fullscreen),
461 							  NULL, NULL,
462 							  g_cclosure_marshal_VOID__VOID,
463 							  G_TYPE_NONE,
464 							  0);
465 }
466 
467 static void
468 rb_visualizer_page_class_finalize (RBVisualizerPageClass *klass)
469 {
470 }
471 
472 void
473 _rb_visualizer_page_register_type (GTypeModule *module)
474 {
475 	rb_visualizer_page_register_type (module);
476 }