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 num = par_n * disp_par_d;
104 den = par_d * disp_par_n;
107 *width = *width * num / den;
109 *height = *height * den / num;
113 ghb_preview_init(signal_user_data_t *ud)
118 ud->preview = g_malloc0(sizeof(preview_t));
119 ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
120 gtk_widget_realize(ud->preview->view);
121 g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
122 G_CALLBACK(preview_expose_cb), ud);
123 ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
125 ud->preview->play = gst_element_factory_make("playbin", "play");
126 ud->preview->pause = TRUE;
127 ud->preview->encode_frame = -1;
128 ud->preview->live_id = -1;
129 //xover = gst_element_factory_make("xvimagesink", "xover");
130 xover = gst_element_factory_make("gconfvideosink", "xover");
131 g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
132 //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
134 bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
135 gst_bus_add_watch(bus, live_preview_cb, ud);
136 gst_bus_set_sync_handler(bus, create_window, ud->preview);
137 gst_object_unref(bus);
141 ghb_preview_cleanup(signal_user_data_t *ud)
143 if (ud->preview->current)
145 ud->preview->current = NULL;
146 g_free(ud->preview->current);
150 static GstBusSyncReply
151 create_window(GstBus *bus, GstMessage *msg, gpointer data)
153 preview_t *preview = (preview_t*)data;
155 switch (GST_MESSAGE_TYPE(msg))
157 case GST_MESSAGE_ELEMENT:
159 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
161 gst_x_overlay_set_xwindow_id(
162 GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
163 gst_message_unref(msg);
175 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
177 GValueArray *info_arr = NULL;
184 g_object_get(play, "stream-info-value-array", &info_arr, NULL);
185 if (info_arr == NULL)
188 for (ii = 0; ii < info_arr->n_values; ++ii)
193 val = g_value_array_get_nth(info_arr, ii);
194 info_obj = g_value_get_object(val);
201 g_object_get(info_obj, "type", &type, NULL);
202 pspec = g_object_class_find_property(
203 G_OBJECT_GET_CLASS (info_obj), "type");
204 value = g_enum_get_value(
205 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
208 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
209 g_ascii_strcasecmp (value->value_name, typestr) == 0)
211 ret = g_list_prepend (ret, g_object_ref (info_obj));
216 g_value_array_free (info_arr);
217 return g_list_reverse (ret);
221 caps_set(GstCaps *caps, signal_user_data_t *ud)
225 ss = gst_caps_get_structure(caps, 0);
228 gint fps_n, fps_d, width, height;
229 guint num, den, par_n, par_d;
230 gint disp_par_n, disp_par_d;
233 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
234 gst_structure_get_int(ss, "width", &width);
235 gst_structure_get_int(ss, "height", &height);
236 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
237 par_n = gst_value_get_fraction_numerator(par);
238 par_d = gst_value_get_fraction_denominator(par);
240 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
241 gst_video_calculate_display_ratio(
242 &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
245 width = gst_util_uint64_scale_int(height, num, den);
247 height = gst_util_uint64_scale_int(width, den, num);
249 if (ghb_settings_get_boolean(ud->settings, "reduce_hd_preview"))
254 ss = gdk_screen_get_default();
255 s_w = gdk_screen_get_width(ss);
256 s_h = gdk_screen_get_height(ss);
258 if (width > s_w * 80 / 100)
260 width = s_w * 80 / 100;
261 height = gst_util_uint64_scale_int(width, den, num);
263 if (height > s_h * 80 / 100)
265 height = s_h * 80 / 100;
266 width = gst_util_uint64_scale_int(height, num, den);
270 if (width != ud->preview->width || height != ud->preview->height)
272 gtk_widget_set_size_request(ud->preview->view, width, height);
273 ud->preview->width = width;
274 ud->preview->height = height;
280 update_stream_info(signal_user_data_t *ud)
282 GList *vstreams, *ll;
285 vstreams = get_stream_info_objects_for_type(ud->preview->play, "video");
288 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
290 g_object_get(ll->data, "object", &vpad, NULL);
297 caps = gst_pad_get_negotiated_caps(vpad);
301 gst_caps_unref(caps);
303 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
304 gst_object_unref(vpad);
306 g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
307 g_list_free(vstreams);
311 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
313 signal_user_data_t *ud = (signal_user_data_t*)data;
315 switch (GST_MESSAGE_TYPE(msg))
317 case GST_MESSAGE_ERROR:
322 gst_message_parse_error(msg, &err, &debug);
323 g_warning("Gstreamer Error: %s", err->message);
328 case GST_MESSAGE_ELEMENT:
330 if (gst_is_missing_plugin_message(msg))
332 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
333 gchar *message, *desc;
334 desc = gst_missing_plugin_message_get_description(msg);
335 message = g_strdup_printf(
336 "Missing GStreamer plugin\n"
337 "Audio or Video may not play as expected\n\n%s",
339 ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
341 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
345 case GST_MESSAGE_STATE_CHANGED:
347 GstState state, pending;
348 gst_element_get_state(ud->preview->play, &state, &pending, 0);
349 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
351 update_stream_info(ud);
355 case GST_MESSAGE_EOS:
360 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
361 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
362 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
363 ud->preview->pause = TRUE;
364 gst_element_seek(ud->preview->play, 1.0,
365 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
366 GST_SEEK_TYPE_SET, 0,
367 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
379 live_preview_start(signal_user_data_t *ud)
384 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
385 if (!ud->preview->encoded[ud->preview->frame])
387 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
388 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
389 ud->preview->pause = TRUE;
393 uri = g_strdup_printf("file://%s", ud->preview->current);
394 gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
395 ud->preview->state = PREVIEW_STATE_LIVE;
396 g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
397 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
398 ud->preview->pause = FALSE;
403 live_preview_pause(signal_user_data_t *ud)
407 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
408 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
409 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
410 ud->preview->pause = TRUE;
414 live_preview_stop(signal_user_data_t *ud)
419 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
420 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
421 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
422 ud->preview->pause = TRUE;
423 ud->preview->state = PREVIEW_STATE_IMAGE;
425 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
426 gtk_range_set_value(progress, 0);
430 ghb_live_reset(signal_user_data_t *ud)
434 if (ud->preview->live_id >= 0)
436 ghb_stop_live_encode();
438 ud->preview->live_id = -1;
439 ud->preview->encode_frame = -1;
440 if (!ud->preview->pause)
441 live_preview_stop(ud);
442 if (ud->preview->current)
444 g_free(ud->preview->current);
445 ud->preview->current = NULL;
447 encoded = ud->preview->encoded[ud->preview->frame];
448 memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
450 ghb_set_preview_image(ud);
453 extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
456 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
460 gint frame = ud->preview->frame;
462 tmp_dir = ghb_get_tmp_dir();
463 name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
464 if (ud->preview->current)
465 g_free(ud->preview->current);
466 ud->preview->current = name;
468 if (ud->preview->encoded[frame] &&
469 g_file_test(name, G_FILE_TEST_IS_REGULAR))
471 if (ud->preview->pause)
472 live_preview_start(ud);
474 live_preview_pause(ud);
480 ud->preview->encode_frame = frame;
481 js = ghb_value_dup(ud->settings);
482 ghb_settings_set_string(js, "destination", name);
483 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
484 ud->preview->live_id = 0;
485 ghb_add_live_job(js, ud->preview->live_id);
486 ghb_start_live_encode();
492 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
497 ud->preview->live_id = -1;
498 prog = GHB_WIDGET(ud->builder, "live_encode_progress");
500 ud->preview->encode_frame == ud->preview->frame)
502 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
503 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
504 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
505 live_preview_start(ud);
506 widget = GHB_WIDGET(ud->builder, "live_progress_box");
507 gtk_widget_hide (widget);
508 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
509 gtk_widget_show (widget);
513 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
514 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
515 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
520 unlock_progress_cb(signal_user_data_t *ud)
522 ud->preview->progress_lock = FALSE;
523 // This function is initiated by g_idle_add. Must return false
524 // so that it is not called again
529 ghb_live_preview_progress(signal_user_data_t *ud)
531 GstFormat fmt = GST_FORMAT_TIME;
532 gint64 len = -1, pos = -1;
534 if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
537 ud->preview->progress_lock = TRUE;
538 if (gst_element_query_duration(ud->preview->play, &fmt, &len))
540 if (len != -1 && fmt == GST_FORMAT_TIME)
542 ud->preview->len = len / GST_MSECOND;
545 if (gst_element_query_position(ud->preview->play, &fmt, &pos))
547 if (pos != -1 && fmt == GST_FORMAT_TIME)
549 ud->preview->pos = pos / GST_MSECOND;
552 if (ud->preview->len > 0)
557 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
558 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
559 gtk_range_set_value(progress, percent);
561 g_idle_add((GSourceFunc)unlock_progress_cb, ud);
565 unlock_seek_cb(signal_user_data_t *ud)
567 ud->preview->seek_lock = FALSE;
568 // This function is initiated by g_idle_add. Must return false
569 // so that it is not called again
574 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
579 if (ud->preview->progress_lock)
582 ud->preview->seek_lock = TRUE;
583 dval = gtk_range_get_value(GTK_RANGE(widget));
584 pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
585 gst_element_seek(ud->preview->play, 1.0,
586 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
587 GST_SEEK_TYPE_SET, pos,
588 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
589 g_idle_add((GSourceFunc)unlock_seek_cb, ud);
593 ghb_set_preview_image(signal_user_data_t *ud)
596 gint preview_width, preview_height, target_height, width, height;
598 g_debug("set_preview_button_image ()");
601 live_preview_stop(ud);
603 titleindex = ghb_settings_combo_int(ud->settings, "title");
604 if (titleindex < 0) return;
605 widget = GHB_WIDGET (ud->builder, "preview_frame");
606 ud->preview->frame = ghb_widget_int(widget) - 1;
607 if (ud->preview->encoded[ud->preview->frame])
609 widget = GHB_WIDGET(ud->builder, "live_progress_box");
610 gtk_widget_hide (widget);
611 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
612 gtk_widget_show (widget);
616 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
617 gtk_widget_hide (widget);
618 widget = GHB_WIDGET(ud->builder, "live_progress_box");
619 gtk_widget_show (widget);
620 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
621 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
622 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
624 if (ud->preview->pix != NULL)
625 g_object_unref(ud->preview->pix);
628 ghb_get_preview_image(titleindex, ud->preview->frame,
629 ud, TRUE, &width, &height);
630 if (ud->preview->pix == NULL) return;
631 preview_width = gdk_pixbuf_get_width(ud->preview->pix);
632 preview_height = gdk_pixbuf_get_height(ud->preview->pix);
633 widget = GHB_WIDGET (ud->builder, "preview_image");
634 if (preview_width != ud->preview->width ||
635 preview_height != ud->preview->height)
637 gtk_widget_set_size_request(widget, preview_width, preview_height);
638 ud->preview->width = preview_width;
639 ud->preview->height = preview_height;
642 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
643 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
645 gchar *text = g_strdup_printf("%d x %d", width, height);
646 widget = GHB_WIDGET (ud->builder, "preview_dims");
647 gtk_label_set_text(GTK_LABEL(widget), text);
650 g_debug("preview %d x %d", preview_width, preview_height);
651 target_height = MIN(ud->preview->button_height, 128);
652 height = target_height;
653 width = preview_width * height / preview_height;
655 if ((height >= 16) && (width >= 16))
657 GdkPixbuf *scaled_preview;
658 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width,
659 height, GDK_INTERP_NEAREST);
660 if (scaled_preview != NULL)
662 widget = GHB_WIDGET (ud->builder, "preview_button_image");
663 gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
664 g_object_unref (scaled_preview);
670 delayed_expose_cb(signal_user_data_t *ud)
675 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
676 if (GST_IS_BIN(vsink))
677 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
678 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
680 xover = GST_X_OVERLAY(vsink);
681 gst_x_overlay_expose(xover);
682 // This function is initiated by g_idle_add. Must return false
683 // so that it is not called again
690 GdkEventExpose *event,
691 signal_user_data_t *ud)
693 if (ud->preview->state == PREVIEW_STATE_LIVE)
695 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
700 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
701 if (GST_IS_BIN(vsink))
702 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
703 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
705 xover = GST_X_OVERLAY(vsink);
706 gst_x_overlay_expose(xover);
707 // For some reason, the exposed region doesn't always get
708 // cleaned up here. But a delayed gst_x_overlay_expose()
710 g_idle_add((GSourceFunc)delayed_expose_cb, ud);
716 if (ud->preview->pix != NULL)
719 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
720 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
726 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
728 g_debug("allocate %d x %d", allocation->width, allocation->height);
729 if (ud->preview->button_width == allocation->width &&
730 ud->preview->button_height == allocation->height)
732 // Nothing to do. Bug out.
733 g_debug("nothing to do");
736 g_debug("prev allocate %d x %d", ud->preview->button_width,
737 ud->preview->button_height);
738 ud->preview->button_width = allocation->width;
739 ud->preview->button_height = allocation->height;
740 ghb_set_preview_image(ud);
744 set_visible(GtkWidget *widget, gboolean visible)
748 gtk_widget_show_now(widget);
752 gtk_widget_hide(widget);
757 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
761 g_debug("preview_button_clicked_cb()");
762 titleindex = ghb_settings_combo_int(ud->settings, "title");
766 GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
767 x = ghb_settings_get_int(ud->settings, "preview_x");
768 y = ghb_settings_get_int(ud->settings, "preview_y");
769 if (x >= 0 && y >= 0)
770 gtk_window_move(GTK_WINDOW(widget), x, y);
771 set_visible(widget, gtk_toggle_button_get_active(
772 GTK_TOGGLE_BUTTON(xwidget)));
774 ghb_widget_to_setting (ud->settings, xwidget);
775 ghb_check_dependency(ud, xwidget);
776 const gchar *name = gtk_widget_get_name(xwidget);
777 ghb_pref_save(ud->settings, name);
781 picture_settings_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
787 g_debug("picture_settings_clicked_cb()");
788 widget = GHB_WIDGET (ud->builder, "settings_window");
789 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(xwidget));
790 x = ghb_settings_get_int(ud->settings, "settings_x");
791 y = ghb_settings_get_int(ud->settings, "settings_y");
792 if (x >= 0 && y >= 0)
793 gtk_window_move(GTK_WINDOW(widget), x, y);
794 set_visible(widget, active);
795 if (ghb_settings_get_boolean(ud->settings, "show_preview"))
797 widget = GHB_WIDGET (ud->builder, "preview_window");
798 x = ghb_settings_get_int(ud->settings, "preview_x");
799 y = ghb_settings_get_int(ud->settings, "preview_y");
800 if (x >= 0 && y >= 0)
801 gtk_window_move(GTK_WINDOW(widget), x, y);
802 set_visible(widget, active);
807 picture_settings_alt_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
812 g_debug("picture_settings_alt_clicked_cb()");
813 toggle = GHB_WIDGET (ud->builder, "show_picture");
814 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
815 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
819 picture_settings_alt2_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
826 g_debug("picture_settings_alt2_clicked_cb()");
827 toggle = GHB_WIDGET (ud->builder, "show_picture");
828 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
831 // I don't want deleting the settings window to also remove the
832 // preview window, but changing the toggle will do this, so temporarily
833 // ignore the toggled signal
834 signal_id = g_signal_lookup("toggled", GTK_TYPE_TOGGLE_TOOL_BUTTON);
837 // Valid signal id found. This should always succeed.
838 handler_id = g_signal_handler_find((gpointer)toggle,
840 signal_id, 0, 0, 0, 0);
843 // This should also always succeed
844 g_signal_handler_block ((gpointer)toggle, handler_id);
849 GtkWidget *widget = GHB_WIDGET (ud->builder, "settings_window");
852 x = ghb_settings_get_int(ud->settings, "settings_x");
853 y = ghb_settings_get_int(ud->settings, "settings_y");
854 if (x >= 0 && y >= 0)
855 gtk_window_move(GTK_WINDOW(widget), x, y);
856 set_visible(widget, !active);
857 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
861 g_signal_handler_unblock ((gpointer)toggle, handler_id);
866 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
868 if (ud->preview->live_id >= 0)
870 ghb_stop_live_encode();
871 ud->preview->live_id = -1;
872 ud->preview->encode_frame = -1;
874 ghb_set_preview_image(ud);
878 preview_window_delete_cb(
881 signal_user_data_t *ud)
883 live_preview_stop(ud);
884 gtk_widget_hide(widget);
889 settings_window_delete_cb(
892 signal_user_data_t *ud)
897 gtk_widget_hide(widget);
898 widget = GHB_WIDGET (ud->builder, "show_picture");
900 // I don't want deleting the settings window to also remove the
901 // preview window, but changing the toggle will do this, so temporarily
902 // ignore the toggled signal
903 signal_id = g_signal_lookup("toggled", GTK_TYPE_TOGGLE_TOOL_BUTTON);
906 // Valid signal id found. This should always succeed.
907 handler_id = g_signal_handler_find((gpointer)widget, G_SIGNAL_MATCH_ID,
908 signal_id, 0, 0, 0, 0);
911 // This should also always succeed
912 g_signal_handler_block ((gpointer)widget, handler_id);
916 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
920 g_signal_handler_unblock ((gpointer)widget, handler_id);
926 preview_duration_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
928 g_debug("preview_duration_changed_cb ()");
930 ghb_widget_to_setting (ud->settings, widget);
931 ghb_check_dependency(ud, widget);
932 const gchar *name = gtk_widget_get_name(widget);
933 ghb_pref_save(ud->settings, name);
936 static guint hud_timeout_id = 0;
939 hud_timeout(signal_user_data_t *ud)
943 widget = GHB_WIDGET(ud->builder, "preview_hud");
944 gtk_widget_hide(widget);
949 G_MODULE_EXPORT gboolean
952 GdkEventCrossing *event,
953 signal_user_data_t *ud)
955 if (hud_timeout_id != 0)
960 mc = g_main_context_default();
961 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
963 g_source_destroy(source);
965 widget = GHB_WIDGET(ud->builder, "preview_hud");
966 gtk_widget_show(widget);
971 G_MODULE_EXPORT gboolean
974 GdkEventCrossing *event,
975 signal_user_data_t *ud)
977 if (hud_timeout_id != 0)
982 mc = g_main_context_default();
983 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
985 g_source_destroy(source);
987 hud_timeout_id = g_timeout_add(300, (GSourceFunc)hud_timeout, ud);
991 G_MODULE_EXPORT gboolean
994 GdkEventMotion *event,
995 signal_user_data_t *ud)
997 if (hud_timeout_id != 0)
1002 mc = g_main_context_default();
1003 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1005 g_source_destroy(source);
1010 GdkWindow *parent, *win;
1011 gint pw, ph, w, h, x, y;
1013 widget = GHB_WIDGET(ud->builder, "preview_image");
1014 parent = gtk_widget_get_window(widget);
1015 widget = GHB_WIDGET(ud->builder, "preview_hud");
1016 win = gtk_widget_get_window(widget);
1017 gtk_widget_show(widget);
1018 gdk_drawable_get_size(GDK_DRAWABLE(parent), &pw, &ph);
1019 gdk_drawable_get_size(GDK_DRAWABLE(win), &w, &h);
1022 y = ph - ph/4 - h/2;
1025 gdk_window_move(win, x, y);
1027 hud_timeout_id = g_timeout_add_seconds(10, (GSourceFunc)hud_timeout, ud);
1031 G_MODULE_EXPORT gboolean
1032 preview_configure_cb(
1034 GdkEventConfigure *event,
1035 signal_user_data_t *ud)
1039 g_debug("preview_configure_cb()");
1040 if (GTK_WIDGET_VISIBLE(widget))
1042 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1043 ghb_settings_set_int(ud->settings, "preview_x", x);
1044 ghb_settings_set_int(ud->settings, "preview_y", y);
1045 ghb_pref_set(ud->settings, "preview_x");
1046 ghb_pref_set(ud->settings, "preview_y");
1052 G_MODULE_EXPORT gboolean
1053 settings_configure_cb(
1055 GdkEventConfigure *event,
1056 signal_user_data_t *ud)
1060 g_debug("settings_configure_cb()");
1061 if (GTK_WIDGET_VISIBLE(widget))
1063 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1064 ghb_settings_set_int(ud->settings, "settings_x", x);
1065 ghb_settings_set_int(ud->settings, "settings_y", y);
1066 ghb_pref_set(ud->settings, "settings_x");
1067 ghb_pref_set(ud->settings, "settings_y");