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);
88 klass = G_OBJECT_GET_CLASS(xover);
89 pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
92 GValue par_prop = {0,};
94 g_value_init(&par_prop, pspec->value_type);
95 g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
97 if (!g_value_transform(&par_prop, &disp_par))
99 g_warning("transform failed");
100 gst_value_set_fraction(&disp_par, 1, 1);
102 g_value_unset(&par_prop);
104 *par_n = gst_value_get_fraction_numerator(&disp_par);
105 *par_d = gst_value_get_fraction_denominator(&disp_par);
106 g_value_unset(&disp_par);
114 ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d)
116 gint disp_par_n, disp_par_d;
119 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
120 if (disp_par_n < 1) disp_par_n = 1;
121 if (disp_par_d < 1) disp_par_d = 1;
122 num = par_n * disp_par_d;
123 den = par_d * disp_par_n;
126 *width = *width * num / den;
128 *height = *height * den / num;
132 ghb_preview_init(signal_user_data_t *ud)
134 ud->preview = g_malloc0(sizeof(preview_t));
135 ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
136 gtk_widget_realize(ud->preview->view);
137 g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
138 G_CALLBACK(preview_expose_cb), ud);
140 ud->preview->pause = TRUE;
141 ud->preview->encode_frame = -1;
142 ud->preview->live_id = -1;
144 #if defined(_ENABLE_GST)
148 ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
149 ud->preview->play = gst_element_factory_make("playbin", "play");
150 //xover = gst_element_factory_make("xvimagesink", "xover");
151 //xover = gst_element_factory_make("ximagesink", "xover");
152 xover = gst_element_factory_make("gconfvideosink", "xover");
153 g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
154 //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
156 bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
157 gst_bus_add_watch(bus, live_preview_cb, ud);
158 gst_bus_set_sync_handler(bus, create_window, ud->preview);
159 gst_object_unref(bus);
161 GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box");
162 gtk_widget_hide (widget);
163 widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
164 gtk_widget_hide (widget);
169 ghb_preview_cleanup(signal_user_data_t *ud)
171 if (ud->preview->current)
173 ud->preview->current = NULL;
174 g_free(ud->preview->current);
178 #if defined(_ENABLE_GST)
179 static GstBusSyncReply
180 create_window(GstBus *bus, GstMessage *msg, gpointer data)
182 preview_t *preview = (preview_t*)data;
184 switch (GST_MESSAGE_TYPE(msg))
186 case GST_MESSAGE_ELEMENT:
188 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
190 gst_x_overlay_set_xwindow_id(
191 GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
192 gst_message_unref(msg);
204 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
206 GValueArray *info_arr = NULL;
213 g_object_get(play, "stream-info-value-array", &info_arr, NULL);
214 if (info_arr == NULL)
217 for (ii = 0; ii < info_arr->n_values; ++ii)
222 val = g_value_array_get_nth(info_arr, ii);
223 info_obj = g_value_get_object(val);
230 g_object_get(info_obj, "type", &type, NULL);
231 pspec = g_object_class_find_property(
232 G_OBJECT_GET_CLASS (info_obj), "type");
233 value = g_enum_get_value(
234 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
237 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
238 g_ascii_strcasecmp (value->value_name, typestr) == 0)
240 ret = g_list_prepend (ret, g_object_ref (info_obj));
245 g_value_array_free (info_arr);
246 return g_list_reverse (ret);
250 caps_set(GstCaps *caps, signal_user_data_t *ud)
254 ss = gst_caps_get_structure(caps, 0);
257 gint fps_n, fps_d, width, height;
258 guint num, den, par_n, par_d;
259 gint disp_par_n, disp_par_d;
262 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
263 gst_structure_get_int(ss, "width", &width);
264 gst_structure_get_int(ss, "height", &height);
265 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
266 par_n = gst_value_get_fraction_numerator(par);
267 par_d = gst_value_get_fraction_denominator(par);
269 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
270 gst_video_calculate_display_ratio(
271 &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
274 width = gst_util_uint64_scale_int(height, num, den);
276 height = gst_util_uint64_scale_int(width, den, num);
278 if (ghb_settings_get_boolean(ud->settings, "reduce_hd_preview"))
283 ss = gdk_screen_get_default();
284 s_w = gdk_screen_get_width(ss);
285 s_h = gdk_screen_get_height(ss);
287 if (width > s_w * 80 / 100)
289 width = s_w * 80 / 100;
290 height = gst_util_uint64_scale_int(width, den, num);
292 if (height > s_h * 80 / 100)
294 height = s_h * 80 / 100;
295 width = gst_util_uint64_scale_int(height, num, den);
299 if (width != ud->preview->width || height != ud->preview->height)
301 gtk_widget_set_size_request(ud->preview->view, width, height);
302 ud->preview->width = width;
303 ud->preview->height = height;
309 update_stream_info(signal_user_data_t *ud)
311 GList *vstreams, *ll;
314 vstreams = get_stream_info_objects_for_type(ud->preview->play, "video");
317 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
319 g_object_get(ll->data, "object", &vpad, NULL);
326 caps = gst_pad_get_negotiated_caps(vpad);
330 gst_caps_unref(caps);
332 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
333 gst_object_unref(vpad);
335 g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
336 g_list_free(vstreams);
339 G_MODULE_EXPORT gboolean
340 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
342 signal_user_data_t *ud = (signal_user_data_t*)data;
344 switch (GST_MESSAGE_TYPE(msg))
346 case GST_MESSAGE_ERROR:
351 gst_message_parse_error(msg, &err, &debug);
352 g_warning("Gstreamer Error: %s", err->message);
357 case GST_MESSAGE_ELEMENT:
359 if (gst_is_missing_plugin_message(msg))
361 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
362 gchar *message, *desc;
363 desc = gst_missing_plugin_message_get_description(msg);
364 message = g_strdup_printf(
365 "Missing GStreamer plugin\n"
366 "Audio or Video may not play as expected\n\n%s",
368 ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
370 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
374 case GST_MESSAGE_STATE_CHANGED:
376 GstState state, pending;
377 gst_element_get_state(ud->preview->play, &state, &pending, 0);
378 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
380 update_stream_info(ud);
384 case GST_MESSAGE_EOS:
389 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
390 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
391 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
392 ud->preview->pause = TRUE;
393 gst_element_seek(ud->preview->play, 1.0,
394 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
395 GST_SEEK_TYPE_SET, 0,
396 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
408 live_preview_start(signal_user_data_t *ud)
413 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
414 if (!ud->preview->encoded[ud->preview->frame])
416 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
417 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
418 ud->preview->pause = TRUE;
422 uri = g_strdup_printf("file://%s", ud->preview->current);
423 gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
424 ud->preview->state = PREVIEW_STATE_LIVE;
425 g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
426 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
427 ud->preview->pause = FALSE;
432 live_preview_pause(signal_user_data_t *ud)
436 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
437 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
438 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
439 ud->preview->pause = TRUE;
444 live_preview_stop(signal_user_data_t *ud)
449 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
450 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
451 #if defined(_ENABLE_GST)
452 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
454 ud->preview->pause = TRUE;
455 ud->preview->state = PREVIEW_STATE_IMAGE;
457 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
458 gtk_range_set_value(progress, 0);
462 ghb_live_reset(signal_user_data_t *ud)
466 if (ud->preview->live_id >= 0)
468 ghb_stop_live_encode();
470 ud->preview->live_id = -1;
471 ud->preview->encode_frame = -1;
472 if (!ud->preview->pause)
473 live_preview_stop(ud);
474 if (ud->preview->current)
476 g_free(ud->preview->current);
477 ud->preview->current = NULL;
479 encoded = ud->preview->encoded[ud->preview->frame];
480 memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
482 ghb_set_preview_image(ud);
485 extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
488 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
492 gint frame = ud->preview->frame;
494 tmp_dir = ghb_get_tmp_dir();
495 name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
496 if (ud->preview->current)
497 g_free(ud->preview->current);
498 ud->preview->current = name;
500 if (ud->preview->encoded[frame] &&
501 g_file_test(name, G_FILE_TEST_IS_REGULAR))
503 #if defined(_ENABLE_GST)
504 if (ud->preview->pause)
505 live_preview_start(ud);
507 live_preview_pause(ud);
514 ud->preview->encode_frame = frame;
515 js = ghb_value_dup(ud->settings);
516 ghb_settings_set_string(js, "destination", name);
517 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
518 ud->preview->live_id = 0;
519 ghb_add_live_job(js, ud->preview->live_id);
520 ghb_start_live_encode();
526 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
531 ud->preview->live_id = -1;
532 prog = GHB_WIDGET(ud->builder, "live_encode_progress");
534 ud->preview->encode_frame == ud->preview->frame)
536 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
537 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
538 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
539 #if defined(_ENABLE_GST)
540 live_preview_start(ud);
542 widget = GHB_WIDGET(ud->builder, "live_progress_box");
543 gtk_widget_hide (widget);
544 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
545 gtk_widget_show (widget);
549 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
550 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
551 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
555 #if defined(_ENABLE_GST)
556 G_MODULE_EXPORT gboolean
557 unlock_progress_cb(signal_user_data_t *ud)
559 ud->preview->progress_lock = FALSE;
560 // This function is initiated by g_idle_add. Must return false
561 // so that it is not called again
567 ghb_live_preview_progress(signal_user_data_t *ud)
569 #if defined(_ENABLE_GST)
570 GstFormat fmt = GST_FORMAT_TIME;
571 gint64 len = -1, pos = -1;
573 if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
576 ud->preview->progress_lock = TRUE;
577 if (gst_element_query_duration(ud->preview->play, &fmt, &len))
579 if (len != -1 && fmt == GST_FORMAT_TIME)
581 ud->preview->len = len / GST_MSECOND;
584 if (gst_element_query_position(ud->preview->play, &fmt, &pos))
586 if (pos != -1 && fmt == GST_FORMAT_TIME)
588 ud->preview->pos = pos / GST_MSECOND;
591 if (ud->preview->len > 0)
596 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
597 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
598 gtk_range_set_value(progress, percent);
600 g_idle_add((GSourceFunc)unlock_progress_cb, ud);
604 #if defined(_ENABLE_GST)
605 G_MODULE_EXPORT gboolean
606 unlock_seek_cb(signal_user_data_t *ud)
608 ud->preview->seek_lock = FALSE;
609 // This function is initiated by g_idle_add. Must return false
610 // so that it is not called again
616 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
618 #if defined(_ENABLE_GST)
622 if (ud->preview->progress_lock)
625 ud->preview->seek_lock = TRUE;
626 dval = gtk_range_get_value(GTK_RANGE(widget));
627 pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
628 gst_element_seek(ud->preview->play, 1.0,
629 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
630 GST_SEEK_TYPE_SET, pos,
631 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
632 g_idle_add((GSourceFunc)unlock_seek_cb, ud);
637 ghb_set_preview_image(signal_user_data_t *ud)
640 gint preview_width, preview_height, target_height, width, height;
642 g_debug("set_preview_button_image ()");
645 live_preview_stop(ud);
647 titleindex = ghb_settings_combo_int(ud->settings, "title");
648 if (titleindex < 0) return;
649 widget = GHB_WIDGET (ud->builder, "preview_frame");
650 ud->preview->frame = ghb_widget_int(widget) - 1;
651 if (ud->preview->encoded[ud->preview->frame])
653 widget = GHB_WIDGET(ud->builder, "live_progress_box");
654 gtk_widget_hide (widget);
655 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
656 gtk_widget_show (widget);
660 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
661 gtk_widget_hide (widget);
662 widget = GHB_WIDGET(ud->builder, "live_progress_box");
663 gtk_widget_show (widget);
664 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
665 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
666 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
668 if (ud->preview->pix != NULL)
669 g_object_unref(ud->preview->pix);
672 ghb_get_preview_image(titleindex, ud->preview->frame,
673 ud, &width, &height);
674 if (ud->preview->pix == NULL) return;
675 preview_width = gdk_pixbuf_get_width(ud->preview->pix);
676 preview_height = gdk_pixbuf_get_height(ud->preview->pix);
677 widget = GHB_WIDGET (ud->builder, "preview_image");
678 if (preview_width != ud->preview->width ||
679 preview_height != ud->preview->height)
681 gtk_widget_set_size_request(widget, preview_width, preview_height);
682 ud->preview->width = preview_width;
683 ud->preview->height = preview_height;
686 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
687 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
689 gchar *text = g_strdup_printf("%d x %d", width, height);
690 widget = GHB_WIDGET (ud->builder, "preview_dims");
691 gtk_label_set_text(GTK_LABEL(widget), text);
694 g_debug("preview %d x %d", preview_width, preview_height);
695 target_height = MIN(ud->preview->button_height, 128);
696 height = target_height;
697 width = preview_width * height / preview_height;
699 if ((height >= 16) && (width >= 16))
701 GdkPixbuf *scaled_preview;
702 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width,
703 height, GDK_INTERP_NEAREST);
704 if (scaled_preview != NULL)
706 widget = GHB_WIDGET (ud->builder, "preview_button_image");
707 gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
708 g_object_unref (scaled_preview);
713 #if defined(_ENABLE_GST)
714 G_MODULE_EXPORT gboolean
715 delayed_expose_cb(signal_user_data_t *ud)
720 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
721 if (GST_IS_BIN(vsink))
722 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
723 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
725 xover = GST_X_OVERLAY(vsink);
726 gst_x_overlay_expose(xover);
727 // This function is initiated by g_idle_add. Must return false
728 // so that it is not called again
733 G_MODULE_EXPORT gboolean
736 GdkEventExpose *event,
737 signal_user_data_t *ud)
739 #if defined(_ENABLE_GST)
740 if (ud->preview->state == PREVIEW_STATE_LIVE)
742 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
747 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
748 if (GST_IS_BIN(vsink))
749 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
750 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
752 xover = GST_X_OVERLAY(vsink);
753 gst_x_overlay_expose(xover);
754 // For some reason, the exposed region doesn't always get
755 // cleaned up here. But a delayed gst_x_overlay_expose()
757 g_idle_add((GSourceFunc)delayed_expose_cb, ud);
764 if (ud->preview->pix != NULL)
767 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
768 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
774 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
776 g_debug("allocate %d x %d", allocation->width, allocation->height);
777 if (ud->preview->button_width == allocation->width &&
778 ud->preview->button_height == allocation->height)
780 // Nothing to do. Bug out.
781 g_debug("nothing to do");
784 g_debug("prev allocate %d x %d", ud->preview->button_width,
785 ud->preview->button_height);
786 ud->preview->button_width = allocation->width;
787 ud->preview->button_height = allocation->height;
788 ghb_set_preview_image(ud);
792 set_visible(GtkWidget *widget, gboolean visible)
796 gtk_widget_show_now(widget);
800 gtk_widget_hide(widget);
805 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
809 g_debug("preview_button_clicked_cb()");
810 titleindex = ghb_settings_combo_int(ud->settings, "title");
814 GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
815 x = ghb_settings_get_int(ud->settings, "preview_x");
816 y = ghb_settings_get_int(ud->settings, "preview_y");
817 if (x >= 0 && y >= 0)
818 gtk_window_move(GTK_WINDOW(widget), x, y);
819 set_visible(widget, gtk_toggle_button_get_active(
820 GTK_TOGGLE_BUTTON(xwidget)));
822 ghb_widget_to_setting (ud->settings, xwidget);
823 ghb_check_dependency(ud, xwidget);
824 const gchar *name = gtk_widget_get_name(xwidget);
825 ghb_pref_save(ud->settings, name);
829 picture_settings_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
835 g_debug("picture_settings_clicked_cb()");
836 widget = GHB_WIDGET (ud->builder, "settings_window");
837 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(xwidget));
838 x = ghb_settings_get_int(ud->settings, "settings_x");
839 y = ghb_settings_get_int(ud->settings, "settings_y");
840 if (x >= 0 && y >= 0)
841 gtk_window_move(GTK_WINDOW(widget), x, y);
842 set_visible(widget, active);
843 if (ghb_settings_get_boolean(ud->settings, "show_preview"))
845 widget = GHB_WIDGET (ud->builder, "preview_window");
846 x = ghb_settings_get_int(ud->settings, "preview_x");
847 y = ghb_settings_get_int(ud->settings, "preview_y");
848 if (x >= 0 && y >= 0)
849 gtk_window_move(GTK_WINDOW(widget), x, y);
850 set_visible(widget, active);
851 // The window may be hidden behind the main window, raise it
853 gtk_window_present(GTK_WINDOW(widget));
858 picture_settings_alt_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
863 g_debug("picture_settings_alt_clicked_cb()");
864 toggle = GHB_WIDGET (ud->builder, "show_picture");
865 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
866 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
870 go_full(signal_user_data_t *ud)
873 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
874 gtk_window_fullscreen(window);
875 ghb_set_preview_image(ud);
880 fullscreen_clicked_cb(GtkWidget *toggle, signal_user_data_t *ud)
885 g_debug("fullscreen_clicked_cb()");
886 ghb_widget_to_setting (ud->settings, toggle);
887 ghb_check_dependency(ud, toggle);
888 const gchar *name = gtk_widget_get_name(toggle);
889 ghb_pref_save(ud->settings, name);
891 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
892 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));
895 gtk_window_set_resizable(window, TRUE);
896 gtk_button_set_label(GTK_BUTTON(toggle), "Windowed");
897 // Changing resizable property doesn't take effect immediately
898 // need to delay fullscreen till after this callback returns
900 g_idle_add((GSourceFunc)go_full, ud);
904 gtk_window_unfullscreen(window);
905 gtk_window_set_resizable(window, FALSE);
906 gtk_button_set_label(GTK_BUTTON(toggle), "Fullscreen");
907 ghb_set_preview_image(ud);
912 picture_settings_alt2_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
919 g_debug("picture_settings_alt2_clicked_cb()");
920 toggle = GHB_WIDGET (ud->builder, "show_picture");
921 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
924 // I don't want deleting the settings window to also remove the
925 // preview window, but changing the toggle will do this, so temporarily
926 // ignore the toggled signal
927 signal_id = g_signal_lookup("toggled", GTK_TYPE_TOGGLE_TOOL_BUTTON);
930 // Valid signal id found. This should always succeed.
931 handler_id = g_signal_handler_find((gpointer)toggle,
933 signal_id, 0, 0, 0, 0);
936 // This should also always succeed
937 g_signal_handler_block ((gpointer)toggle, handler_id);
942 GtkWidget *widget = GHB_WIDGET (ud->builder, "settings_window");
945 x = ghb_settings_get_int(ud->settings, "settings_x");
946 y = ghb_settings_get_int(ud->settings, "settings_y");
947 if (x >= 0 && y >= 0)
948 gtk_window_move(GTK_WINDOW(widget), x, y);
949 set_visible(widget, !active);
950 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
954 g_signal_handler_unblock ((gpointer)toggle, handler_id);
959 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
961 if (ud->preview->live_id >= 0)
963 ghb_stop_live_encode();
964 ud->preview->live_id = -1;
965 ud->preview->encode_frame = -1;
967 ghb_set_preview_image(ud);
970 G_MODULE_EXPORT gboolean
971 preview_window_delete_cb(
974 signal_user_data_t *ud)
976 live_preview_stop(ud);
977 gtk_widget_hide(widget);
982 settings_window_delete_cb(
985 signal_user_data_t *ud)
990 gtk_widget_hide(widget);
991 widget = GHB_WIDGET (ud->builder, "show_picture");
993 // I don't want deleting the settings window to also remove the
994 // preview window, but changing the toggle will do this, so temporarily
995 // ignore the toggled signal
996 signal_id = g_signal_lookup("toggled", GTK_TYPE_TOGGLE_TOOL_BUTTON);
999 // Valid signal id found. This should always succeed.
1000 handler_id = g_signal_handler_find((gpointer)widget, G_SIGNAL_MATCH_ID,
1001 signal_id, 0, 0, 0, 0);
1004 // This should also always succeed
1005 g_signal_handler_block ((gpointer)widget, handler_id);
1009 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
1013 g_signal_handler_unblock ((gpointer)widget, handler_id);
1018 G_MODULE_EXPORT void
1019 preview_duration_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1021 g_debug("preview_duration_changed_cb ()");
1023 ghb_widget_to_setting (ud->settings, widget);
1024 ghb_check_dependency(ud, widget);
1025 const gchar *name = gtk_widget_get_name(widget);
1026 ghb_pref_save(ud->settings, name);
1029 static guint hud_timeout_id = 0;
1032 hud_timeout(signal_user_data_t *ud)
1036 g_debug("hud_timeout()");
1037 widget = GHB_WIDGET(ud->builder, "preview_hud");
1038 gtk_widget_hide(widget);
1043 G_MODULE_EXPORT gboolean
1046 GdkEventCrossing *event,
1047 signal_user_data_t *ud)
1049 g_debug("hud_enter_cb()");
1050 if (hud_timeout_id != 0)
1055 mc = g_main_context_default();
1056 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1058 g_source_destroy(source);
1060 widget = GHB_WIDGET(ud->builder, "preview_hud");
1061 gtk_widget_show(widget);
1066 G_MODULE_EXPORT gboolean
1069 GdkEventCrossing *event,
1070 signal_user_data_t *ud)
1072 g_debug("hud_leave_cb()");
1073 if (hud_timeout_id != 0)
1078 mc = g_main_context_default();
1079 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1081 g_source_destroy(source);
1083 hud_timeout_id = g_timeout_add(300, (GSourceFunc)hud_timeout, ud);
1087 G_MODULE_EXPORT gboolean
1090 GdkEventMotion *event,
1091 signal_user_data_t *ud)
1093 //g_debug("hud_motion_cb %d", hud_timeout_id);
1094 if (hud_timeout_id != 0)
1099 mc = g_main_context_default();
1100 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1102 g_source_destroy(source);
1104 widget = GHB_WIDGET(ud->builder, "preview_hud");
1105 if (!GTK_WIDGET_VISIBLE(widget))
1107 gtk_widget_show(widget);
1109 hud_timeout_id = g_timeout_add_seconds(4, (GSourceFunc)hud_timeout, ud);
1114 ghb_curved_rect_mask(gint width, gint height, gint radius)
1120 if (!width || !height)
1123 shape = (GdkDrawable *)gdk_pixmap_new (NULL, width, height, 1);
1125 cr = gdk_cairo_create (shape);
1129 if (radius > width / 2)
1131 if (radius > height / 2)
1132 radius = height / 2;
1134 // fill shape with black
1136 cairo_rectangle (cr, 0, 0, width, height);
1137 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1141 cairo_move_to (cr, 0, radius);
1142 cairo_curve_to (cr, 0 , 0, 0 , 0, radius, 0);
1143 cairo_line_to (cr, w - radius, 0);
1144 cairo_curve_to (cr, w, 0, w, 0, w, radius);
1145 cairo_line_to (cr, w , h - radius);
1146 cairo_curve_to (cr, w, h, w, h, w - radius, h);
1147 cairo_line_to (cr, 0 + radius, h);
1148 cairo_curve_to (cr, 0, h, 0, h, 0, h - radius);
1150 cairo_close_path(cr);
1152 cairo_set_source_rgb(cr, 1, 1, 1);
1160 G_MODULE_EXPORT void
1161 preview_hud_size_alloc_cb(
1163 GtkAllocation *allocation,
1164 signal_user_data_t *ud)
1168 //g_message("preview_hud_size_alloc_cb()");
1169 if (GTK_WIDGET_VISIBLE(widget) && allocation->height > 50)
1171 shape = ghb_curved_rect_mask(allocation->width,
1172 allocation->height, allocation->height/4);
1175 gtk_widget_shape_combine_mask(widget, shape, 0, 0);
1176 gdk_pixmap_unref(shape);
1181 G_MODULE_EXPORT gboolean
1182 preview_configure_cb(
1184 GdkEventConfigure *event,
1185 signal_user_data_t *ud)
1189 //g_message("preview_configure_cb()");
1190 if (GTK_WIDGET_VISIBLE(widget))
1192 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1193 ghb_settings_set_int(ud->settings, "preview_x", x);
1194 ghb_settings_set_int(ud->settings, "preview_y", y);
1195 ghb_pref_set(ud->settings, "preview_x");
1196 ghb_pref_set(ud->settings, "preview_y");
1202 G_MODULE_EXPORT gboolean
1203 settings_configure_cb(
1205 GdkEventConfigure *event,
1206 signal_user_data_t *ud)
1210 //g_message("settings_configure_cb()");
1211 if (GTK_WIDGET_VISIBLE(widget))
1213 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1214 ghb_settings_set_int(ud->settings, "settings_x", x);
1215 ghb_settings_set_int(ud->settings, "settings_y", y);
1216 ghb_pref_set(ud->settings, "settings_x");
1217 ghb_pref_set(ud->settings, "settings_y");