1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
15 #include <glib/gstdio.h>
19 #include <gdk-pixbuf/gdk-pixbuf.h>
21 #include <mono/jit/jit.h>
22 #include <mono/metadata/appdomain.h>
23 #include <mono/metadata/assembly.h>
25 #include "codec-version.h"
26 #include "pipeline-ui.h"
27 #include "downloader.h"
31 #include "codec-url.h"
33 #define EULA_URL "http://go.microsoft.com/fwlink/?LinkId=149579"
35 bool CodecDownloader::running
= false;
37 CodecDownloader::CodecDownloader (Surface
*surf
)
48 eula_scrollwindow
= NULL
;
56 CodecDownloader::~CodecDownloader ()
66 CodecDownloader::ShowUI (Surface
*surface
)
68 g_return_if_fail (surface
!= NULL
);
74 if (!(moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
))
77 CodecDownloader
*cd
= new CodecDownloader (surface
);
82 // ----- Event Proxies -----
85 CodecDownloader::ResponseEventHandler (GtkDialog
*dialog
, gint response
, gpointer data
)
87 ((CodecDownloader
*) data
)->ResponseEvent (dialog
, (GtkResponseType
)response
);
91 CodecDownloader::DownloadFailedHandler (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
93 ((CodecDownloader
*) closure
)->DownloadFailed (sender
, args
);
97 CodecDownloader::DownloadCompletedHandler (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
99 ((CodecDownloader
*) closure
)->DownloadCompleted (sender
, args
);
103 CodecDownloader::DownloadProgressChangedHandler (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
105 ((CodecDownloader
*) closure
)->DownloadProgressChanged (sender
, args
);
108 // ----- Event Handlers -----
111 CodecDownloader::ResponseEvent (GtkDialog
*dialog
, GtkResponseType response
)
113 LOG_UI ("CodecDownloader::ResponseEvent (%d)\n", response
);
114 SetCurrentDeployment ();
117 case GTK_RESPONSE_CANCEL
:
118 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dont_ask
))) {
119 LOG_UI ("Setting DontInstallMSCodecs\n");
120 configuration
.SetBooleanValue ("Codecs", "DontInstallMSCodecs", true);
121 configuration
.Save ();
127 case GTK_RESPONSE_DELETE_EVENT
:
130 case GTK_RESPONSE_OK
:
139 CodecDownloader::DownloadProgressChanged (EventObject
*sender
, EventArgs
*args
)
141 g_return_if_fail (dl
!= NULL
);
142 double progress
= dl
->GetDownloadProgress ();
143 LOG_UI ("CodecDownloader::DownloadProgressChanged (): %.2f\n", progress
);
144 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar
), progress
);
148 CodecDownloader::DownloadFailed (EventObject
*sender
, EventArgs
*args
)
150 ErrorEventArgs
*eea
= (ErrorEventArgs
*) args
;
153 LOG_UI ("CodecDownloader::DownloadFailed ()\n");
155 msg
= g_strdup_printf ("An error occurred while downloading the %s", state
== 1
156 ? "End User License Agreement."
157 : "add-on software.");
159 SetHeader ((const gchar
*)msg
);
160 SetMessage (eea
->GetErrorMessage());
162 ToggleProgress (false);
164 gtk_image_set_from_stock (GTK_IMAGE (icon
), GTK_STOCK_DIALOG_ERROR
, GTK_ICON_SIZE_DIALOG
);
165 gtk_button_set_label (GTK_BUTTON (accept_button
), GTK_STOCK_CLOSE
);
166 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, true);
167 gtk_widget_hide (cancel_button
);
175 CodecDownloader::VerifyDownload (const char *filename
)
177 static MonoMethod
*moon_codec_integrity
= NULL
;
179 if (!moon_codec_integrity
) {
180 MonoAssembly
*sw
= mono_assembly_load_with_partial_name ("System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", NULL
);
184 MonoImage
*image
= mono_assembly_get_image (sw
);
188 MonoClass
*klass
= mono_class_from_name (image
, "Mono", "Helper");
192 moon_codec_integrity
= mono_class_get_method_from_name (klass
, "CheckFileIntegrity", 1);
193 if (!moon_codec_integrity
)
198 params
[0] = mono_string_new (mono_domain_get (), filename
);
199 MonoObject
*exc
= NULL
;
201 MonoObject
*ret
= mono_runtime_invoke (moon_codec_integrity
, NULL
, params
, &exc
);
205 return (bool) (*(MonoBoolean
*) mono_object_unbox (ret
));
209 CodecDownloader::DownloadCompleted (EventObject
*sender
, EventArgs
*args
)
211 gchar
*downloaded_file
= NULL
;
212 gchar
*codec_path
= NULL
;
213 gchar
*codec_dir
= NULL
;
217 LOG_UI ("CodecDownloader::DownloadCompleted ()\n");
219 ToggleProgress (false);
222 case 1: // downloading eula, we're now finished downloading the eula
223 eula
= dl
->GetResponseText (NULL
, &size
);
225 SetHeader ("End User License Agreement");
226 SetMessage ("Before the required software can be installed, you must first agree "
227 "to the End User License Agreement below.");
230 gtk_button_set_label (GTK_BUTTON (accept_button
), "_Accept");
231 gtk_label_set_markup (GTK_LABEL (eula_view
), eula
);
233 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, true);
237 case 3: // downloading codec, we're now finished downloading the codec
238 codec_path
= g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME
, NULL
);
239 codec_dir
= g_path_get_dirname (codec_path
);
241 downloaded_file
= dl
->GetDownloadedFilename (NULL
);
245 if (!VerifyDownload (downloaded_file
)) {
246 SetHeader ("An error occurred when installing the software");
247 SetMessage ("We could not verify the downloaded binary. Please try again later.");
248 } else if (g_mkdir_with_parents (codec_dir
, 0700) == -1 ||
249 (codec_fd
= g_open (codec_path
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0700)) == -1 ||
250 CopyFileTo (downloaded_file
, codec_fd
) == -1) {
251 SetHeader ("An error occurred when installing the software");
252 SetMessage (strerror (errno
));
254 SetHeader ("Software successfully downloaded and installed!");
255 SetMessage ("Please refresh the web page you were viewing to allow the new software to take effect.");
257 Media::RegisterMSCodecs ();
262 g_free (downloaded_file
);
264 gtk_widget_hide (cancel_button
);
265 gtk_button_set_label (GTK_BUTTON (accept_button
), GTK_STOCK_CLOSE
);
266 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, true);
271 printf ("CodecDownloader::DownloadCompleted (): Invalid state: %i\n", state
);
277 CodecDownloader::AcceptClicked ()
279 LOG_UI ("CodecDownloader::AcceptClicked\n");
281 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar
), 0.0);
282 ToggleProgress (true);
287 case 0: // initial, waiting for user input
288 g_return_if_fail (dl
!= NULL
);
289 SetHeader ("Downloading license agreement...");
291 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, false);
293 dl
->Open ("GET", EULA_URL
, NoPolicy
);
298 case 2: // eula downloaded, waiting for user input
299 g_return_if_fail (dl
!= NULL
);
301 SetHeader ("Downloading the required software...");
304 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, false);
306 env_url
= getenv ("MOONLIGHT_CODEC_URL");
308 dl
->Open ("GET", env_url
, NoPolicy
);
310 char *codec_url
= g_strdup_printf("%s", CODEC_URL
);
311 dl
->Open ("GET", codec_url
, NoPolicy
);
323 printf ("CodecDownloader::AcceptClicked (): Invalid state: %i\n", state
);
328 // ----- Utilities -----
331 CodecDownloader::CreateDownloader ()
334 dl
= surface
->CreateDownloader ();
335 // since we put up a UI, this might happen if the user has navigated to another page before dismissing the UI
336 // (the surface would be zombified).
337 g_return_if_fail (dl
!= NULL
);
338 dl
->AddHandler (Downloader::DownloadProgressChangedEvent
, DownloadProgressChangedHandler
, this);
339 dl
->AddHandler (Downloader::DownloadFailedEvent
, DownloadFailedHandler
, this);
340 dl
->AddHandler (Downloader::CompletedEvent
, DownloadCompletedHandler
, this);
345 CodecDownloader::DestroyDownloader ()
348 dl
->RemoveHandler (Downloader::DownloadProgressChangedEvent
, DownloadProgressChangedHandler
, this);
349 dl
->RemoveHandler (Downloader::DownloadFailedEvent
, DownloadFailedHandler
, this);
350 dl
->RemoveHandler (Downloader::CompletedEvent
, DownloadCompletedHandler
, this);
357 CodecDownloader::SetHeader (const gchar
*message
)
359 gchar
*message_full
= g_strdup_printf ("<big><b>%s</b></big>", message
);
360 gtk_label_set_markup (GTK_LABEL (header_label
), message_full
);
361 g_free (message_full
);
365 CodecDownloader::SetMessage (const gchar
*message
)
367 gtk_label_set_text (GTK_LABEL (message_label
), message
);
368 gtk_widget_show (message_label
);
372 CodecDownloader::HideMessage ()
374 gtk_widget_hide (message_label
);
378 CodecDownloader::ToggleEula (bool show
)
381 gtk_widget_show_all (eula_scrollwindow
);
383 gtk_widget_hide (eula_scrollwindow
);
388 CodecDownloader::ToggleProgress (bool show
)
391 gtk_image_set_from_stock (GTK_IMAGE (icon
), GTK_STOCK_SAVE
, GTK_ICON_SIZE_DIALOG
);
392 gtk_widget_hide (dont_ask
);
393 gtk_widget_show_all (progress_bar
);
395 gtk_image_set_from_stock (GTK_IMAGE (icon
), GTK_STOCK_DIALOG_QUESTION
, GTK_ICON_SIZE_DIALOG
);
396 gtk_widget_hide (progress_bar
);
401 CodecDownloader::AdaptToParentWindow ()
403 // try to find a parent for our window
404 // there must be a better way of doing this though :|
405 GList
*toplevels
= gtk_window_list_toplevels ();
406 GList
*current
= toplevels
;
407 GtkWindow
*parent
= NULL
;
409 while (current
!= NULL
) {
410 const char *title
= gtk_window_get_title (GTK_WINDOW (current
->data
));
411 if (title
!= NULL
&& strstr (title
, "Mozilla Firefox") != NULL
) {
412 parent
= GTK_WINDOW (current
->data
);
416 current
= current
->next
;
418 g_list_free (toplevels
);
420 if (parent
!= NULL
) {
421 gtk_window_set_transient_for (GTK_WINDOW (dialog
), parent
);
422 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_CENTER_ON_PARENT
);
424 // If no parent could be found, just center in the screen
425 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_CENTER
);
429 // ----- Dialog Create/Destroy -----
432 CodecDownloader::Show ()
434 if (configuration
.GetBooleanValue ("Codecs", "DontInstallMSCodecs")) {
440 fprintf (stderr
, "CodecDownloader::Show (): Can't call Show more than once.\n");
445 gint label_width
= 400;
446 GdkColor white
= {0, 65535, 65535, 65535};
448 // Build HIG Dialog Box
449 dialog
= gtk_dialog_new_with_buttons ("Moonlight Codecs Installer", NULL
, (GtkDialogFlags
)
450 (GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_NO_SEPARATOR
), NULL
);
451 cancel_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
), GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
452 accept_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
), "_Install Codecs", GTK_RESPONSE_OK
);
453 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
);
455 AdaptToParentWindow ();
456 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 5);
457 gtk_object_set (GTK_OBJECT (dialog
), "resizable", false, NULL
);
460 GtkWidget
*hbox
= gtk_hbox_new (false, 12);
461 gtk_container_set_border_width (GTK_CONTAINER (hbox
), 5);
462 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), hbox
, true, true, 0);
465 icon
= gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION
, GTK_ICON_SIZE_DIALOG
);
466 gtk_misc_set_alignment (GTK_MISC (icon
), 0.5f
, 0.0f
);
467 gtk_box_pack_start (GTK_BOX (hbox
), icon
, false, false, 0);
469 // Contents container
470 vbox
= gtk_vbox_new (false, 0);
471 gtk_box_set_spacing (GTK_BOX (vbox
), 10);
472 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, true, true, 0);
475 header_label
= gtk_label_new (NULL
);
476 SetHeader ("Would you like to install the required add-on to play the content on this page?");
477 gtk_label_set_line_wrap (GTK_LABEL (header_label
), true);
478 gtk_label_set_justify (GTK_LABEL (header_label
), GTK_JUSTIFY_LEFT
);
479 gtk_misc_set_alignment (GTK_MISC (header_label
), 0.0f
, 0.5f
);
480 gtk_widget_set_size_request (header_label
, label_width
, -1);
481 gtk_box_pack_start (GTK_BOX (vbox
), header_label
, false, false, 0);
484 message_label
= gtk_label_new (NULL
);
485 SetMessage ("This page requires the Microsoft Media Pack "
486 "to be installed to play multimedia content.\n\n"
487 "If you choose to install it, the software will be "
488 "automatically downloaded and installed "
489 "from Microsoft's web site.");
490 gtk_label_set_line_wrap (GTK_LABEL (message_label
), true);
491 gtk_label_set_justify (GTK_LABEL (message_label
), GTK_JUSTIFY_LEFT
);
492 gtk_misc_set_alignment (GTK_MISC (message_label
), 0.0f
, 0.5f
);
493 gtk_widget_set_size_request (message_label
, label_width
, -1);
494 gtk_box_pack_start (GTK_BOX (vbox
), message_label
, false, false, 0);
496 dont_ask
= gtk_check_button_new_with_label ("Do not ask me to install this add-on again");
497 gtk_box_pack_start (GTK_BOX (vbox
), dont_ask
, false, false, 0);
500 progress_bar
= gtk_progress_bar_new ();
501 gtk_box_pack_start (GTK_BOX (vbox
), progress_bar
, false, false, 0);
504 eula_view
= gtk_label_new (NULL
);
505 gtk_label_set_selectable (GTK_LABEL (eula_view
), TRUE
);
506 gtk_label_set_line_wrap (GTK_LABEL (eula_view
), TRUE
);
508 eula_scrollwindow
= gtk_scrolled_window_new (NULL
, NULL
);
509 eula_evtbox
= gtk_event_box_new ();
511 gtk_widget_modify_bg (GTK_WIDGET (eula_evtbox
), GTK_STATE_NORMAL
, &white
);
512 gtk_container_add (GTK_CONTAINER (eula_evtbox
), eula_view
);
514 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (eula_scrollwindow
), GTK_SHADOW_IN
);
515 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (eula_scrollwindow
), eula_evtbox
);
516 gtk_widget_set_size_request (eula_scrollwindow
, -1, 225);
517 gtk_box_pack_end (GTK_BOX (vbox
), eula_scrollwindow
, false, false, 0);
520 g_signal_connect (G_OBJECT (dialog
), "response", G_CALLBACK (ResponseEventHandler
), this);
522 gtk_object_set (GTK_OBJECT (accept_button
), "has-focus", true, "has-default", true, NULL
);
524 gtk_widget_show_all (dialog
);
525 ToggleProgress (false);
528 ref (); // We manage our lifetime ourself
533 CodecDownloader::Close ()
535 LOG_UI ("CodecDownloader::Close ()\n");
539 DestroyDownloader ();
542 gtk_widget_destroy (dialog
);