in plugin/:
[moon.git] / src / pipeline-ui.cpp
blob86303093621c7c6aa039da640811e34217ccd8a2
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * pipeline-ui.cpp:
5 * Contact:
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.
13 #include <config.h>
15 #include <glib/gstdio.h>
16 #include <fcntl.h>
17 #include <errno.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"
28 #include "utils.h"
29 #include "pipeline.h"
30 #include "debug.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)
39 surface = surf;
40 eula = NULL;
41 state = 0;
42 dl = NULL;
43 dialog = NULL;
44 vbox = NULL;
45 header_label = NULL;
46 message_label = NULL;
47 progress_bar = NULL;
48 eula_scrollwindow = NULL;
49 eula_view = NULL;
50 accept_button = NULL;
51 cancel_button = NULL;
52 icon = NULL;
53 dont_ask = NULL;
56 CodecDownloader::~CodecDownloader ()
58 g_free (eula);
59 if (dl != NULL) {
60 dl->unref ();
62 running = false;
65 void
66 CodecDownloader::ShowUI (Surface *surface)
68 g_return_if_fail (surface != NULL);
70 if (running) {
71 return;
74 if (!(moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS))
75 return;
77 CodecDownloader *cd = new CodecDownloader (surface);
78 cd->Show ();
79 cd->unref ();
82 // ----- Event Proxies -----
84 void
85 CodecDownloader::ResponseEventHandler (GtkDialog *dialog, gint response, gpointer data)
87 ((CodecDownloader *) data)->ResponseEvent (dialog, (GtkResponseType)response);
90 void
91 CodecDownloader::DownloadFailedHandler (EventObject *sender, EventArgs *args, gpointer closure)
93 ((CodecDownloader *) closure)->DownloadFailed (sender, args);
96 void
97 CodecDownloader::DownloadCompletedHandler (EventObject *sender, EventArgs *args, gpointer closure)
99 ((CodecDownloader *) closure)->DownloadCompleted (sender, args);
102 void
103 CodecDownloader::DownloadProgressChangedHandler (EventObject *sender, EventArgs *args, gpointer closure)
105 ((CodecDownloader *) closure)->DownloadProgressChanged (sender, args);
108 // ----- Event Handlers -----
110 void
111 CodecDownloader::ResponseEvent (GtkDialog *dialog, GtkResponseType response)
113 LOG_UI ("CodecDownloader::ResponseEvent (%d)\n", response);
114 SetCurrentDeployment ();
116 switch (response) {
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 ();
124 state = 5;
125 Close ();
126 return;
127 case GTK_RESPONSE_DELETE_EVENT:
128 Close ();
129 return;
130 case GTK_RESPONSE_OK:
131 AcceptClicked ();
132 return;
133 default:
134 return;
138 void
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);
147 void
148 CodecDownloader::DownloadFailed (EventObject *sender, EventArgs *args)
150 ErrorEventArgs *eea = (ErrorEventArgs *) args;
151 gchar *msg;
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);
169 g_free (msg);
171 state = 6;
174 bool
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);
181 if (!sw)
182 return false;
184 MonoImage *image = mono_assembly_get_image (sw);
185 if (!image)
186 return false;
188 MonoClass *klass = mono_class_from_name (image, "Mono", "Helper");
189 if (!klass)
190 return false;
192 moon_codec_integrity = mono_class_get_method_from_name (klass, "CheckFileIntegrity", 1);
193 if (!moon_codec_integrity)
194 return false;
197 void *params [1];
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);
202 if (exc)
203 return false;
205 return (bool) (*(MonoBoolean *) mono_object_unbox (ret));
208 void
209 CodecDownloader::DownloadCompleted (EventObject *sender, EventArgs *args)
211 gchar *downloaded_file = NULL;
212 gchar *codec_path = NULL;
213 gchar *codec_dir = NULL;
214 int codec_fd = 0;
215 gint64 size;
217 LOG_UI ("CodecDownloader::DownloadCompleted ()\n");
219 ToggleProgress (false);
221 switch (state) {
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.");
228 ToggleEula (true);
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);
235 state = 2;
236 break;
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);
243 errno = 0;
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));
253 } else {
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 ();
260 g_free (codec_path);
261 g_free (codec_dir);
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);
268 state = 4;
269 break;
270 default:
271 printf ("CodecDownloader::DownloadCompleted (): Invalid state: %i\n", state);
272 break;
276 void
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);
284 CreateDownloader ();
286 switch (state) {
287 case 0: // initial, waiting for user input
288 g_return_if_fail (dl != NULL);
289 SetHeader ("Downloading license agreement...");
290 HideMessage ();
291 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, false);
293 dl->Open ("GET", EULA_URL, NoPolicy);
294 dl->Send ();
296 state = 1;
297 break;
298 case 2: // eula downloaded, waiting for user input
299 g_return_if_fail (dl != NULL);
300 char *env_url;
301 SetHeader ("Downloading the required software...");
302 HideMessage ();
303 ToggleEula (false);
304 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, false);
306 env_url = getenv ("MOONLIGHT_CODEC_URL");
307 if (env_url != NULL)
308 dl->Open ("GET", env_url, NoPolicy);
309 else {
310 char *codec_url = g_strdup_printf("%s", CODEC_URL);
311 dl->Open ("GET", codec_url, NoPolicy);
312 g_free (codec_url);
314 dl->Send ();
316 state = 3;
317 break;
318 case 4:
319 case 6:
320 Close ();
321 break;
322 default:
323 printf ("CodecDownloader::AcceptClicked (): Invalid state: %i\n", state);
324 break;
328 // ----- Utilities -----
330 void
331 CodecDownloader::CreateDownloader ()
333 if (dl == NULL) {
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);
344 void
345 CodecDownloader::DestroyDownloader ()
347 if (dl != NULL) {
348 dl->RemoveHandler (Downloader::DownloadProgressChangedEvent, DownloadProgressChangedHandler, this);
349 dl->RemoveHandler (Downloader::DownloadFailedEvent, DownloadFailedHandler, this);
350 dl->RemoveHandler (Downloader::CompletedEvent, DownloadCompletedHandler, this);
351 dl->unref ();
352 dl = NULL;
356 void
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);
364 void
365 CodecDownloader::SetMessage (const gchar *message)
367 gtk_label_set_text (GTK_LABEL (message_label), message);
368 gtk_widget_show (message_label);
371 void
372 CodecDownloader::HideMessage ()
374 gtk_widget_hide (message_label);
377 void
378 CodecDownloader::ToggleEula (bool show)
380 if (show) {
381 gtk_widget_show_all (eula_scrollwindow);
382 } else {
383 gtk_widget_hide (eula_scrollwindow);
387 void
388 CodecDownloader::ToggleProgress (bool show)
390 if (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);
394 } else {
395 gtk_image_set_from_stock (GTK_IMAGE (icon), GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
396 gtk_widget_hide (progress_bar);
400 void
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);
413 break;
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);
423 } else {
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 -----
431 void
432 CodecDownloader::Show ()
434 if (configuration.GetBooleanValue ("Codecs", "DontInstallMSCodecs")) {
435 state = 5;
436 return;
439 if (state != 0) {
440 fprintf (stderr, "CodecDownloader::Show (): Can't call Show more than once.\n");
441 state = 6;
442 return;
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);
459 // HIG HBox
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);
464 // Message box icon
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);
474 // Header Label
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);
483 // Secondary Label
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);
499 // Other elements
500 progress_bar = gtk_progress_bar_new ();
501 gtk_box_pack_start (GTK_BOX (vbox), progress_bar, false, false, 0);
503 // EULA
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);
519 // Connect and go
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);
526 ToggleEula (false);
528 ref (); // We manage our lifetime ourself
529 running = true;
532 void
533 CodecDownloader::Close ()
535 LOG_UI ("CodecDownloader::Close ()\n");
537 if (dl != NULL) {
538 dl->Abort ();
539 DestroyDownloader ();
542 gtk_widget_destroy (dialog);
543 unref ();
544 running = false;