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>
20 #include <gdk-pixbuf/gdk-pixbuf.h>
22 #define MONO_HEADERS_INCLUDED 1
24 #include <mono/jit/jit.h>
25 #include <mono/metadata/appdomain.h>
26 #include <mono/metadata/assembly.h>
28 #include "codec-version.h"
29 #include "pipeline-ui.h"
30 #include "downloader.h"
34 #include "codec-url.h"
36 #define EULA_URL "http://go.microsoft.com/fwlink/?LinkId=149579"
38 bool CodecDownloader::running
= false;
40 CodecDownloader::CodecDownloader (Surface
*surf
, bool is_user_initiated
)
42 this->is_user_initiated
= is_user_initiated
;
52 eula_scrollwindow
= NULL
;
60 CodecDownloader::~CodecDownloader ()
70 CodecDownloader::ShowUI (Surface
*surface
, bool is_user_initiated
)
72 g_return_if_fail (surface
!= NULL
);
78 if (!(moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
))
81 surface
->SetCurrentDeployment ();
82 CodecDownloader
*cd
= new CodecDownloader (surface
, is_user_initiated
);
87 // ----- Event Proxies -----
90 CodecDownloader::ResponseEventHandler (GtkDialog
*dialog
, gint response
, gpointer data
)
92 ((CodecDownloader
*) data
)->ResponseEvent (dialog
, (GtkResponseType
)response
);
96 CodecDownloader::DownloadFailedHandler (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
98 ((CodecDownloader
*) closure
)->DownloadFailed (sender
, args
);
102 CodecDownloader::DownloadCompletedHandler (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
104 ((CodecDownloader
*) closure
)->DownloadCompleted (sender
, args
);
108 CodecDownloader::DownloadProgressChangedHandler (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
110 ((CodecDownloader
*) closure
)->DownloadProgressChanged (sender
, args
);
113 // ----- Event Handlers -----
116 CodecDownloader::ResponseEvent (GtkDialog
*dialog
, GtkResponseType response
)
118 LOG_UI ("CodecDownloader::ResponseEvent (%d)\n", response
);
119 SetCurrentDeployment ();
122 case GTK_RESPONSE_CANCEL
:
123 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dont_ask
))) {
124 LOG_UI ("Setting DontInstallMSCodecs\n");
125 configuration
.SetBooleanValue ("Codecs", "DontInstallMSCodecs", true);
126 configuration
.Save ();
132 case GTK_RESPONSE_DELETE_EVENT
:
135 case GTK_RESPONSE_OK
:
144 CodecDownloader::DownloadProgressChanged (EventObject
*sender
, EventArgs
*args
)
146 g_return_if_fail (dl
!= NULL
);
147 double progress
= dl
->GetDownloadProgress ();
148 LOG_UI ("CodecDownloader::DownloadProgressChanged (): %.2f\n", progress
);
149 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar
), progress
);
153 CodecDownloader::DownloadFailed (EventObject
*sender
, EventArgs
*args
)
155 ErrorEventArgs
*eea
= (ErrorEventArgs
*) args
;
158 LOG_UI ("CodecDownloader::DownloadFailed ()\n");
160 msg
= g_strdup_printf ("An error occurred while downloading the %s", state
== 1
161 ? "End User License Agreement."
162 : "add-on software.");
164 SetHeader ((const gchar
*)msg
);
165 SetMessage (eea
->GetErrorMessage());
167 ToggleProgress (false);
169 gtk_image_set_from_stock (GTK_IMAGE (icon
), GTK_STOCK_DIALOG_ERROR
, GTK_ICON_SIZE_DIALOG
);
170 gtk_button_set_label (GTK_BUTTON (accept_button
), GTK_STOCK_CLOSE
);
171 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, true);
172 gtk_widget_hide (cancel_button
);
180 CodecDownloader::VerifyDownload (const char *filename
)
182 static MonoMethod
*moon_codec_integrity
= NULL
;
184 if (!moon_codec_integrity
) {
185 MonoAssembly
*sw
= mono_assembly_load_with_partial_name ("System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", NULL
);
189 MonoImage
*image
= mono_assembly_get_image (sw
);
193 MonoClass
*klass
= mono_class_from_name (image
, "Mono", "Helper");
197 moon_codec_integrity
= mono_class_get_method_from_name (klass
, "CheckFileIntegrity", 1);
198 if (!moon_codec_integrity
)
203 params
[0] = mono_string_new (mono_domain_get (), filename
);
204 MonoObject
*exc
= NULL
;
206 MonoObject
*ret
= mono_runtime_invoke (moon_codec_integrity
, NULL
, params
, &exc
);
210 return (bool) (*(MonoBoolean
*) mono_object_unbox (ret
));
214 CodecDownloader::DownloadCompleted (EventObject
*sender
, EventArgs
*args
)
216 gchar
*downloaded_file
= NULL
;
217 gchar
*codec_path
= NULL
;
218 gchar
*codec_dir
= NULL
;
222 LOG_UI ("CodecDownloader::DownloadCompleted ()\n");
224 ToggleProgress (false);
227 case 1: // downloading eula, we're now finished downloading the eula
228 eula
= dl
->GetResponseText (NULL
, &size
);
230 SetHeader ("End User License Agreement");
231 SetMessage ("Before the required software can be installed, you must first agree "
232 "to the End User License Agreement below.");
235 gtk_button_set_label (GTK_BUTTON (accept_button
), "_Accept");
236 gtk_label_set_markup (GTK_LABEL (eula_view
), eula
);
238 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, true);
242 case 3: // downloading codec, we're now finished downloading the codec
243 codec_path
= g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME
, NULL
);
244 codec_dir
= g_path_get_dirname (codec_path
);
246 downloaded_file
= dl
->GetDownloadedFilename (NULL
);
250 if (!VerifyDownload (downloaded_file
)) {
251 SetHeader ("An error occurred when installing the software");
252 SetMessage ("We could not verify the downloaded binary. Please try again later.");
253 } else if (g_mkdir_with_parents (codec_dir
, 0700) == -1 ||
254 (codec_fd
= g_open (codec_path
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0700)) == -1 ||
255 CopyFileTo (downloaded_file
, codec_fd
) == -1) {
256 SetHeader ("An error occurred when installing the software");
257 SetMessage (strerror (errno
));
259 SetHeader ("Software successfully downloaded and installed!");
260 SetMessage ("Please refresh the web page you were viewing to allow the new software to take effect.");
262 Media::RegisterMSCodecs ();
267 g_free (downloaded_file
);
269 gtk_widget_hide (cancel_button
);
270 gtk_button_set_label (GTK_BUTTON (accept_button
), GTK_STOCK_CLOSE
);
271 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, true);
276 printf ("CodecDownloader::DownloadCompleted (): Invalid state: %i\n", state
);
282 CodecDownloader::AcceptClicked ()
284 LOG_UI ("CodecDownloader::AcceptClicked\n");
286 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress_bar
), 0.0);
287 ToggleProgress (true);
292 case 0: // initial, waiting for user input
293 g_return_if_fail (dl
!= NULL
);
294 SetHeader ("Downloading license agreement...");
296 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, false);
298 dl
->Open ("GET", EULA_URL
, NoPolicy
);
303 case 2: // eula downloaded, waiting for user input
304 g_return_if_fail (dl
!= NULL
);
306 SetHeader ("Downloading the required software...");
309 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, false);
311 env_url
= getenv ("MOONLIGHT_CODEC_URL");
313 dl
->Open ("GET", env_url
, NoPolicy
);
315 dl
->Open ("GET", CODEC_URL
, NoPolicy
);
326 printf ("CodecDownloader::AcceptClicked (): Invalid state: %i\n", state
);
331 // ----- Utilities -----
334 CodecDownloader::CreateDownloader ()
337 dl
= surface
->CreateDownloader ();
338 // since we put up a UI, this might happen if the user has navigated to another page before dismissing the UI
339 // (the surface would be zombified).
340 g_return_if_fail (dl
!= NULL
);
341 dl
->AddHandler (Downloader::DownloadProgressChangedEvent
, DownloadProgressChangedHandler
, this);
342 dl
->AddHandler (Downloader::DownloadFailedEvent
, DownloadFailedHandler
, this);
343 dl
->AddHandler (Downloader::CompletedEvent
, DownloadCompletedHandler
, this);
348 CodecDownloader::DestroyDownloader ()
351 dl
->RemoveHandler (Downloader::DownloadProgressChangedEvent
, DownloadProgressChangedHandler
, this);
352 dl
->RemoveHandler (Downloader::DownloadFailedEvent
, DownloadFailedHandler
, this);
353 dl
->RemoveHandler (Downloader::CompletedEvent
, DownloadCompletedHandler
, this);
360 CodecDownloader::SetHeader (const gchar
*message
)
362 gchar
*message_full
= g_strdup_printf ("<big><b>%s</b></big>", message
);
363 gtk_label_set_markup (GTK_LABEL (header_label
), message_full
);
364 g_free (message_full
);
368 CodecDownloader::SetMessage (const gchar
*message
)
370 gtk_label_set_text (GTK_LABEL (message_label
), message
);
371 gtk_widget_show (message_label
);
375 CodecDownloader::HideMessage ()
377 gtk_widget_hide (message_label
);
381 CodecDownloader::ToggleEula (bool show
)
384 gtk_object_set (GTK_OBJECT (dialog
), "resizable", true, NULL
);
385 gtk_widget_show_all (eula_scrollwindow
);
387 gtk_object_set (GTK_OBJECT (dialog
), "resizable", false, NULL
);
388 gtk_widget_hide (eula_scrollwindow
);
393 CodecDownloader::ToggleProgress (bool show
)
396 gtk_image_set_from_stock (GTK_IMAGE (icon
), GTK_STOCK_SAVE
, GTK_ICON_SIZE_DIALOG
);
397 gtk_widget_hide (dont_ask
);
398 gtk_widget_show_all (progress_bar
);
400 gtk_image_set_from_stock (GTK_IMAGE (icon
), GTK_STOCK_DIALOG_QUESTION
, GTK_ICON_SIZE_DIALOG
);
401 gtk_widget_hide (progress_bar
);
406 CodecDownloader::AdaptToParentWindow ()
408 // try to find a parent for our window
409 // there must be a better way of doing this though :|
410 GList
*toplevels
= gtk_window_list_toplevels ();
411 GList
*current
= toplevels
;
412 GtkWindow
*parent
= NULL
;
414 while (current
!= NULL
) {
415 const char *title
= gtk_window_get_title (GTK_WINDOW (current
->data
));
416 if (title
!= NULL
&& strstr (title
, "Mozilla Firefox") != NULL
) {
417 parent
= GTK_WINDOW (current
->data
);
421 current
= current
->next
;
423 g_list_free (toplevels
);
425 if (parent
!= NULL
) {
426 gtk_window_set_transient_for (GTK_WINDOW (dialog
), parent
);
427 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_CENTER_ON_PARENT
);
429 // If no parent could be found, just center in the screen
430 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_CENTER
);
434 // ----- Dialog Create/Destroy -----
437 CodecDownloader::Show ()
439 if (!is_user_initiated
&& configuration
.GetBooleanValue ("Codecs", "DontInstallMSCodecs")) {
445 fprintf (stderr
, "CodecDownloader::Show (): Can't call Show more than once.\n");
450 gint label_width
= 400;
451 GdkColor white
= {0, 65535, 65535, 65535};
453 // Build HIG Dialog Box
454 dialog
= gtk_dialog_new_with_buttons ("Moonlight Codecs Installer", NULL
, (GtkDialogFlags
)
455 (GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_NO_SEPARATOR
), NULL
);
456 cancel_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
), GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
457 accept_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
), "_Install Codecs", GTK_RESPONSE_OK
);
458 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
);
460 AdaptToParentWindow ();
461 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 5);
462 gtk_object_set (GTK_OBJECT (dialog
), "resizable", false, NULL
);
465 GtkWidget
*hbox
= gtk_hbox_new (false, 12);
466 gtk_container_set_border_width (GTK_CONTAINER (hbox
), 5);
467 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), hbox
, true, true, 0);
470 icon
= gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION
, GTK_ICON_SIZE_DIALOG
);
471 gtk_misc_set_alignment (GTK_MISC (icon
), 0.5f
, 0.0f
);
472 gtk_box_pack_start (GTK_BOX (hbox
), icon
, false, false, 0);
474 // Contents container
475 vbox
= gtk_vbox_new (false, 0);
476 gtk_box_set_spacing (GTK_BOX (vbox
), 10);
477 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, true, true, 0);
480 header_label
= gtk_label_new (NULL
);
481 SetHeader ("Would you like to install the required add-on to play the content on this page?");
482 gtk_label_set_line_wrap (GTK_LABEL (header_label
), true);
483 gtk_label_set_justify (GTK_LABEL (header_label
), GTK_JUSTIFY_LEFT
);
484 gtk_misc_set_alignment (GTK_MISC (header_label
), 0.0f
, 0.5f
);
485 gtk_widget_set_size_request (header_label
, label_width
, -1);
486 gtk_box_pack_start (GTK_BOX (vbox
), header_label
, false, false, 0);
489 message_label
= gtk_label_new (NULL
);
490 SetMessage ("This page requires the Microsoft Media Pack "
491 "to be installed to play multimedia content.\n\n"
492 "If you choose to install it, the software will be "
493 "automatically downloaded and installed "
494 "from Microsoft's web site.");
495 gtk_label_set_line_wrap (GTK_LABEL (message_label
), true);
496 gtk_label_set_justify (GTK_LABEL (message_label
), GTK_JUSTIFY_LEFT
);
497 gtk_misc_set_alignment (GTK_MISC (message_label
), 0.0f
, 0.5f
);
498 gtk_widget_set_size_request (message_label
, label_width
, -1);
499 gtk_box_pack_start (GTK_BOX (vbox
), message_label
, false, false, 0);
501 dont_ask
= gtk_check_button_new_with_label ("Do not ask me to install this add-on again");
502 gtk_box_pack_start (GTK_BOX (vbox
), dont_ask
, false, false, 0);
505 progress_bar
= gtk_progress_bar_new ();
506 gtk_box_pack_start (GTK_BOX (vbox
), progress_bar
, false, false, 0);
509 eula_view
= gtk_label_new (NULL
);
510 gtk_label_set_selectable (GTK_LABEL (eula_view
), TRUE
);
511 gtk_label_set_line_wrap (GTK_LABEL (eula_view
), TRUE
);
513 eula_scrollwindow
= gtk_scrolled_window_new (NULL
, NULL
);
514 eula_evtbox
= gtk_event_box_new ();
516 gtk_widget_modify_bg (GTK_WIDGET (eula_evtbox
), GTK_STATE_NORMAL
, &white
);
517 gtk_container_add (GTK_CONTAINER (eula_evtbox
), eula_view
);
519 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (eula_scrollwindow
), GTK_SHADOW_IN
);
520 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (eula_scrollwindow
), eula_evtbox
);
521 gtk_widget_set_size_request (eula_scrollwindow
, -1, 225);
522 gtk_box_pack_end (GTK_BOX (vbox
), eula_scrollwindow
, true, true, 0);
525 g_signal_connect (G_OBJECT (dialog
), "response", G_CALLBACK (ResponseEventHandler
), this);
527 gtk_object_set (GTK_OBJECT (accept_button
), "has-focus", true, "has-default", true, NULL
);
529 gtk_widget_show_all (dialog
);
530 ToggleProgress (false);
533 ref (); // We manage our lifetime ourself
538 CodecDownloader::Close ()
540 LOG_UI ("CodecDownloader::Close ()\n");
544 DestroyDownloader ();
547 gtk_widget_destroy (dialog
);