2
* Copyright © 2021 Pojar Gheorghe–Ioan <geoubuntu@gmail.com>
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; either version 2 of
7
* the License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
#include <gio/gdesktopappinfo.h>
21
#include <glib/gi18n.h>
23
#include <gst/pbutils/install-plugins.h>
24
#include <gst/pbutils/pbutils.h>
27
#ifdef GDK_WINDOWING_X11
33
#include "missing_plugins.h"
35
static GList *blocked_plugins = NULL;
37
static gpointer parent_window = NULL;
38
static gboolean retry;
45
} CodecInstallContext;
48
codec_install_plugin_is_blocked (const gchar *detail)
52
res = g_list_find_custom (blocked_plugins, detail, (GCompareFunc) strcmp);
58
codec_install_block_plugin (const gchar *detail)
60
if (!codec_install_plugin_is_blocked (detail))
62
blocked_plugins = g_list_prepend (blocked_plugins, g_strdup (detail));
67
codec_install_context_free (CodecInstallContext *ctx)
69
g_strfreev (ctx->descriptions);
70
g_strfreev (ctx->details);
75
on_plugin_installation_done (GstInstallPluginsReturn res,
78
CodecInstallContext *ctx = (CodecInstallContext *) user_data;
83
/* treat partial success the same as success; in the worst case we'll
84
* just do another round and get NOT_FOUND as result that time */
85
case GST_INSTALL_PLUGINS_PARTIAL_SUCCESS:
86
case GST_INSTALL_PLUGINS_SUCCESS:
88
/* block installed plugins too, so that we don't get into
89
* endless installer loops in case of inconsistencies */
90
for (p = ctx->details; p != NULL && *p != NULL; ++p)
92
codec_install_block_plugin (*p);
95
g_message ("Missing plugins installed. Updating plugin registry…");
97
/* force GStreamer to re-read its plugin registry */
98
retry = gst_update_registry ();
101
g_message ("Plugin registry updated, trying again");
105
g_warning ("GStreamer registry update failed");
111
case GST_INSTALL_PLUGINS_NOT_FOUND:
113
g_message ("No installation candidate for missing plugins found");
115
/* NOT_FOUND should only be returned if not a single one of the
116
* requested plugins was found; if we managed to play something
117
* anyway, we should just continue playing what we have and
118
* block the requested plugins for this session; if we could not
119
* play anything we should block them as well, so the install
120
* wizard isn't called again for nothing */
121
for (p = ctx->details; p != NULL && *p != NULL; ++p)
123
codec_install_block_plugin (*p);
130
case GST_INSTALL_PLUGINS_USER_ABORT:
132
/* block on user abort, so we show an error next time (or just
133
* what we can) instead of calling the installer */
134
for (p = ctx->details; p != NULL && *p != NULL; ++p)
136
codec_install_block_plugin (*p);
143
case GST_INSTALL_PLUGINS_ERROR:
144
case GST_INSTALL_PLUGINS_CRASHED:
147
g_message ("Missing plugin installation failed: %s", gst_install_plugins_return_get_name (res));
153
case GST_INSTALL_PLUGINS_STARTED_OK:
154
case GST_INSTALL_PLUGINS_INTERNAL_FAILURE:
155
case GST_INSTALL_PLUGINS_HELPER_MISSING:
156
case GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS:
158
g_assert_not_reached ();
163
codec_install_context_free (ctx);
167
set_startup_notification_id (GstInstallPluginsContext *install_ctx)
169
g_autofree gchar *startup_id = NULL;
172
timestamp = gtk_get_current_event_time ();
173
startup_id = g_strdup_printf ("_TIME%u", timestamp);
174
gst_install_plugins_context_set_startup_notification_id (install_ctx, startup_id);
178
start_plugin_installation (CodecInstallContext *ctx,
179
gboolean confirm_search)
181
GstInstallPluginsContext *install_ctx;
182
GstInstallPluginsReturn status;
184
install_ctx = gst_install_plugins_context_new ();
185
gst_install_plugins_context_set_desktop_id (install_ctx, "org.gnome." PACKAGE_NAME ".desktop");
186
gst_install_plugins_context_set_confirm_search (install_ctx, confirm_search);
187
set_startup_notification_id (install_ctx);
189
#ifdef GDK_WINDOWING_X11
190
if (parent_window != NULL && gtk_widget_get_realized (GTK_WIDGET (parent_window)))
192
if (GDK_IS_X11_WINDOW (gtk_widget_get_window (GTK_WIDGET (parent_window))))
195
xid = gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (parent_window)));
196
gst_install_plugins_context_set_xid (install_ctx, xid);
201
status = gst_install_plugins_async ((const char * const *) ctx->details, install_ctx,
202
on_plugin_installation_done,
205
gst_install_plugins_context_free (install_ctx);
207
if (status != GST_INSTALL_PLUGINS_STARTED_OK)
209
if (status == GST_INSTALL_PLUGINS_HELPER_MISSING)
211
g_message ("Automatic missing codec installation not supported (helper script missing)");
215
g_warning ("Failed to start codec installation: %s", gst_install_plugins_return_get_name (status));
218
codec_install_context_free (ctx);
226
on_codec_confirmation_dialog_response (GtkDialog *dialog,
230
CodecInstallContext *ctx = (CodecInstallContext *) user_data;
234
case GTK_RESPONSE_ACCEPT:
236
start_plugin_installation (ctx, FALSE);
240
case GTK_RESPONSE_CANCEL:
241
case GTK_RESPONSE_DELETE_EVENT:
248
g_assert_not_reached ();
252
gtk_widget_destroy (GTK_WIDGET (dialog));
256
show_codec_confirmation_dialog (CodecInstallContext *ctx,
257
const gchar *install_helper_display_name)
261
g_autofree gchar *button_text = NULL;
262
g_autofree gchar *descriptions_text = NULL;
263
g_autofree gchar *message_text = NULL;
266
dialog = gtk_message_dialog_new (GTK_WINDOW (ctx->widget),
268
GTK_DIALOG_DESTROY_WITH_PARENT,
271
_("An error occurred while trying to start recording"));
273
num = g_strv_length (ctx->descriptions);
274
descriptions_text = g_strjoinv ("\n", ctx->descriptions);
276
message_text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
277
"Additional “%s” plugin required to record in the selected format, but is not installed.",
278
"Additional plugins required to record in the selected format, but are not installed:\n%s", num),
279
(num == 1) ? *ctx->descriptions : descriptions_text);
281
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", message_text);
283
/* Translators: This is a button to launch a codec installer.
284
* %s will be replaced with the software installer's name, e.g.
285
* “Software” in case of gnome-software. */
286
button_text = g_strdup_printf (_("_Find in %s"), install_helper_display_name);
287
button = gtk_dialog_add_button (GTK_DIALOG (dialog), button_text, GTK_RESPONSE_ACCEPT);
288
gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
289
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
290
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, gst_install_plugins_supported ());
292
g_signal_connect (dialog, "response",
293
G_CALLBACK (on_codec_confirmation_dialog_response),
296
gtk_window_present (GTK_WINDOW (dialog));
300
on_codec_confirmation_infobar_response (GtkInfoBar *infobar,
304
CodecInstallContext *ctx = (CodecInstallContext *) user_data;
308
case GTK_RESPONSE_ACCEPT:
310
start_plugin_installation (ctx, FALSE);
314
case GTK_RESPONSE_CLOSE:
321
g_assert_not_reached ();
325
gtk_info_bar_set_revealed (infobar, FALSE);
329
show_codec_confirmation_infobar (CodecInstallContext *ctx,
330
const gchar *install_helper_display_name)
335
g_autofree gchar *markup = NULL;
336
g_autofree gchar *button_text = NULL;
338
infobar = gtk_info_bar_new ();
339
gtk_container_add (GTK_CONTAINER (ctx->widget), infobar);
341
gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_OTHER);
342
gtk_info_bar_set_show_close_button (GTK_INFO_BAR (infobar), TRUE);
344
markup = g_markup_printf_escaped ("<span style='italic'>\%s</span>", _("Additional software required to use the selected file format."));
345
label = gtk_label_new (NULL);
346
gtk_label_set_markup (GTK_LABEL (label), markup);
347
gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))), label);
349
/* Translators: This is a button to launch a codec installer.
350
* %s will be replaced with the software installer's name, e.g.
351
* “Software” in case of gnome-software. */
352
button_text = g_strdup_printf (_("_Find in %s"), install_helper_display_name);
353
button = gtk_info_bar_add_button (GTK_INFO_BAR (infobar), button_text, GTK_RESPONSE_ACCEPT);
354
gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
355
gtk_info_bar_set_default_response (GTK_INFO_BAR (infobar), GTK_RESPONSE_ACCEPT);
356
gtk_info_bar_set_response_sensitive (GTK_INFO_BAR (infobar), GTK_RESPONSE_ACCEPT, gst_install_plugins_supported ());
358
g_signal_connect (infobar, "response",
359
G_CALLBACK (on_codec_confirmation_infobar_response),
362
gtk_widget_show_all (infobar);
364
gtk_info_bar_set_revealed (GTK_INFO_BAR (infobar), TRUE);
365
gtk_widget_grab_focus (button);
369
on_packagekit_proxy_ready (GObject *source_object,
370
GAsyncResult *result,
373
CodecInstallContext *ctx = (CodecInstallContext *) user_data;
374
g_autoptr (GDBusProxy) packagekit_proxy = NULL;
375
g_autoptr (GVariant) property = NULL;
376
g_autoptr (GError) error = NULL;
378
packagekit_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
379
if (packagekit_proxy == NULL && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
384
if (packagekit_proxy != NULL)
386
property = g_dbus_proxy_get_cached_property (packagekit_proxy, "DisplayName");
387
if (property != NULL)
389
const gchar *display_name;
391
display_name = g_variant_get_string (property, NULL);
392
if (display_name != NULL && *display_name != '\0')
394
if (GTK_IS_WINDOW (ctx->widget))
396
show_codec_confirmation_dialog (ctx, display_name);
400
show_codec_confirmation_infobar (ctx, display_name);
408
/* If the above failed, fall back to immediately starting the codec installation */
409
start_plugin_installation (ctx, TRUE);
413
on_missing_plugins_event (GtkWidget *widget,
415
gchar **descriptions,
416
gboolean ignore_block,
419
CodecInstallContext *ctx;
422
num = g_strv_length (details);
423
g_return_val_if_fail (num > 0 && g_strv_length (descriptions) == num, FALSE);
425
ctx = g_new0 (CodecInstallContext, 1);
426
ctx->descriptions = g_strdupv (descriptions);
427
ctx->details = g_strdupv (details);
428
ctx->widget = widget;
430
for (i = 0; i < num; ++i)
432
if (ignore_block == FALSE && codec_install_plugin_is_blocked (ctx->details[i]))
434
g_message ("Missing plugin: %s (ignoring)", ctx->details[i]);
435
g_free (ctx->details[i]);
436
g_free (ctx->descriptions[i]);
437
ctx->details[i] = ctx->details[num - 1];
438
ctx->descriptions[i] = ctx->descriptions[num - 1];
439
ctx->details[num - 1] = NULL;
440
ctx->descriptions[num - 1] = NULL;
446
g_message ("Missing plugin: %s (%s)", ctx->details[i], ctx->descriptions[i]);
452
g_message ("All missing plugins are blocked, doing nothing");
453
codec_install_context_free (ctx);
457
/* Get the PackageKit session interface proxy and continue with the
458
* codec installation in the callback */
459
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
460
G_DBUS_PROXY_FLAGS_NONE,
462
"org.freedesktop.PackageKit",
463
"/org/freedesktop/PackageKit",
464
"org.freedesktop.PackageKit.Modify2",
466
on_packagekit_proxy_ready,
473
missing_plugins_init (GtkWindow *window)
475
parent_window = window;
476
g_object_add_weak_pointer (G_OBJECT (parent_window), &parent_window);
478
gst_pb_utils_init ();