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>
24 #if defined(_ENABLE_GST)
26 #include <gst/interfaces/xoverlay.h>
27 #include <gst/video/video.h>
28 #include <gst/pbutils/missing-plugins.h>
33 #include "callbacks.h"
34 #include "hb-backend.h"
39 #define PREVIEW_STATE_IMAGE 0
40 #define PREVIEW_STATE_LIVE 1
44 #if defined(_ENABLE_GST)
51 gboolean progress_lock;
67 #if defined(_ENABLE_GST)
68 G_MODULE_EXPORT gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data);
69 static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg,
73 G_MODULE_EXPORT gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event,
74 signal_user_data_t *ud);
77 ghb_screen_par(signal_user_data_t *ud, gint *par_n, gint *par_d)
79 #if defined(_ENABLE_GST)
80 GValue disp_par = {0,};
85 g_value_init(&disp_par, GST_TYPE_FRACTION);
86 gst_value_set_fraction(&disp_par, 1, 1);
87 g_object_get(ud->preview->play, "video-sink", &xover, NULL);
91 klass = G_OBJECT_GET_CLASS(xover);
95 pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
98 GValue par_prop = {0,};
100 g_value_init(&par_prop, pspec->value_type);
101 g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
103 if (!g_value_transform(&par_prop, &disp_par))
105 g_warning("transform failed");
106 gst_value_set_fraction(&disp_par, 1, 1);
108 g_value_unset(&par_prop);
110 *par_n = gst_value_get_fraction_numerator(&disp_par);
111 *par_d = gst_value_get_fraction_denominator(&disp_par);
112 g_value_unset(&disp_par);
125 ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d)
127 gint disp_par_n, disp_par_d;
130 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
131 if (disp_par_n < 1) disp_par_n = 1;
132 if (disp_par_d < 1) disp_par_d = 1;
133 num = par_n * disp_par_d;
134 den = par_d * disp_par_n;
137 *width = *width * num / den;
139 *height = *height * den / num;
143 ghb_preview_init(signal_user_data_t *ud)
147 ud->preview = g_malloc0(sizeof(preview_t));
148 ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
149 gtk_widget_realize(ud->preview->view);
150 g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
151 G_CALLBACK(preview_expose_cb), ud);
153 ud->preview->pause = TRUE;
154 ud->preview->encode_frame = -1;
155 ud->preview->live_id = -1;
156 widget = GHB_WIDGET (ud->builder, "preview_button_image");
157 gtk_widget_get_size_request(widget, &ud->preview->button_width, &ud->preview->button_height);
159 #if defined(_ENABLE_GST)
164 ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
166 ud->preview->xid = GDK_WINDOW_HWND(ud->preview->view->window);
168 ud->preview->play = gst_element_factory_make("playbin", "play");
169 //xover = gst_element_factory_make("xvimagesink", "xover");
170 //xover = gst_element_factory_make("ximagesink", "xover");
171 xover = gst_element_factory_make("gconfvideosink", "xover");
174 GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box");
175 gtk_widget_hide (widget);
176 widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
177 gtk_widget_hide (widget);
181 g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
182 g_object_set(ud->preview->play, "subtitle-font-desc",
183 "sans bold 20", NULL);
184 //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
186 bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
187 gst_bus_add_watch(bus, live_preview_cb, ud);
188 gst_bus_set_sync_handler(bus, create_window, ud->preview);
189 gst_object_unref(bus);
191 GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box");
192 gtk_widget_hide (widget);
193 widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
194 gtk_widget_hide (widget);
199 ghb_preview_cleanup(signal_user_data_t *ud)
201 if (ud->preview->current)
203 ud->preview->current = NULL;
204 g_free(ud->preview->current);
208 #if defined(_ENABLE_GST)
209 static GstBusSyncReply
210 create_window(GstBus *bus, GstMessage *msg, gpointer data)
212 preview_t *preview = (preview_t*)data;
214 switch (GST_MESSAGE_TYPE(msg))
216 case GST_MESSAGE_ELEMENT:
218 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
221 gst_x_overlay_set_xwindow_id(
222 GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
224 gst_directdraw_sink_set_window_id(
225 GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
227 gst_message_unref(msg);
239 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
241 GValueArray *info_arr = NULL;
248 g_object_get(play, "stream-info-value-array", &info_arr, NULL);
249 if (info_arr == NULL)
252 for (ii = 0; ii < info_arr->n_values; ++ii)
257 val = g_value_array_get_nth(info_arr, ii);
258 info_obj = g_value_get_object(val);
265 g_object_get(info_obj, "type", &type, NULL);
266 pspec = g_object_class_find_property(
267 G_OBJECT_GET_CLASS (info_obj), "type");
268 value = g_enum_get_value(
269 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
272 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
273 g_ascii_strcasecmp (value->value_name, typestr) == 0)
275 ret = g_list_prepend (ret, g_object_ref (info_obj));
280 g_value_array_free (info_arr);
281 return g_list_reverse (ret);
285 caps_set(GstCaps *caps, signal_user_data_t *ud)
289 ss = gst_caps_get_structure(caps, 0);
292 gint fps_n, fps_d, width, height;
293 guint num, den, par_n, par_d;
294 gint disp_par_n, disp_par_d;
297 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
298 gst_structure_get_int(ss, "width", &width);
299 gst_structure_get_int(ss, "height", &height);
300 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
301 par_n = gst_value_get_fraction_numerator(par);
302 par_d = gst_value_get_fraction_denominator(par);
304 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
305 gst_video_calculate_display_ratio(
306 &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
309 width = gst_util_uint64_scale_int(height, num, den);
311 height = gst_util_uint64_scale_int(width, den, num);
313 if (ghb_settings_get_boolean(ud->settings, "reduce_hd_preview"))
318 ss = gdk_screen_get_default();
319 s_w = gdk_screen_get_width(ss);
320 s_h = gdk_screen_get_height(ss);
322 if (width > s_w * 80 / 100)
324 width = s_w * 80 / 100;
325 height = gst_util_uint64_scale_int(width, den, num);
327 if (height > s_h * 80 / 100)
329 height = s_h * 80 / 100;
330 width = gst_util_uint64_scale_int(height, num, den);
334 if (width != ud->preview->width || height != ud->preview->height)
336 gtk_widget_set_size_request(ud->preview->view, width, height);
337 ud->preview->width = width;
338 ud->preview->height = height;
344 update_stream_info(signal_user_data_t *ud)
346 GList *vstreams, *ll;
349 vstreams = get_stream_info_objects_for_type(ud->preview->play, "video");
352 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
354 g_object_get(ll->data, "object", &vpad, NULL);
361 caps = gst_pad_get_negotiated_caps(vpad);
365 gst_caps_unref(caps);
367 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
368 gst_object_unref(vpad);
370 g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
371 g_list_free(vstreams);
374 G_MODULE_EXPORT gboolean
375 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
377 signal_user_data_t *ud = (signal_user_data_t*)data;
379 switch (GST_MESSAGE_TYPE(msg))
381 case GST_MESSAGE_ERROR:
386 gst_message_parse_error(msg, &err, &debug);
387 g_warning("Gstreamer Error: %s", err->message);
392 case GST_MESSAGE_ELEMENT:
394 if (gst_is_missing_plugin_message(msg))
396 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
397 gchar *message, *desc;
398 desc = gst_missing_plugin_message_get_description(msg);
399 message = g_strdup_printf(
400 "Missing GStreamer plugin\n"
401 "Audio or Video may not play as expected\n\n%s",
403 ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
405 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
409 case GST_MESSAGE_STATE_CHANGED:
411 GstState state, pending;
412 gst_element_get_state(ud->preview->play, &state, &pending, 0);
413 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
415 update_stream_info(ud);
419 case GST_MESSAGE_EOS:
424 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
425 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
426 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
427 ud->preview->pause = TRUE;
428 gst_element_seek(ud->preview->play, 1.0,
429 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
430 GST_SEEK_TYPE_SET, 0,
431 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
443 live_preview_start(signal_user_data_t *ud)
448 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
449 if (!ud->preview->encoded[ud->preview->frame])
451 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
452 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
453 ud->preview->pause = TRUE;
457 uri = g_strdup_printf("file://%s", ud->preview->current);
458 gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
459 ud->preview->state = PREVIEW_STATE_LIVE;
460 g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
461 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
462 ud->preview->pause = FALSE;
467 live_preview_pause(signal_user_data_t *ud)
471 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
472 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
473 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
474 ud->preview->pause = TRUE;
479 live_preview_stop(signal_user_data_t *ud)
484 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
485 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
486 #if defined(_ENABLE_GST)
487 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
489 ud->preview->pause = TRUE;
490 ud->preview->state = PREVIEW_STATE_IMAGE;
492 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
493 gtk_range_set_value(progress, 0);
497 ghb_live_reset(signal_user_data_t *ud)
501 if (ud->preview->live_id >= 0)
503 ghb_stop_live_encode();
505 ud->preview->live_id = -1;
506 ud->preview->encode_frame = -1;
507 if (!ud->preview->pause)
508 live_preview_stop(ud);
509 if (ud->preview->current)
511 g_free(ud->preview->current);
512 ud->preview->current = NULL;
514 encoded = ud->preview->encoded[ud->preview->frame];
515 memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
517 ghb_set_preview_image(ud);
520 extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
523 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
527 gint frame = ud->preview->frame;
529 tmp_dir = ghb_get_tmp_dir();
530 name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
531 if (ud->preview->current)
532 g_free(ud->preview->current);
533 ud->preview->current = name;
535 if (ud->preview->encoded[frame] &&
536 g_file_test(name, G_FILE_TEST_IS_REGULAR))
538 #if defined(_ENABLE_GST)
539 if (ud->preview->pause)
540 live_preview_start(ud);
542 live_preview_pause(ud);
549 ud->preview->encode_frame = frame;
550 js = ghb_value_dup(ud->settings);
551 ghb_settings_set_string(js, "destination", name);
552 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
553 ud->preview->live_id = 0;
554 ghb_add_live_job(js, ud->preview->live_id);
555 ghb_start_live_encode();
561 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
566 ud->preview->live_id = -1;
567 prog = GHB_WIDGET(ud->builder, "live_encode_progress");
569 ud->preview->encode_frame == ud->preview->frame)
571 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
572 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
573 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
574 #if defined(_ENABLE_GST)
575 live_preview_start(ud);
577 widget = GHB_WIDGET(ud->builder, "live_progress_box");
578 gtk_widget_hide (widget);
579 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
580 gtk_widget_show (widget);
584 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
585 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
586 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
590 #if defined(_ENABLE_GST)
591 G_MODULE_EXPORT gboolean
592 unlock_progress_cb(signal_user_data_t *ud)
594 ud->preview->progress_lock = FALSE;
595 // This function is initiated by g_idle_add. Must return false
596 // so that it is not called again
602 ghb_live_preview_progress(signal_user_data_t *ud)
604 #if defined(_ENABLE_GST)
605 GstFormat fmt = GST_FORMAT_TIME;
606 gint64 len = -1, pos = -1;
608 if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
611 ud->preview->progress_lock = TRUE;
612 if (gst_element_query_duration(ud->preview->play, &fmt, &len))
614 if (len != -1 && fmt == GST_FORMAT_TIME)
616 ud->preview->len = len / GST_MSECOND;
619 if (gst_element_query_position(ud->preview->play, &fmt, &pos))
621 if (pos != -1 && fmt == GST_FORMAT_TIME)
623 ud->preview->pos = pos / GST_MSECOND;
626 if (ud->preview->len > 0)
631 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
632 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
633 gtk_range_set_value(progress, percent);
635 g_idle_add((GSourceFunc)unlock_progress_cb, ud);
639 #if defined(_ENABLE_GST)
640 G_MODULE_EXPORT gboolean
641 unlock_seek_cb(signal_user_data_t *ud)
643 ud->preview->seek_lock = FALSE;
644 // This function is initiated by g_idle_add. Must return false
645 // so that it is not called again
651 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
653 #if defined(_ENABLE_GST)
657 if (ud->preview->progress_lock)
660 ud->preview->seek_lock = TRUE;
661 dval = gtk_range_get_value(GTK_RANGE(widget));
662 pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
663 gst_element_seek(ud->preview->play, 1.0,
664 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
665 GST_SEEK_TYPE_SET, pos,
666 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
667 g_idle_add((GSourceFunc)unlock_seek_cb, ud);
672 ghb_set_preview_image(signal_user_data_t *ud)
675 gint preview_width, preview_height, target_height, width, height;
677 g_debug("set_preview_button_image ()");
680 live_preview_stop(ud);
682 titleindex = ghb_settings_combo_int(ud->settings, "title");
683 if (titleindex < 0) return;
684 widget = GHB_WIDGET (ud->builder, "preview_frame");
685 ud->preview->frame = ghb_widget_int(widget) - 1;
686 if (ud->preview->encoded[ud->preview->frame])
688 widget = GHB_WIDGET(ud->builder, "live_progress_box");
689 gtk_widget_hide (widget);
690 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
691 gtk_widget_show (widget);
695 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
696 gtk_widget_hide (widget);
697 widget = GHB_WIDGET(ud->builder, "live_progress_box");
698 gtk_widget_show (widget);
699 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
700 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
701 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
703 if (ud->preview->pix != NULL)
704 g_object_unref(ud->preview->pix);
707 ghb_get_preview_image(titleindex, ud->preview->frame,
708 ud, &width, &height);
709 if (ud->preview->pix == NULL) return;
710 preview_width = gdk_pixbuf_get_width(ud->preview->pix);
711 preview_height = gdk_pixbuf_get_height(ud->preview->pix);
712 widget = GHB_WIDGET (ud->builder, "preview_image");
713 if (preview_width != ud->preview->width ||
714 preview_height != ud->preview->height)
716 gtk_widget_set_size_request(widget, preview_width, preview_height);
717 ud->preview->width = preview_width;
718 ud->preview->height = preview_height;
721 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
722 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
724 gchar *text = g_strdup_printf("%d x %d", width, height);
725 widget = GHB_WIDGET (ud->builder, "preview_dims");
726 gtk_label_set_text(GTK_LABEL(widget), text);
729 g_debug("preview %d x %d", preview_width, preview_height);
730 target_height = MIN(ud->preview->button_height, 200);
731 height = target_height;
732 width = preview_width * height / preview_height;
736 height = preview_height * width / preview_width;
739 if ((height >= 16) && (width >= 16))
741 GdkPixbuf *scaled_preview;
742 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width,
743 height, GDK_INTERP_NEAREST);
744 if (scaled_preview != NULL)
746 widget = GHB_WIDGET (ud->builder, "preview_button_image");
747 gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
748 g_object_unref (scaled_preview);
753 #if defined(_ENABLE_GST)
754 G_MODULE_EXPORT gboolean
755 delayed_expose_cb(signal_user_data_t *ud)
760 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
764 if (GST_IS_BIN(vsink))
765 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
766 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
768 xover = GST_X_OVERLAY(vsink);
769 gst_x_overlay_expose(xover);
770 // This function is initiated by g_idle_add. Must return false
771 // so that it is not called again
776 G_MODULE_EXPORT gboolean
779 GdkEventExpose *event,
780 signal_user_data_t *ud)
782 #if defined(_ENABLE_GST)
783 if (ud->preview->state == PREVIEW_STATE_LIVE)
785 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
790 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
791 if (GST_IS_BIN(vsink))
792 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
793 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
795 xover = GST_X_OVERLAY(vsink);
796 gst_x_overlay_expose(xover);
797 // For some reason, the exposed region doesn't always get
798 // cleaned up here. But a delayed gst_x_overlay_expose()
800 g_idle_add((GSourceFunc)delayed_expose_cb, ud);
807 if (ud->preview->pix != NULL)
810 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
811 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
817 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
819 g_debug("allocate %d x %d", allocation->width, allocation->height);
820 if (ud->preview->button_width == allocation->width &&
821 ud->preview->button_height == allocation->height)
823 // Nothing to do. Bug out.
824 g_debug("nothing to do");
827 g_debug("prev allocate %d x %d", ud->preview->button_width,
828 ud->preview->button_height);
829 ud->preview->button_width = allocation->width;
830 ud->preview->button_height = allocation->height;
831 ghb_set_preview_image(ud);
835 set_visible(GtkWidget *widget, gboolean visible)
839 gtk_widget_show_now(widget);
843 gtk_widget_hide(widget);
848 ghb_preview_set_visible(signal_user_data_t *ud)
852 gboolean settings_active;
854 settings_active = ghb_settings_get_boolean(ud->settings, "show_picture");
855 widget = GHB_WIDGET (ud->builder, "preview_window");
856 titleindex = ghb_settings_combo_int(ud->settings, "title");
857 if (settings_active && titleindex >= 0)
860 x = ghb_settings_get_int(ud->settings, "preview_x");
861 y = ghb_settings_get_int(ud->settings, "preview_y");
862 if (x >= 0 && y >= 0)
863 gtk_window_move(GTK_WINDOW(widget), x, y);
865 ghb_settings_get_boolean(ud->settings, "show_preview"));
869 set_visible(widget, FALSE);
874 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
876 g_debug("preview_button_clicked_cb()");
877 ghb_widget_to_setting (ud->settings, xwidget);
878 ghb_preview_set_visible(ud);
879 ghb_check_dependency(ud, xwidget, NULL);
880 const gchar *name = gtk_widget_get_name(xwidget);
881 ghb_pref_save(ud->settings, name);
885 picture_settings_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
888 gboolean active, hide_settings;
891 g_debug("picture_settings_clicked_cb()");
892 ghb_widget_to_setting (ud->settings, xwidget);
894 hide_settings = ghb_settings_get_boolean(ud->settings, "hide_settings");
896 active = ghb_settings_get_boolean(ud->settings, "show_picture");
897 widget = GHB_WIDGET (ud->builder, "settings_window");
898 x = ghb_settings_get_int(ud->settings, "settings_x");
899 y = ghb_settings_get_int(ud->settings, "settings_y");
900 if (x >= 0 && y >= 0)
901 gtk_window_move(GTK_WINDOW(widget), x, y);
902 set_visible(widget, active && !hide_settings);
903 ghb_preview_set_visible(ud);
907 picture_settings_alt_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
912 g_debug("picture_settings_alt_clicked_cb()");
913 toggle = GHB_WIDGET (ud->builder, "show_picture");
914 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
915 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
919 go_full(signal_user_data_t *ud)
922 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
923 gtk_window_fullscreen(window);
924 ghb_set_preview_image(ud);
929 fullscreen_clicked_cb(GtkWidget *toggle, signal_user_data_t *ud)
934 g_debug("fullscreen_clicked_cb()");
935 ghb_widget_to_setting (ud->settings, toggle);
936 ghb_check_dependency(ud, toggle, NULL);
937 const gchar *name = gtk_widget_get_name(toggle);
938 ghb_pref_save(ud->settings, name);
940 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
941 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));
944 gtk_window_set_resizable(window, TRUE);
945 gtk_button_set_label(GTK_BUTTON(toggle), "Windowed");
946 // Changing resizable property doesn't take effect immediately
947 // need to delay fullscreen till after this callback returns
949 g_idle_add((GSourceFunc)go_full, ud);
953 gtk_window_unfullscreen(window);
954 gtk_window_set_resizable(window, FALSE);
955 gtk_button_set_label(GTK_BUTTON(toggle), "Fullscreen");
956 ghb_set_preview_image(ud);
961 picture_settings_alt2_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
967 g_debug("picture_settings_alt2_clicked_cb()");
968 ghb_widget_to_setting (ud->settings, xwidget);
969 active = ghb_settings_get_boolean(ud->settings, "hide_settings");
971 toggle = GHB_WIDGET (ud->builder, "hide_settings");
972 window = GHB_WIDGET(ud->builder, "settings_window");
975 gtk_button_set_label(GTK_BUTTON(toggle), "Hide Settings");
976 gtk_widget_set_tooltip_text(toggle,
977 "Hide the picture settings window while "
978 "leaving the preview visible.");
979 gtk_widget_show(window);
983 gtk_button_set_label(GTK_BUTTON(toggle), "Show Settings");
984 gtk_widget_set_tooltip_text(toggle, "Show picture settings.");
985 gtk_widget_hide(window);
990 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
992 if (ud->preview->live_id >= 0)
994 ghb_stop_live_encode();
995 ud->preview->live_id = -1;
996 ud->preview->encode_frame = -1;
998 ghb_set_preview_image(ud);
1001 G_MODULE_EXPORT gboolean
1002 preview_window_delete_cb(
1005 signal_user_data_t *ud)
1007 live_preview_stop(ud);
1008 widget = GHB_WIDGET (ud->builder, "show_picture");
1009 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
1013 G_MODULE_EXPORT gboolean
1014 settings_window_delete_cb(
1017 signal_user_data_t *ud)
1019 live_preview_stop(ud);
1020 widget = GHB_WIDGET (ud->builder, "show_picture");
1021 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
1026 G_MODULE_EXPORT void
1027 preview_duration_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1029 g_debug("preview_duration_changed_cb ()");
1031 ghb_widget_to_setting (ud->settings, widget);
1032 ghb_check_dependency(ud, widget, NULL);
1033 const gchar *name = gtk_widget_get_name(widget);
1034 ghb_pref_save(ud->settings, name);
1037 static guint hud_timeout_id = 0;
1040 hud_timeout(signal_user_data_t *ud)
1044 g_debug("hud_timeout()");
1045 widget = GHB_WIDGET(ud->builder, "preview_hud");
1046 gtk_widget_hide(widget);
1051 G_MODULE_EXPORT gboolean
1054 GdkEventCrossing *event,
1055 signal_user_data_t *ud)
1057 g_debug("hud_enter_cb()");
1058 if (hud_timeout_id != 0)
1063 mc = g_main_context_default();
1064 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1066 g_source_destroy(source);
1068 widget = GHB_WIDGET(ud->builder, "preview_hud");
1069 gtk_widget_show(widget);
1074 G_MODULE_EXPORT gboolean
1077 GdkEventCrossing *event,
1078 signal_user_data_t *ud)
1080 g_debug("hud_leave_cb()");
1081 if (hud_timeout_id != 0)
1086 mc = g_main_context_default();
1087 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1089 g_source_destroy(source);
1091 hud_timeout_id = g_timeout_add(300, (GSourceFunc)hud_timeout, ud);
1095 G_MODULE_EXPORT gboolean
1098 GdkEventMotion *event,
1099 signal_user_data_t *ud)
1101 //g_debug("hud_motion_cb %d", hud_timeout_id);
1102 if (hud_timeout_id != 0)
1107 mc = g_main_context_default();
1108 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1110 g_source_destroy(source);
1112 widget = GHB_WIDGET(ud->builder, "preview_hud");
1113 if (!GTK_WIDGET_VISIBLE(widget))
1115 gtk_widget_show(widget);
1117 hud_timeout_id = g_timeout_add_seconds(4, (GSourceFunc)hud_timeout, ud);
1122 ghb_curved_rect_mask(gint width, gint height, gint radius)
1128 if (!width || !height)
1131 shape = (GdkDrawable *)gdk_pixmap_new (NULL, width, height, 1);
1133 cr = gdk_cairo_create (shape);
1137 if (radius > width / 2)
1139 if (radius > height / 2)
1140 radius = height / 2;
1142 // fill shape with black
1144 cairo_rectangle (cr, 0, 0, width, height);
1145 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1149 cairo_move_to (cr, 0, radius);
1150 cairo_curve_to (cr, 0 , 0, 0 , 0, radius, 0);
1151 cairo_line_to (cr, w - radius, 0);
1152 cairo_curve_to (cr, w, 0, w, 0, w, radius);
1153 cairo_line_to (cr, w , h - radius);
1154 cairo_curve_to (cr, w, h, w, h, w - radius, h);
1155 cairo_line_to (cr, 0 + radius, h);
1156 cairo_curve_to (cr, 0, h, 0, h, 0, h - radius);
1158 cairo_close_path(cr);
1160 cairo_set_source_rgb(cr, 1, 1, 1);
1168 G_MODULE_EXPORT void
1169 preview_hud_size_alloc_cb(
1171 GtkAllocation *allocation,
1172 signal_user_data_t *ud)
1176 //g_message("preview_hud_size_alloc_cb()");
1177 if (GTK_WIDGET_VISIBLE(widget) && allocation->height > 50)
1179 shape = ghb_curved_rect_mask(allocation->width,
1180 allocation->height, allocation->height/4);
1183 gtk_widget_shape_combine_mask(widget, shape, 0, 0);
1184 gdk_pixmap_unref(shape);
1189 G_MODULE_EXPORT gboolean
1190 preview_configure_cb(
1192 GdkEventConfigure *event,
1193 signal_user_data_t *ud)
1197 //g_message("preview_configure_cb()");
1198 if (GTK_WIDGET_VISIBLE(widget))
1200 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1201 ghb_settings_set_int(ud->settings, "preview_x", x);
1202 ghb_settings_set_int(ud->settings, "preview_y", y);
1203 ghb_pref_set(ud->settings, "preview_x");
1204 ghb_pref_set(ud->settings, "preview_y");
1210 G_MODULE_EXPORT gboolean
1211 settings_configure_cb(
1213 GdkEventConfigure *event,
1214 signal_user_data_t *ud)
1218 //g_message("settings_configure_cb()");
1219 if (GTK_WIDGET_VISIBLE(widget))
1221 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1222 ghb_settings_set_int(ud->settings, "settings_x", x);
1223 ghb_settings_set_int(ud->settings, "settings_y", y);
1224 ghb_pref_set(ud->settings, "settings_x");
1225 ghb_pref_set(ud->settings, "settings_y");