1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
6 * preview.c is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
16 #include <glib/gstdio.h>
17 #include <glib-object.h>
21 #include <gst/interfaces/xoverlay.h>
22 #include <gst/video/video.h>
23 #include <gst/pbutils/missing-plugins.h>
26 #include "callbacks.h"
27 #include "hb-backend.h"
32 #define PREVIEW_STATE_IMAGE 0
33 #define PREVIEW_STATE_LIVE 1
41 gboolean progress_lock;
58 static gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data);
59 static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg,
61 gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event,
62 signal_user_data_t *ud);
65 ghb_screen_par(signal_user_data_t *ud, gint *par_n, gint *par_d)
67 GValue disp_par = {0,};
72 g_value_init(&disp_par, GST_TYPE_FRACTION);
73 gst_value_set_fraction(&disp_par, 1, 1);
74 g_object_get(ud->preview->play, "video-sink", &xover, NULL);
75 klass = G_OBJECT_GET_CLASS(xover);
76 pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
79 GValue par_prop = {0,};
81 g_value_init(&par_prop, pspec->value_type);
82 g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
84 if (!g_value_transform(&par_prop, &disp_par))
86 g_warning("transform failed");
87 gst_value_set_fraction(&disp_par, 1, 1);
89 g_value_unset(&par_prop);
91 *par_n = gst_value_get_fraction_numerator(&disp_par);
92 *par_d = gst_value_get_fraction_denominator(&disp_par);
93 g_value_unset(&disp_par);
97 ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d)
99 gint disp_par_n, disp_par_d;
102 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
103 if (disp_par_n < 1) disp_par_n = 1;
104 if (disp_par_d < 1) disp_par_d = 1;
105 num = par_n * disp_par_d;
106 den = par_d * disp_par_n;
109 *width = *width * num / den;
111 *height = *height * den / num;
115 ghb_preview_init(signal_user_data_t *ud)
120 ud->preview = g_malloc0(sizeof(preview_t));
121 ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
122 gtk_widget_realize(ud->preview->view);
123 g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
124 G_CALLBACK(preview_expose_cb), ud);
125 ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
127 ud->preview->play = gst_element_factory_make("playbin", "play");
128 ud->preview->pause = TRUE;
129 ud->preview->encode_frame = -1;
130 ud->preview->live_id = -1;
131 //xover = gst_element_factory_make("xvimagesink", "xover");
132 //xover = gst_element_factory_make("ximagesink", "xover");
133 xover = gst_element_factory_make("gconfvideosink", "xover");
134 g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
135 //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
137 bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
138 gst_bus_add_watch(bus, live_preview_cb, ud);
139 gst_bus_set_sync_handler(bus, create_window, ud->preview);
140 gst_object_unref(bus);
144 ghb_preview_cleanup(signal_user_data_t *ud)
146 if (ud->preview->current)
148 ud->preview->current = NULL;
149 g_free(ud->preview->current);
153 static GstBusSyncReply
154 create_window(GstBus *bus, GstMessage *msg, gpointer data)
156 preview_t *preview = (preview_t*)data;
158 switch (GST_MESSAGE_TYPE(msg))
160 case GST_MESSAGE_ELEMENT:
162 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
164 gst_x_overlay_set_xwindow_id(
165 GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
166 gst_message_unref(msg);
178 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
180 GValueArray *info_arr = NULL;
187 g_object_get(play, "stream-info-value-array", &info_arr, NULL);
188 if (info_arr == NULL)
191 for (ii = 0; ii < info_arr->n_values; ++ii)
196 val = g_value_array_get_nth(info_arr, ii);
197 info_obj = g_value_get_object(val);
204 g_object_get(info_obj, "type", &type, NULL);
205 pspec = g_object_class_find_property(
206 G_OBJECT_GET_CLASS (info_obj), "type");
207 value = g_enum_get_value(
208 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
211 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
212 g_ascii_strcasecmp (value->value_name, typestr) == 0)
214 ret = g_list_prepend (ret, g_object_ref (info_obj));
219 g_value_array_free (info_arr);
220 return g_list_reverse (ret);
224 caps_set(GstCaps *caps, signal_user_data_t *ud)
228 ss = gst_caps_get_structure(caps, 0);
231 gint fps_n, fps_d, width, height;
232 guint num, den, par_n, par_d;
233 gint disp_par_n, disp_par_d;
236 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
237 gst_structure_get_int(ss, "width", &width);
238 gst_structure_get_int(ss, "height", &height);
239 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
240 par_n = gst_value_get_fraction_numerator(par);
241 par_d = gst_value_get_fraction_denominator(par);
243 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
244 gst_video_calculate_display_ratio(
245 &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
248 width = gst_util_uint64_scale_int(height, num, den);
250 height = gst_util_uint64_scale_int(width, den, num);
252 if (ghb_settings_get_boolean(ud->settings, "reduce_hd_preview"))
257 ss = gdk_screen_get_default();
258 s_w = gdk_screen_get_width(ss);
259 s_h = gdk_screen_get_height(ss);
261 if (width > s_w * 80 / 100)
263 width = s_w * 80 / 100;
264 height = gst_util_uint64_scale_int(width, den, num);
266 if (height > s_h * 80 / 100)
268 height = s_h * 80 / 100;
269 width = gst_util_uint64_scale_int(height, num, den);
273 if (width != ud->preview->width || height != ud->preview->height)
275 gtk_widget_set_size_request(ud->preview->view, width, height);
276 ud->preview->width = width;
277 ud->preview->height = height;
283 update_stream_info(signal_user_data_t *ud)
285 GList *vstreams, *ll;
288 vstreams = get_stream_info_objects_for_type(ud->preview->play, "video");
291 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
293 g_object_get(ll->data, "object", &vpad, NULL);
300 caps = gst_pad_get_negotiated_caps(vpad);
304 gst_caps_unref(caps);
306 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
307 gst_object_unref(vpad);
309 g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
310 g_list_free(vstreams);
314 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
316 signal_user_data_t *ud = (signal_user_data_t*)data;
318 switch (GST_MESSAGE_TYPE(msg))
320 case GST_MESSAGE_ERROR:
325 gst_message_parse_error(msg, &err, &debug);
326 g_warning("Gstreamer Error: %s", err->message);
331 case GST_MESSAGE_ELEMENT:
333 if (gst_is_missing_plugin_message(msg))
335 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
336 gchar *message, *desc;
337 desc = gst_missing_plugin_message_get_description(msg);
338 message = g_strdup_printf(
339 "Missing GStreamer plugin\n"
340 "Audio or Video may not play as expected\n\n%s",
342 ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
344 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
348 case GST_MESSAGE_STATE_CHANGED:
350 GstState state, pending;
351 gst_element_get_state(ud->preview->play, &state, &pending, 0);
352 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
354 update_stream_info(ud);
358 case GST_MESSAGE_EOS:
363 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
364 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
365 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
366 ud->preview->pause = TRUE;
367 gst_element_seek(ud->preview->play, 1.0,
368 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
369 GST_SEEK_TYPE_SET, 0,
370 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
382 live_preview_start(signal_user_data_t *ud)
387 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
388 if (!ud->preview->encoded[ud->preview->frame])
390 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
391 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
392 ud->preview->pause = TRUE;
396 uri = g_strdup_printf("file://%s", ud->preview->current);
397 gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
398 ud->preview->state = PREVIEW_STATE_LIVE;
399 g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
400 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
401 ud->preview->pause = FALSE;
406 live_preview_pause(signal_user_data_t *ud)
410 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
411 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
412 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
413 ud->preview->pause = TRUE;
417 live_preview_stop(signal_user_data_t *ud)
422 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
423 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
424 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
425 ud->preview->pause = TRUE;
426 ud->preview->state = PREVIEW_STATE_IMAGE;
428 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
429 gtk_range_set_value(progress, 0);
433 ghb_live_reset(signal_user_data_t *ud)
437 if (ud->preview->live_id >= 0)
439 ghb_stop_live_encode();
441 ud->preview->live_id = -1;
442 ud->preview->encode_frame = -1;
443 if (!ud->preview->pause)
444 live_preview_stop(ud);
445 if (ud->preview->current)
447 g_free(ud->preview->current);
448 ud->preview->current = NULL;
450 encoded = ud->preview->encoded[ud->preview->frame];
451 memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
453 ghb_set_preview_image(ud);
456 extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
459 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
463 gint frame = ud->preview->frame;
465 tmp_dir = ghb_get_tmp_dir();
466 name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
467 if (ud->preview->current)
468 g_free(ud->preview->current);
469 ud->preview->current = name;
471 if (ud->preview->encoded[frame] &&
472 g_file_test(name, G_FILE_TEST_IS_REGULAR))
474 if (ud->preview->pause)
475 live_preview_start(ud);
477 live_preview_pause(ud);
483 ud->preview->encode_frame = frame;
484 js = ghb_value_dup(ud->settings);
485 ghb_settings_set_string(js, "destination", name);
486 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
487 ud->preview->live_id = 0;
488 ghb_add_live_job(js, ud->preview->live_id);
489 ghb_start_live_encode();
495 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
500 ud->preview->live_id = -1;
501 prog = GHB_WIDGET(ud->builder, "live_encode_progress");
503 ud->preview->encode_frame == ud->preview->frame)
505 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
506 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
507 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
508 live_preview_start(ud);
509 widget = GHB_WIDGET(ud->builder, "live_progress_box");
510 gtk_widget_hide (widget);
511 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
512 gtk_widget_show (widget);
516 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
517 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
518 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
523 unlock_progress_cb(signal_user_data_t *ud)
525 ud->preview->progress_lock = FALSE;
526 // This function is initiated by g_idle_add. Must return false
527 // so that it is not called again
532 ghb_live_preview_progress(signal_user_data_t *ud)
534 GstFormat fmt = GST_FORMAT_TIME;
535 gint64 len = -1, pos = -1;
537 if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
540 ud->preview->progress_lock = TRUE;
541 if (gst_element_query_duration(ud->preview->play, &fmt, &len))
543 if (len != -1 && fmt == GST_FORMAT_TIME)
545 ud->preview->len = len / GST_MSECOND;
548 if (gst_element_query_position(ud->preview->play, &fmt, &pos))
550 if (pos != -1 && fmt == GST_FORMAT_TIME)
552 ud->preview->pos = pos / GST_MSECOND;
555 if (ud->preview->len > 0)
560 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
561 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
562 gtk_range_set_value(progress, percent);
564 g_idle_add((GSourceFunc)unlock_progress_cb, ud);
568 unlock_seek_cb(signal_user_data_t *ud)
570 ud->preview->seek_lock = FALSE;
571 // This function is initiated by g_idle_add. Must return false
572 // so that it is not called again
577 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
582 if (ud->preview->progress_lock)
585 ud->preview->seek_lock = TRUE;
586 dval = gtk_range_get_value(GTK_RANGE(widget));
587 pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
588 gst_element_seek(ud->preview->play, 1.0,
589 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
590 GST_SEEK_TYPE_SET, pos,
591 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
592 g_idle_add((GSourceFunc)unlock_seek_cb, ud);
596 ghb_set_preview_image(signal_user_data_t *ud)
599 gint preview_width, preview_height, target_height, width, height;
601 g_debug("set_preview_button_image ()");
604 live_preview_stop(ud);
606 titleindex = ghb_settings_combo_int(ud->settings, "title");
607 if (titleindex < 0) return;
608 widget = GHB_WIDGET (ud->builder, "preview_frame");
609 ud->preview->frame = ghb_widget_int(widget) - 1;
610 if (ud->preview->encoded[ud->preview->frame])
612 widget = GHB_WIDGET(ud->builder, "live_progress_box");
613 gtk_widget_hide (widget);
614 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
615 gtk_widget_show (widget);
619 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
620 gtk_widget_hide (widget);
621 widget = GHB_WIDGET(ud->builder, "live_progress_box");
622 gtk_widget_show (widget);
623 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
624 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
625 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
627 if (ud->preview->pix != NULL)
628 g_object_unref(ud->preview->pix);
631 ghb_get_preview_image(titleindex, ud->preview->frame,
632 ud, TRUE, &width, &height);
633 if (ud->preview->pix == NULL) return;
634 preview_width = gdk_pixbuf_get_width(ud->preview->pix);
635 preview_height = gdk_pixbuf_get_height(ud->preview->pix);
636 widget = GHB_WIDGET (ud->builder, "preview_image");
637 if (preview_width != ud->preview->width ||
638 preview_height != ud->preview->height)
640 gtk_widget_set_size_request(widget, preview_width, preview_height);
641 ud->preview->width = preview_width;
642 ud->preview->height = preview_height;
645 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
646 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
648 gchar *text = g_strdup_printf("%d x %d", width, height);
649 widget = GHB_WIDGET (ud->builder, "preview_dims");
650 gtk_label_set_text(GTK_LABEL(widget), text);
653 g_debug("preview %d x %d", preview_width, preview_height);
654 target_height = MIN(ud->preview->button_height, 128);
655 height = target_height;
656 width = preview_width * height / preview_height;
658 if ((height >= 16) && (width >= 16))
660 GdkPixbuf *scaled_preview;
661 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width,
662 height, GDK_INTERP_NEAREST);
663 if (scaled_preview != NULL)
665 widget = GHB_WIDGET (ud->builder, "preview_button_image");
666 gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
667 g_object_unref (scaled_preview);
673 delayed_expose_cb(signal_user_data_t *ud)
678 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
679 if (GST_IS_BIN(vsink))
680 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
681 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
683 xover = GST_X_OVERLAY(vsink);
684 gst_x_overlay_expose(xover);
685 // This function is initiated by g_idle_add. Must return false
686 // so that it is not called again
693 GdkEventExpose *event,
694 signal_user_data_t *ud)
696 if (ud->preview->state == PREVIEW_STATE_LIVE)
698 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
703 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
704 if (GST_IS_BIN(vsink))
705 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
706 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
708 xover = GST_X_OVERLAY(vsink);
709 gst_x_overlay_expose(xover);
710 // For some reason, the exposed region doesn't always get
711 // cleaned up here. But a delayed gst_x_overlay_expose()
713 g_idle_add((GSourceFunc)delayed_expose_cb, ud);
719 if (ud->preview->pix != NULL)
722 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
723 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
729 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
731 g_debug("allocate %d x %d", allocation->width, allocation->height);
732 if (ud->preview->button_width == allocation->width &&
733 ud->preview->button_height == allocation->height)
735 // Nothing to do. Bug out.
736 g_debug("nothing to do");
739 g_debug("prev allocate %d x %d", ud->preview->button_width,
740 ud->preview->button_height);
741 ud->preview->button_width = allocation->width;
742 ud->preview->button_height = allocation->height;
743 ghb_set_preview_image(ud);
747 set_visible(GtkWidget *widget, gboolean visible)
751 gtk_widget_show_now(widget);
755 gtk_widget_hide(widget);
760 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
764 g_debug("preview_button_clicked_cb()");
765 titleindex = ghb_settings_combo_int(ud->settings, "title");
769 GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
770 x = ghb_settings_get_int(ud->settings, "preview_x");
771 y = ghb_settings_get_int(ud->settings, "preview_y");
772 if (x >= 0 && y >= 0)
773 gtk_window_move(GTK_WINDOW(widget), x, y);
774 set_visible(widget, gtk_toggle_button_get_active(
775 GTK_TOGGLE_BUTTON(xwidget)));
777 ghb_widget_to_setting (ud->settings, xwidget);
778 ghb_check_dependency(ud, xwidget);
779 const gchar *name = gtk_widget_get_name(xwidget);
780 ghb_pref_save(ud->settings, name);
784 picture_settings_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
790 g_debug("picture_settings_clicked_cb()");
791 widget = GHB_WIDGET (ud->builder, "settings_window");
792 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(xwidget));
793 x = ghb_settings_get_int(ud->settings, "settings_x");
794 y = ghb_settings_get_int(ud->settings, "settings_y");
795 if (x >= 0 && y >= 0)
796 gtk_window_move(GTK_WINDOW(widget), x, y);
797 set_visible(widget, active);
798 if (ghb_settings_get_boolean(ud->settings, "show_preview"))
800 widget = GHB_WIDGET (ud->builder, "preview_window");
801 x = ghb_settings_get_int(ud->settings, "preview_x");
802 y = ghb_settings_get_int(ud->settings, "preview_y");
803 if (x >= 0 && y >= 0)
804 gtk_window_move(GTK_WINDOW(widget), x, y);
805 set_visible(widget, active);
806 // The window may be hidden behind the main window, raise it
808 gtk_window_present(GTK_WINDOW(widget));
813 picture_settings_alt_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
818 g_debug("picture_settings_alt_clicked_cb()");
819 toggle = GHB_WIDGET (ud->builder, "show_picture");
820 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
821 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
825 go_full(signal_user_data_t *ud)
828 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
829 gtk_window_fullscreen(window);
830 ghb_set_preview_image(ud);
835 fullscreen_clicked_cb(GtkWidget *toggle, signal_user_data_t *ud)
840 g_debug("fullscreen_clicked_cb()");
841 ghb_widget_to_setting (ud->settings, toggle);
842 ghb_check_dependency(ud, toggle);
843 const gchar *name = gtk_widget_get_name(toggle);
844 ghb_pref_save(ud->settings, name);
846 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
847 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));
850 gtk_window_set_resizable(window, TRUE);
851 gtk_button_set_label(GTK_BUTTON(toggle), "Windowed");
852 // Changing resizable property doesn't take effect immediately
853 // need to delay fullscreen till after this callback returns
855 g_idle_add((GSourceFunc)go_full, ud);
859 gtk_window_unfullscreen(window);
860 gtk_window_set_resizable(window, FALSE);
861 gtk_button_set_label(GTK_BUTTON(toggle), "Fullscreen");
862 ghb_set_preview_image(ud);
867 picture_settings_alt2_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
874 g_debug("picture_settings_alt2_clicked_cb()");
875 toggle = GHB_WIDGET (ud->builder, "show_picture");
876 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
879 // I don't want deleting the settings window to also remove the
880 // preview window, but changing the toggle will do this, so temporarily
881 // ignore the toggled signal
882 signal_id = g_signal_lookup("toggled", GTK_TYPE_TOGGLE_TOOL_BUTTON);
885 // Valid signal id found. This should always succeed.
886 handler_id = g_signal_handler_find((gpointer)toggle,
888 signal_id, 0, 0, 0, 0);
891 // This should also always succeed
892 g_signal_handler_block ((gpointer)toggle, handler_id);
897 GtkWidget *widget = GHB_WIDGET (ud->builder, "settings_window");
900 x = ghb_settings_get_int(ud->settings, "settings_x");
901 y = ghb_settings_get_int(ud->settings, "settings_y");
902 if (x >= 0 && y >= 0)
903 gtk_window_move(GTK_WINDOW(widget), x, y);
904 set_visible(widget, !active);
905 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
909 g_signal_handler_unblock ((gpointer)toggle, handler_id);
914 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
916 if (ud->preview->live_id >= 0)
918 ghb_stop_live_encode();
919 ud->preview->live_id = -1;
920 ud->preview->encode_frame = -1;
922 ghb_set_preview_image(ud);
926 preview_window_delete_cb(
929 signal_user_data_t *ud)
931 live_preview_stop(ud);
932 gtk_widget_hide(widget);
937 settings_window_delete_cb(
940 signal_user_data_t *ud)
945 gtk_widget_hide(widget);
946 widget = GHB_WIDGET (ud->builder, "show_picture");
948 // I don't want deleting the settings window to also remove the
949 // preview window, but changing the toggle will do this, so temporarily
950 // ignore the toggled signal
951 signal_id = g_signal_lookup("toggled", GTK_TYPE_TOGGLE_TOOL_BUTTON);
954 // Valid signal id found. This should always succeed.
955 handler_id = g_signal_handler_find((gpointer)widget, G_SIGNAL_MATCH_ID,
956 signal_id, 0, 0, 0, 0);
959 // This should also always succeed
960 g_signal_handler_block ((gpointer)widget, handler_id);
964 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
968 g_signal_handler_unblock ((gpointer)widget, handler_id);
974 preview_duration_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
976 g_debug("preview_duration_changed_cb ()");
978 ghb_widget_to_setting (ud->settings, widget);
979 ghb_check_dependency(ud, widget);
980 const gchar *name = gtk_widget_get_name(widget);
981 ghb_pref_save(ud->settings, name);
984 static guint hud_timeout_id = 0;
987 hud_timeout(signal_user_data_t *ud)
991 widget = GHB_WIDGET(ud->builder, "preview_hud");
992 gtk_widget_hide(widget);
997 G_MODULE_EXPORT gboolean
1000 GdkEventCrossing *event,
1001 signal_user_data_t *ud)
1003 if (hud_timeout_id != 0)
1008 mc = g_main_context_default();
1009 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1011 g_source_destroy(source);
1013 widget = GHB_WIDGET(ud->builder, "preview_hud");
1014 gtk_widget_show(widget);
1019 G_MODULE_EXPORT gboolean
1022 GdkEventCrossing *event,
1023 signal_user_data_t *ud)
1025 if (hud_timeout_id != 0)
1030 mc = g_main_context_default();
1031 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1033 g_source_destroy(source);
1035 hud_timeout_id = g_timeout_add(300, (GSourceFunc)hud_timeout, ud);
1039 G_MODULE_EXPORT gboolean
1042 GdkEventMotion *event,
1043 signal_user_data_t *ud)
1045 if (hud_timeout_id != 0)
1050 mc = g_main_context_default();
1051 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1053 g_source_destroy(source);
1059 widget = GHB_WIDGET(ud->builder, "preview_hud");
1060 gtk_widget_show(widget);
1062 hud_timeout_id = g_timeout_add_seconds(10, (GSourceFunc)hud_timeout, ud);
1066 G_MODULE_EXPORT gboolean
1067 preview_image_configure_cb(
1069 GdkEventConfigure *event,
1070 signal_user_data_t *ud)
1072 static gint w = 0, h = 0;
1074 g_debug("preview_image_configure_cb()");
1075 if ((w != event->width) || (h != event->height))
1084 G_MODULE_EXPORT gboolean
1085 preview_configure_cb(
1087 GdkEventConfigure *event,
1088 signal_user_data_t *ud)
1092 g_debug("preview_configure_cb()");
1093 if (GTK_WIDGET_VISIBLE(widget))
1095 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1096 ghb_settings_set_int(ud->settings, "preview_x", x);
1097 ghb_settings_set_int(ud->settings, "preview_y", y);
1098 ghb_pref_set(ud->settings, "preview_x");
1099 ghb_pref_set(ud->settings, "preview_y");
1105 G_MODULE_EXPORT gboolean
1106 settings_configure_cb(
1108 GdkEventConfigure *event,
1109 signal_user_data_t *ud)
1113 g_debug("settings_configure_cb()");
1114 if (GTK_WIDGET_VISIBLE(widget))
1116 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1117 ghb_settings_set_int(ud->settings, "settings_x", x);
1118 ghb_settings_set_int(ud->settings, "settings_y", y);
1119 ghb_pref_set(ud->settings, "settings_x");
1120 ghb_pref_set(ud->settings, "settings_y");
1126 G_MODULE_EXPORT gboolean
1127 preview_window_expose_cb(
1129 GdkEventExpose *event,
1130 signal_user_data_t *ud)
1136 //g_debug("preview_window_expose_cb()");
1137 /* get our child (in this case, the draw area) */
1138 child = GHB_WIDGET(ud->builder, "preview_image");
1139 /* create a cairo context to draw to the window */
1140 cr = gdk_cairo_create (widget->window);
1141 /* the source data is the (composited) event box */
1142 gdk_cairo_set_source_pixmap (cr, child->window,
1143 child->allocation.x,
1144 child->allocation.y);
1145 /* draw no more than our expose event intersects our child */
1146 region = gdk_region_rectangle (&child->allocation);
1147 gdk_region_intersect (region, event->region);
1148 gdk_cairo_region (cr, region);
1150 /* composite, with a 100% opacity */
1151 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1152 cairo_paint_with_alpha (cr, 1);
1154 cairo_reset_clip(cr);
1155 /* get our child (in this case, the event box) */
1156 child = GHB_WIDGET(ud->builder, "preview_event_box");
1157 /* create a cairo context to draw to the window */
1158 cr = gdk_cairo_create (widget->window);
1159 /* the source data is the (composited) event box */
1160 gdk_cairo_set_source_pixmap (cr, child->window,
1161 child->allocation.x,
1162 child->allocation.y);
1163 /* draw no more than our expose event intersects our child */
1164 region = gdk_region_rectangle (&child->allocation);
1165 gdk_region_intersect (region, event->region);
1166 gdk_cairo_region (cr, region);
1168 /* composite, with a 85% opacity */
1169 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1170 cairo_paint_with_alpha (cr, .85);