* MoonlightTypeConverter.cs: Convert CacheMode's from strings.
[moon.git] / src / runtime.cpp
blobd517ed5c7b5008a4676248eff9d179b655227bc0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * runtime.cpp: Core surface
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.
14 #include <config.h>
16 #include <gtk/gtk.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <malloc.h>
21 #include <math.h>
23 #define Visual _XxVisual
24 #define Region _XxRegion
25 #include <gdk/gdkx.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <cairo-xlib.h>
28 #undef Visual
29 #undef Region
31 #include "runtime.h"
32 #include "canvas.h"
33 #include "color.h"
34 #include "shape.h"
35 #include "transform.h"
36 #include "animation.h"
37 #include "downloader.h"
38 #include "frameworkelement.h"
39 #include "textblock.h"
40 #include "media.h"
41 #include "stylus.h"
42 #include "rect.h"
43 #include "panel.h"
44 #include "value.h"
45 #include "namescope.h"
46 #include "xaml.h"
47 #include "dirty.h"
48 #include "fullscreen.h"
49 #include "incomplete-support.h"
50 #include "utils.h"
51 #include "window-gtk.h"
52 #include "timemanager.h"
54 #include "contentcontrol.h"
55 #include "usercontrol.h"
56 #include "deployment.h"
57 #include "grid.h"
58 #include "cbinding.h"
59 #include "tabnavigationwalker.h"
61 //#define DEBUG_INVALIDATE 1
62 //#define RENDER_INDIVIDUALLY 1
63 #define DEBUG_REFCNT 0
65 #define CAIRO_CLIP 0
66 #define TIME_CLIP 0
67 #define TIME_REDRAW 1
69 #define NO_EVENT_ID -1
71 bool Surface::main_thread_inited = false;
72 pthread_t Surface::main_thread = 0;
74 static bool inited = false;
75 static bool g_type_inited = false;
76 static GList* surface_list = NULL;
77 guint32 moonlight_flags = 0;
78 #if DEBUG || LOGGING
79 guint32 debug_flags_ex = 0;
80 guint32 debug_flags = 0;
81 #endif
84 struct env_options {
85 const char *name;
86 guint64 flag;
87 bool set;
90 static struct env_options overrides[] = {
91 // There's no "ms-codecs=yes" option to not allow enabling them from the command line.
92 { "ms-codecs=no", RUNTIME_INIT_ENABLE_MS_CODECS, false },
93 { "ffmpeg-codecs=no", RUNTIME_INIT_DISABLE_FFMPEG_CODECS, true },
94 { "ffmpeg-codecs=yes", RUNTIME_INIT_DISABLE_FFMPEG_CODECS, false },
95 { "timesource=manual", RUNTIME_INIT_MANUAL_TIMESOURCE, true },
96 { "timesource=system", RUNTIME_INIT_MANUAL_TIMESOURCE, false },
97 { "expose=show", RUNTIME_INIT_SHOW_EXPOSE, true },
98 { "expose=hide", RUNTIME_INIT_SHOW_EXPOSE, false },
99 { "clipping=show", RUNTIME_INIT_SHOW_CLIPPING, true },
100 { "clipping=hide", RUNTIME_INIT_SHOW_CLIPPING, false },
101 { "bbox=show", RUNTIME_INIT_SHOW_BOUNDING_BOXES, true },
102 { "bbox=hide", RUNTIME_INIT_SHOW_BOUNDING_BOXES, false },
103 { "textbox=show", RUNTIME_INIT_SHOW_TEXTBOXES, true },
104 { "textbox=hide", RUNTIME_INIT_SHOW_TEXTBOXES, false },
105 { "fps=show", RUNTIME_INIT_SHOW_FPS, true },
106 { "fps=hide", RUNTIME_INIT_SHOW_FPS, false },
107 /* FIXME disabled for layout clipping */
108 { "render=ftb", RUNTIME_INIT_RENDER_FRONT_TO_BACK, false },
109 { "render=btf", RUNTIME_INIT_RENDER_FRONT_TO_BACK, true },
110 { "cache=show", RUNTIME_INIT_SHOW_CACHE_SIZE, true },
111 { "cache=hide", RUNTIME_INIT_SHOW_CACHE_SIZE, false },
112 { "converter=default", RUNTIME_INIT_FFMPEG_YUV_CONVERTER, false },
113 { "converter=ffmpeg", RUNTIME_INIT_FFMPEG_YUV_CONVERTER, true },
114 { "shapecache=yes", RUNTIME_INIT_USE_SHAPE_CACHE, true },
115 { "shapecache=no", RUNTIME_INIT_USE_SHAPE_CACHE, false },
116 { "updatepos=yes", RUNTIME_INIT_USE_UPDATE_POSITION, true },
117 { "updatepos=no", RUNTIME_INIT_USE_UPDATE_POSITION, false },
118 { "windowless=yes", RUNTIME_INIT_ALLOW_WINDOWLESS, true },
119 { "windowless=no", RUNTIME_INIT_ALLOW_WINDOWLESS, false },
120 { "audio=alsa", RUNTIME_INIT_AUDIO_ALSA, true },
121 { "audio=alsa-mmap", RUNTIME_INIT_AUDIO_ALSA_MMAP, true },
122 { "audio=alsa-rw", RUNTIME_INIT_AUDIO_ALSA_RW, true },
123 { "audio=pulseaudio", RUNTIME_INIT_AUDIO_PULSE, true },
124 { "idlehint=yes", RUNTIME_INIT_USE_IDLE_HINT, false },
125 { "idlehint=no", RUNTIME_INIT_USE_IDLE_HINT, true },
126 /* default to the image backend until cairo is actually faster that way */
127 { "backend=xlib", RUNTIME_INIT_USE_BACKEND_XLIB, false },
128 { "backend=image", RUNTIME_INIT_USE_BACKEND_XLIB, true },
129 { "keepmedia=no", RUNTIME_INIT_KEEP_MEDIA, false },
130 { "keepmedia=yes", RUNTIME_INIT_KEEP_MEDIA, true },
131 { "allimages=no", RUNTIME_INIT_ALL_IMAGE_FORMATS, false },
132 { "allimages=yes", RUNTIME_INIT_ALL_IMAGE_FORMATS, true },
135 #define RUNTIME_INIT_DESKTOP (RUNTIME_INIT_PANGO_TEXT_LAYOUT | RUNTIME_INIT_RENDER_FRONT_TO_BACK | RUNTIME_INIT_USE_UPDATE_POSITION | RUNTIME_INIT_USE_SHAPE_CACHE | RUNTIME_INIT_USE_IDLE_HINT | RUNTIME_INIT_USE_BACKEND_XLIB | RUNTIME_INIT_ALL_IMAGE_FORMATS | RUNTIME_INIT_OUT_OF_BROWSER | RUNTIME_INIT_DESKTOP_EXTENSIONS)
136 #define RUNTIME_INIT_BROWSER (RUNTIME_INIT_RENDER_FRONT_TO_BACK | RUNTIME_INIT_USE_UPDATE_POSITION | RUNTIME_INIT_USE_SHAPE_CACHE | RUNTIME_INIT_ALLOW_WINDOWLESS | RUNTIME_INIT_USE_IDLE_HINT | RUNTIME_INIT_USE_BACKEND_XLIB | RUNTIME_INIT_ENABLE_MS_CODECS | RUNTIME_INIT_CREATE_ROOT_DOMAIN)
138 #if DEBUG || LOGGING
139 static struct env_options debugs[] = {
140 { "alsa", RUNTIME_DEBUG_ALSA, true },
141 { "audio", RUNTIME_DEBUG_AUDIO, true },
142 { "pulse", RUNTIME_DEBUG_PULSE, true },
143 { "httpstreaming", RUNTIME_DEBUG_HTTPSTREAMING, true },
144 { "markers", RUNTIME_DEBUG_MARKERS, true },
145 { "mms", RUNTIME_DEBUG_MMS, true },
146 { "mediaplayer", RUNTIME_DEBUG_MEDIAPLAYER, true },
147 { "pipeline", RUNTIME_DEBUG_PIPELINE, true },
148 { "pipeline-error", RUNTIME_DEBUG_PIPELINE_ERROR, true },
149 { "framereaderloop", RUNTIME_DEBUG_FRAMEREADERLOOP, true },
150 { "ui", RUNTIME_DEBUG_UI, true },
151 { "ffmpeg", RUNTIME_DEBUG_FFMPEG, true },
152 { "codecs", RUNTIME_DEBUG_CODECS, true },
153 { "dependencyobject", RUNTIME_DEBUG_DP, true },
154 { "downloader", RUNTIME_DEBUG_DOWNLOADER, true },
155 { "font", RUNTIME_DEBUG_FONT, true },
156 { "layout", RUNTIME_DEBUG_LAYOUT, true },
157 { "media", RUNTIME_DEBUG_MEDIA, true },
158 { "mediaelement", RUNTIME_DEBUG_MEDIAELEMENT, true },
159 { "msi", RUNTIME_DEBUG_MSI, true },
160 { "buffering", RUNTIME_DEBUG_BUFFERING, true },
161 { "asf", RUNTIME_DEBUG_ASF, true },
162 { "playlist", RUNTIME_DEBUG_PLAYLIST, true },
163 { "text", RUNTIME_DEBUG_TEXT, true },
164 { "xaml", RUNTIME_DEBUG_XAML, true },
165 { "deployment", RUNTIME_DEBUG_DEPLOYMENT, true },
166 { "mp3", RUNTIME_DEBUG_MP3, true },
167 { "asf", RUNTIME_DEBUG_ASF, true },
168 { "value", RUNTIME_DEBUG_VALUE, true },
169 { NULL, 0, false }
172 static struct env_options debug_extras[] = {
173 { "alsa-ex", RUNTIME_DEBUG_ALSA_EX, true },
174 { "audio-ex", RUNTIME_DEBUG_AUDIO_EX, true },
175 { "pulse-ex", RUNTIME_DEBUG_PULSE_EX, true },
176 { "markers-ex", RUNTIME_DEBUG_MARKERS_EX, true },
177 { "mediaplayer-ex", RUNTIME_DEBUG_MEDIAPLAYER_EX, true },
178 { "mediaelement-ex", RUNTIME_DEBUG_MEDIAELEMENT_EX, true },
179 { "playlist-ex", RUNTIME_DEBUG_PLAYLIST_EX, true },
180 { "pipeline-ex", RUNTIME_DEBUG_PIPELINE_EX, true },
181 { NULL, 0, false }
183 #endif
187 #define RENDER_EXPOSE (moonlight_flags & RUNTIME_INIT_SHOW_EXPOSE)
189 static bool
190 running_on_nvidia ()
192 int event, error, opcode;
194 Display *display = XOpenDisplay (NULL);
195 bool result = XQueryExtension (display, "NV-GLX", &opcode, &event, &error);
196 XCloseDisplay (display);
198 return result;
201 static void
202 fps_report_default (Surface *surface, int nframes, float nsecs, void *user_data)
204 printf ("Rendered %d frames in %.3fs = %.3f FPS\n", nframes, nsecs, nframes / nsecs);
207 static void
208 cache_report_default (Surface *surface, long bytes, void *user_data)
210 printf ("Cache size is ~%.3f MB\n", bytes / 1048576.0);
213 GList *
214 runtime_get_surface_list (void)
216 if (!Surface::InMainThread ()) {
217 g_warning ("This method can be only called from the main thread!\n");
218 return NULL;
221 return surface_list;
224 static cairo_t *
225 runtime_cairo_create (GdkWindow *drawable, GdkVisual *visual, bool native)
227 int width, height;
228 cairo_surface_t *surface;
229 cairo_t *cr;
231 gdk_drawable_get_size (drawable, &width, &height);
233 if (native)
234 surface = cairo_xlib_surface_create (gdk_x11_drawable_get_xdisplay (drawable),
235 gdk_x11_drawable_get_xid (drawable),
236 GDK_VISUAL_XVISUAL (visual),
237 width, height);
238 else
239 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
241 cr = cairo_create (surface);
242 cairo_surface_destroy (surface);
244 return cr;
247 static gboolean
248 flags_can_be_modifed (void)
250 if (g_list_length (surface_list) != 0) {
251 g_warning ("Flags can be dynamically modified only when there are no surfaces created!");
252 return FALSE;
253 } else if (inited == FALSE) {
254 g_warning ("Runtime has not been initialized yet, your flags will be overriden!");
255 return FALSE;
256 } else
257 return TRUE;
260 void
261 runtime_flags_set_manual_timesource (gboolean flag)
263 if (flags_can_be_modifed ())
264 moonlight_flags |= RUNTIME_INIT_MANUAL_TIMESOURCE;
267 void
268 runtime_flags_set_use_shapecache (gboolean flag)
270 if (flags_can_be_modifed ())
271 moonlight_flags |= RUNTIME_INIT_USE_SHAPE_CACHE;
274 void
275 runtime_flags_set_show_fps (gboolean flag)
277 if (flags_can_be_modifed ())
278 moonlight_flags |= RUNTIME_INIT_SHOW_FPS;
281 /* FIXME More flag setters here */
283 Surface::Surface (MoonWindow *window)
284 : EventObject (Type::SURFACE)
286 GetDeployment ()->SetSurface (this);
288 main_thread = pthread_self ();
289 main_thread_inited = true;
291 zombie = false;
292 downloader_context = NULL;
293 downloaders = NULL;
294 background_color = NULL;
295 cursor = MouseCursorDefault;
296 mouse_event = NULL;
298 background_color = new Color (1, 1, 1, 0);
300 time_manager = new TimeManager ();
301 time_manager->Start ();
302 ticked_after_attach = false;
304 fullscreen_window = NULL;
305 normal_window = active_window = window;
306 if (active_window->IsFullScreen())
307 g_warning ("Surfaces cannot be initialized with fullscreen windows.");
308 window->SetSurface (this);
310 layers = new HitTestCollection ();
311 toplevel = NULL;
312 input_list = new List ();
313 captured = NULL;
315 focused_element = NULL;
316 focus_changed_events = new List ();
318 full_screen = false;
319 first_user_initiated_event = false;
320 user_initiated_event = false;
322 zoom_factor = 1.0;
324 incomplete_support_message = NULL;
325 full_screen_message = NULL;
326 source_location = NULL;
328 fps_report = fps_report_default;
329 fps_data = NULL;
331 frames = 0;
332 fps_nframes = 0;
333 fps_start = 0;
335 cache_report = cache_report_default;
336 cache_data = NULL;
338 cache_size_in_bytes = 0;
339 cache_size_ticker = 0;
340 cache_size_multiplier = -1;
342 expose_handoff = NULL;
343 expose_handoff_data = NULL;
344 expose_handoff_last_timespan = G_MAXINT64;
346 emittingMouseEvent = false;
347 pendingCapture = NULL;
348 pendingReleaseCapture = false;
350 #ifdef DEBUG
351 debug_selected_element = NULL;
352 #endif
354 up_dirty = new DirtyLists (true);
355 down_dirty = new DirtyLists (false);
357 surface_list = g_list_append (surface_list, this);
360 Surface::~Surface ()
362 time_manager->RemoveHandler (TimeManager::RenderEvent, render_cb, this);
363 time_manager->RemoveHandler (TimeManager::UpdateInputEvent, update_input_cb, this);
365 if (toplevel) {
366 toplevel->SetIsAttached (false);
367 toplevel->unref ();
370 #if DEBUG
371 if (debug_selected_element) {
372 debug_selected_element->unref ();
373 debug_selected_element = NULL;
375 #endif
377 HideFullScreenMessage ();
379 delete focus_changed_events;
380 delete input_list;
382 g_free (source_location);
384 if (fullscreen_window)
385 delete fullscreen_window;
387 if (normal_window)
388 delete normal_window;
390 delete background_color;
392 time_manager->unref ();
394 delete up_dirty;
395 delete down_dirty;
397 delete downloaders;
398 layers->unref ();
400 surface_list = g_list_remove (surface_list, this);
403 void
404 Surface::Dispose ()
406 if (toplevel) {
407 toplevel->SetIsAttached (false);
408 toplevel->Dispose ();
411 EventObject::Dispose ();
414 void
415 Surface::Zombify ()
417 time_manager->Shutdown ();
418 DetachDownloaders ();
419 zombie = true;
422 MoonWindow *
423 Surface::DetachWindow ()
425 MoonWindow *result;
427 /* We only detach the normal window. TODO: Testing requires to see what happens if DetachWindow is called (changing plugin.source) in fullscreen mode. */
428 if (active_window == normal_window)
429 active_window = NULL;
430 result = normal_window;
431 normal_window = NULL;
433 return result;
436 void
437 Surface::SetCursor (MouseCursor new_cursor)
439 if (new_cursor != cursor) {
440 cursor = new_cursor;
442 active_window->SetCursor (cursor);
446 TimeManager *
447 Surface::GetTimeManagerReffed ()
449 TimeManager *result;
450 time_manager_mutex.Lock ();
451 result = time_manager;
452 if (result)
453 result->ref ();
454 time_manager_mutex.Unlock ();
455 return result;
458 void
459 Surface::EmitFocusChangeEventsAsync (EventObject *sender)
461 ((Surface *)sender)->EmitFocusChangeEvents ();
464 void
465 Surface::Attach (UIElement *element)
467 bool first = false;
469 #if DEBUG
470 // Attach must be called with NULL to clear out the old
471 // element before attaching element, otherwise the new element
472 // might get loaded with data from the old element (when
473 // parsing xaml ticks will get added to the timemanager of the
474 // surface, if the old element isn't gone when the new element
475 // is parsed, the ticks will be added to the old timemanager).
476 if (toplevel != NULL && element != NULL)
477 g_warning ("Surface::Attach (NULL) should be called to clear out the old toplevel before adding a new element.");
478 #endif
480 #if SANITY
481 if (element != NULL && element->GetDeployment () != GetDeployment ())
482 g_warning ("Surface::Attach (%p): trying to attach an object created on the deployment %p on a surface whose deployment is %p\n", element, element->GetDeployment (), GetDeployment ());
483 if (GetDeployment () != Deployment::GetCurrent ())
484 g_warning ("Surface::Attach (%p): current deployment is %p, surface deployment is %p\n", element, GetDeployment (), Deployment::GetCurrent ());
485 #endif
488 if (toplevel) {
489 toplevel->RemoveHandler (UIElement::LoadedEvent, toplevel_loaded, this);
490 DetachLayer (toplevel);
491 time_manager->RemoveHandler (TimeManager::RenderEvent, render_cb, this);
492 time_manager->RemoveHandler (TimeManager::UpdateInputEvent, update_input_cb, this);
493 time_manager->Stop ();
494 int maxframerate = time_manager->GetMaximumRefreshRate ();
495 toplevel->unref ();
496 time_manager_mutex.Lock ();
497 time_manager->unref ();
498 time_manager = new TimeManager ();
499 time_manager_mutex.Unlock ();
500 time_manager->AddHandler (TimeManager::RenderEvent, render_cb, this);
501 time_manager->AddHandler (TimeManager::UpdateInputEvent, update_input_cb, this);
502 time_manager->SetMaximumRefreshRate (maxframerate);
503 time_manager->NeedRedraw ();
504 time_manager->Start ();
505 } else
506 first = true;
508 if (!element) {
509 DetachDownloaders ();
511 if (first)
512 active_window->EnableEvents (first);
514 if (active_window)
515 active_window->Invalidate();
517 toplevel = NULL;
518 return;
521 if (!element->Is (Type::UIELEMENT)) {
522 printf ("Surface::Attach Unsupported toplevel %s\n", element->GetTypeName ());
523 return;
526 UIElement *new_toplevel = element;
527 new_toplevel->ref ();
529 // make sure we have a namescope at the toplevel so that names
530 // can be registered/resolved properly.
531 if (NameScope::GetNameScope (new_toplevel) == NULL) {
532 NameScope::SetNameScope (new_toplevel, new NameScope());
535 // First time we connect the surface, start responding to events
536 if (first && active_window)
537 active_window->EnableEvents (first);
539 if (zombie)
540 return;
542 toplevel = new_toplevel;
544 this->ref ();
545 toplevel->AddHandler (UIElement::LoadedEvent, toplevel_loaded, this, (GDestroyNotify)event_object_unref);
547 AttachLayer (toplevel);
549 ticked_after_attach = false;
550 time_manager->RemoveTickCall (tick_after_attach_reached, this);
551 time_manager->AddTickCall (tick_after_attach_reached, this);
553 const char *runtime_version = GetDeployment()->GetRuntimeVersion ();
555 if (first && runtime_version
556 && (!strncmp ("3.", runtime_version, 2)
557 || !strncmp ("4.", runtime_version, 2))) {
558 // we're running a SL app, let's warn the user about
559 // moonlight's incomplete support.
560 ShowIncompleteSilverlightSupportMessage ();
564 void
565 Surface::tick_after_attach_reached (EventObject *data)
567 Surface *surface = (Surface*)data;
569 surface->ticked_after_attach = true;
570 surface->Emit (Surface::LoadEvent);
573 void
574 Surface::toplevel_loaded (EventObject *sender, EventArgs *args, gpointer closure)
576 ((Surface*)closure)->ToplevelLoaded ((UIElement*)sender);
579 void
580 Surface::ToplevelLoaded (UIElement *element)
582 if (element == toplevel) {
583 toplevel->RemoveHandler (UIElement::LoadedEvent, toplevel_loaded, this);
585 // FIXME: If the element is supposed to be focused, FocusElement (element)
586 // should be used. I think this is unnecessary anyway.
587 //if (active_window && active_window->HasFocus())
588 // element->EmitGotFocus ();
591 // If the did not get a size specified
593 if (normal_window && normal_window->GetWidth() == 0 && normal_window->GetHeight() == 0 && toplevel) {
595 * this should only be hit in the nonplugin case ans is
596 * simply here to give a reasonable default size
598 Value *vh, *vw;
599 vw = toplevel->GetValue (FrameworkElement::WidthProperty);
600 vh = toplevel->GetValue (FrameworkElement::HeightProperty);
601 if (vh || vw)
602 normal_window->Resize (MAX (vw ? (int)vw->AsDouble () : 0, 0),
603 MAX (vh ? (int)vh->AsDouble () : 0, 0));
606 Emit (ResizeEvent);
608 element->UpdateTotalRenderVisibility ();
609 element->UpdateTotalHitTestVisibility ();
610 element->FullInvalidate (true);
612 // we call this two here so that the layout pass proceeds when
613 // we next process the dirty list.
614 element->InvalidateMeasure ();
618 void
619 Surface::AttachLayer (UIElement *layer)
621 if (layer == toplevel)
622 layers->Insert (0, Value(layer));
623 else
624 layers->Add (Value (layer));
626 layer->SetIsAttached (true);
627 layer->FullInvalidate (true);
628 layer->InvalidateMeasure ();
629 layer->WalkTreeForLoadedHandlers (NULL, false, false);
630 Deployment::GetCurrent()->PostLoaded ();
633 void
634 Surface::DetachLayer (UIElement *layer)
636 layers->Remove (Value (layer));
637 layer->SetIsAttached (false);
638 if (active_window)
639 Invalidate (layer->GetBounds ());
642 void
643 Surface::Invalidate (Rect r)
645 active_window->Invalidate (r);
648 void
649 Surface::ProcessUpdates ()
651 active_window->ProcessUpdates();
654 void
655 Surface::Paint (cairo_t *ctx, int x, int y, int width, int height)
657 Rect r = Rect (x, y, width, height);
658 Region region = Region (r);
659 Paint (ctx, &region);
662 void
663 Surface::Paint (cairo_t *ctx, Region *region)
665 for (int i = 0; i < layers->GetCount (); i++) {
666 UIElement *layer = layers->GetValueAt (i)->AsUIElement ();
667 layer->Paint (ctx, region, NULL);
670 #ifdef DEBUG
671 if (debug_selected_element) {
672 Rect bounds = debug_selected_element->GetSubtreeBounds();
673 // printf ("debug_selected_element is %s\n", debug_selected_element->GetName());
674 // printf ("bounds is %g %g %g %g\n", bounds.x, bounds.y, bounds.w, bounds.h);
675 cairo_save (ctx);
676 //RenderClipPath (ctx);
677 cairo_new_path (ctx);
678 cairo_identity_matrix (ctx);
679 cairo_set_source_rgba (ctx, 1.0, 0.5, 0.2, 1.0);
680 cairo_set_line_width (ctx, 1);
681 cairo_rectangle (ctx, bounds.x, bounds.y, bounds.width, bounds.height);
682 cairo_stroke (ctx);
683 cairo_restore (ctx);
685 #endif
689 // This will resize the surface (merely a convenience function for
690 // resizing the widget area that we have.
692 // This will not change the Width and Height properties of the
693 // toplevel element, if you want that, you must do that yourself
695 void
696 Surface::Resize (int width, int height)
698 if (width == normal_window->GetWidth()
699 && height == normal_window->GetHeight())
700 return;
702 normal_window->Resize (width, height);
705 void
706 Surface::EmitSourceDownloadComplete ()
708 Emit (SourceDownloadCompleteEvent, NULL);
711 void
712 Surface::EmitSourceDownloadProgressChanged (DownloadProgressEventArgs *args)
714 Emit (SourceDownloadProgressChangedEvent, args);
717 void
718 Surface::EmitError (ErrorEventArgs *args)
720 Emit (ErrorEvent, args);
723 void
724 Surface::EmitError (int number, int code, const char *message)
726 EmitError (new ErrorEventArgs ((ErrorEventArgsType)number,
727 MoonError (MoonError::EXCEPTION, code, message)));
730 void
731 Surface::Realloc ()
733 for (int i = 0; i < layers->GetCount (); i++) {
734 UIElement *layer = layers->GetValueAt (i)->AsUIElement ();
736 layer->InvalidateMeasure ();
737 //layer->UpdateBounds();
741 void
742 Surface::SetFullScreen (bool value)
744 if (value && !IsUserInitiatedEvent ()) {
745 g_warning ("You're not allowed to switch to fullscreen from where you're doing it.");
746 return;
749 UpdateFullScreen (value);
752 void
753 Surface::SetZoomFactor (double value)
755 // FIXME: implement surface zooming
756 zoom_factor = value;
758 Emit (ZoomedEvent, new EventArgs ());
761 void
762 Surface::SetUserInitiatedEvent (bool value)
764 EmitFocusChangeEvents ();
765 first_user_initiated_event = first_user_initiated_event | value;
766 user_initiated_event = value;
769 bool
770 Surface::IsTopLevel (UIElement* top)
772 if (top == NULL)
773 return false;
775 bool ret = top == full_screen_message;
777 for (int i = 0; i < layers->GetCount () && !ret; i++)
778 ret = layers->GetValueAt (i)->AsUIElement () == top;
780 return ret;
783 void
784 Surface::ShowIncompleteSilverlightSupportMessage ()
786 g_return_if_fail (incomplete_support_message == NULL);
788 Type::Kind dummy;
789 XamlLoader *loader = new XamlLoader (NULL, INCOMPLETE_SUPPORT_MESSAGE, this);
790 DependencyObject* message = loader->CreateDependencyObjectFromString (INCOMPLETE_SUPPORT_MESSAGE, false, &dummy);
791 delete loader;
793 if (!message) {
794 g_warning ("Unable to create incomplete support message.\n");
795 return;
798 if (!message->Is (Type::FRAMEWORKELEMENT)) {
799 g_warning ("Unable to create incomplete support message, got a %s, expected at least a FrameworkElement.\n", message->GetTypeName ());
800 message->unref ();
801 return;
804 incomplete_support_message = (Panel *) message;
805 AttachLayer (incomplete_support_message);
807 DependencyObject* message_object = incomplete_support_message->FindName ("message");
808 TextBlock* message_block = (message_object != NULL && message_object->Is (Type::TEXTBLOCK)) ? (TextBlock*) message_object : NULL;
811 char *message_text = g_strdup_printf ("You are running a Silverlight %c application. You may experience incompatabilities as Moonlight does not have full support for this runtime yet.", GetDeployment()->GetRuntimeVersion()[0]);
812 message_block->SetValue (TextBlock::TextProperty, message_text);
813 g_free (message_text);
815 DependencyObject* storyboard_object = incomplete_support_message->FindName ("FadeOut");
816 Storyboard* storyboard = (storyboard_object != NULL && storyboard_object->Is (Type::STORYBOARD)) ? (Storyboard*) storyboard_object : NULL;
818 storyboard->AddHandler (Timeline::CompletedEvent, HideIncompleteSilverlightSupportMessageCallback, this);
820 // make the message take up the full width of the window
821 message->SetValue (FrameworkElement::WidthProperty, Value (active_window->GetWidth()));
824 void
825 Surface::HideIncompleteSilverlightSupportMessageCallback (EventObject *sender, EventArgs *args, gpointer closure)
827 ((Surface*)closure)->HideIncompleteSilverlightSupportMessage ();
830 void
831 Surface::HideIncompleteSilverlightSupportMessage ()
833 if (incomplete_support_message) {
834 DetachLayer (incomplete_support_message);
835 incomplete_support_message->unref ();
836 incomplete_support_message = NULL;
837 // Since we're removing a layer the dirty list might get confused
838 active_window->Invalidate ();
843 void
844 Surface::ShowFullScreenMessage ()
846 g_return_if_fail (full_screen_message == NULL);
848 Type::Kind dummy;
849 XamlLoader *loader = new XamlLoader (NULL, FULLSCREEN_MESSAGE, this);
850 DependencyObject* message = loader->CreateDependencyObjectFromString (FULLSCREEN_MESSAGE, false, &dummy);
851 delete loader;
853 if (!message) {
854 g_warning ("Unable to create fullscreen message.\n");
855 return;
858 full_screen_message = (Panel *) message;
859 AttachLayer (full_screen_message);
861 DependencyObject* message_object = full_screen_message->FindName ("message");
862 DependencyObject* url_object = full_screen_message->FindName ("url");
863 TextBlock* message_block = (message_object != NULL && message_object->Is (Type::TEXTBLOCK)) ? (TextBlock*) message_object : NULL;
864 TextBlock* url_block = (url_object != NULL && url_object->Is (Type::TEXTBLOCK)) ? (TextBlock*) url_object : NULL;
866 // Set the url in the box
867 if (url_block != NULL) {
868 char *url = NULL;
870 if (source_location) {
871 if (g_str_has_prefix (source_location, "http://")) {
872 const char *path = strchr (source_location + 7, '/');
874 if (path != NULL && path > source_location + 7) {
875 url = g_strndup (source_location, path - source_location);
876 } else {
877 url = g_strdup (source_location);
879 } else if (g_str_has_prefix (source_location, "file://")) {
880 url = g_strdup ("file://");
881 } else {
882 url = g_strdup (source_location);
886 url_block->SetValue (TextBlock::TextProperty, url ? url : (char *) "file://");
887 g_free (url);
890 DependencyObject* storyboard_object = full_screen_message->FindName ("FadeOut");
891 Storyboard* storyboard = (storyboard_object != NULL && storyboard_object->Is (Type::STORYBOARD)) ? (Storyboard*) storyboard_object : NULL;
893 storyboard->AddHandler (Timeline::CompletedEvent, HideFullScreenMessageCallback, this);
896 void
897 Surface::HideFullScreenMessageCallback (EventObject *sender, EventArgs *args, gpointer closure)
899 ((Surface*)closure)->HideFullScreenMessage ();
902 void
903 Surface::HideFullScreenMessage ()
905 if (full_screen_message) {
906 DetachLayer (full_screen_message);
907 full_screen_message->unref ();
908 full_screen_message = NULL;
909 // Since we're removing a layer the dirty list might get confused
910 active_window->Invalidate ();
914 const char*
915 Surface::GetSourceLocation ()
917 return source_location;
920 void
921 Surface::SetSourceLocation (const char* location)
923 g_free (source_location);
924 source_location = g_strdup (location);
927 void
928 Surface::UpdateFullScreen (bool value)
930 if (value == full_screen)
931 return;
933 if (value) {
934 fullscreen_window = new MoonWindowGtk (true, -1, -1, normal_window, this);
936 active_window = fullscreen_window;
938 ShowFullScreenMessage ();
940 fullscreen_window->EnableEvents (false);
941 } else {
942 active_window = normal_window;
944 HideFullScreenMessage ();
946 delete fullscreen_window;
947 fullscreen_window = NULL;
950 full_screen = value;
952 Realloc ();
954 time_manager->GetSource()->Stop();
955 Emit (FullScreenChangeEvent);
957 if (!value)
958 Emit (ResizeEvent);
959 time_manager->GetSource()->Start();
962 void
963 Surface::render_cb (EventObject *sender, EventArgs *calldata, gpointer closure)
965 Surface *s = (Surface *) closure;
966 gint64 now;
967 bool dirty = false;
969 if (s->active_window == NULL)
970 return; /* no active window to render to */
972 GDK_THREADS_ENTER ();
973 if (s->zombie) {
974 s->up_dirty->Clear (true);
975 s->down_dirty->Clear (true);
976 } else {
977 dirty = s->ProcessDirtyElements ();
980 if (s->expose_handoff) {
981 TimeSpan time = s->GetTimeManager ()->GetCurrentTime ();
982 if (time != s->expose_handoff_last_timespan) {
983 s->expose_handoff (s, time , s->expose_handoff_data);
984 s->expose_handoff_last_timespan = time;
988 GDK_THREADS_LEAVE ();
990 if ((moonlight_flags & RUNTIME_INIT_SHOW_FPS) && s->fps_start == 0)
991 s->fps_start = get_now ();
993 if (dirty) {
994 s->ProcessUpdates ();
997 if ((moonlight_flags & RUNTIME_INIT_SHOW_FPS) && s->fps_report) {
998 s->fps_nframes++;
1000 if ((now = get_now ()) > (s->fps_start + TIMESPANTICKS_IN_SECOND)) {
1001 float nsecs = (now - s->fps_start) / TIMESPANTICKS_IN_SECOND_FLOAT;
1003 s->fps_report (s, s->fps_nframes, nsecs, s->fps_data);
1004 s->fps_nframes = 0;
1005 s->fps_start = now;
1009 if ((moonlight_flags & RUNTIME_INIT_SHOW_CACHE_SIZE) && s->cache_report) {
1010 // By default we report cache status every 50 render's.
1011 // Should be enough for everybody, but syncing to ie. 1s sounds
1012 // better.
1013 if (s->cache_size_ticker == 50) {
1014 s->cache_report (s, s->cache_size_in_bytes, s->cache_data);
1015 s->cache_size_ticker = 0;
1016 } else
1017 s->cache_size_ticker++;
1021 void
1022 Surface::update_input_cb (EventObject *sender, EventArgs *calldata, gpointer closure)
1024 #if notyet
1025 Surface *s = (Surface *) closure;
1027 s->HandleMouseEvent (UIElement::MouseMoveEvent, true, true, false, s->mouse_event_state, s->mouse_event_x, s->mouse_event_y);
1028 s->UpdateCursorFromInputList ()
1029 #endif
1032 void
1033 Surface::HandleUIWindowAvailable ()
1035 time_manager->AddHandler (TimeManager::RenderEvent, render_cb, this);
1036 time_manager->AddHandler (TimeManager::UpdateInputEvent, update_input_cb, this);
1038 time_manager->NeedRedraw ();
1041 void
1042 Surface::HandleUIWindowUnavailable ()
1044 time_manager->RemoveHandler (TimeManager::RenderEvent, render_cb, this);
1045 time_manager->RemoveHandler (TimeManager::UpdateInputEvent, update_input_cb, this);
1048 void
1049 Surface::PaintToDrawable (GdkDrawable *drawable, GdkVisual *visual, GdkEventExpose *event, int off_x, int off_y, bool transparent, bool clear_transparent)
1051 frames++;
1053 LOG_UI ("Surface::PaintToDrawable (%p, %p, (%d,%d %d,%d), %d, %d, %d, %d)\n",
1054 drawable, visual, event->area.x, event->area.y, event->area.width, event->area.height,
1055 off_x, off_y, transparent, clear_transparent);
1057 if (event->area.x > (off_x + active_window->GetWidth()) || event->area.y > (off_y + active_window->GetHeight()))
1058 return;
1060 SetCurrentDeployment ();
1062 #if TIME_REDRAW
1063 STARTTIMER (expose, "redraw");
1064 #endif
1065 if (cache_size_multiplier == -1)
1066 cache_size_multiplier = gdk_drawable_get_depth (drawable) / 8 + 1;
1068 #ifdef DEBUG_INVALIDATE
1069 printf ("Got a request to repaint at %d %d %d %d\n", event->area.x, event->area.y, event->area.width, event->area.height);
1070 #endif
1071 cairo_t *ctx = runtime_cairo_create (drawable, visual, moonlight_flags & RUNTIME_INIT_USE_BACKEND_XLIB);
1072 Region *region = new Region (event->region);
1074 region->Offset (-off_x, -off_y);
1075 cairo_surface_set_device_offset (cairo_get_target (ctx),
1076 off_x - event->area.x,
1077 off_y - event->area.y);
1078 region->Draw (ctx);
1080 // These are temporary while we change this to paint at the offset position
1081 // instead of using the old approach of modifying the topmost UIElement (a no-no),
1083 // The flag "transparent" is here because I could not
1084 // figure out what is painting the background with white now.
1085 // The change that made the white painting implicit instead of
1086 // explicit is patch 80632. I would appreciate any help in tracking down
1087 // the proper way of making the background white when not running in
1088 // "transparent" mode.
1090 // Either exposing surface_set_trans to turn the next code is a hack,
1091 // or it is normal to request all code that wants to paint to manually
1092 // clear the background to white beforehand. For now am going with
1093 // making this an explicit surface API.
1095 // The second part is for coping with the future: when we support being
1096 // windowless
1098 cairo_set_operator (ctx, CAIRO_OPERATOR_OVER);
1100 if (transparent) {
1101 if (clear_transparent) {
1102 cairo_set_operator (ctx, CAIRO_OPERATOR_CLEAR);
1103 cairo_fill_preserve (ctx);
1104 cairo_set_operator (ctx, CAIRO_OPERATOR_OVER);
1107 cairo_set_source_rgba (ctx,
1108 background_color->r,
1109 background_color->g,
1110 background_color->b,
1111 background_color->a);
1113 else {
1114 cairo_set_source_rgb (ctx,
1115 background_color->r,
1116 background_color->g,
1117 background_color->b);
1120 cairo_fill_preserve (ctx);
1121 cairo_clip (ctx);
1123 cairo_save (ctx);
1124 Paint (ctx, region);
1125 cairo_restore (ctx);
1127 if (RENDER_EXPOSE) {
1128 cairo_new_path (ctx);
1129 region->Draw (ctx);
1130 cairo_set_line_width (ctx, 2.0);
1131 cairo_set_source_rgb (ctx, (double)(abs (frames) % 2), (double)((abs (frames) + 1) % 2), (double)((abs (frames) / 3) % 2));
1132 cairo_stroke (ctx);
1135 if (!(moonlight_flags & RUNTIME_INIT_USE_BACKEND_XLIB)) {
1136 cairo_surface_flush (cairo_get_target (ctx));
1137 cairo_t *native = runtime_cairo_create (drawable, visual, true);
1139 cairo_surface_set_device_offset (cairo_get_target (native),
1140 0, 0);
1141 cairo_surface_set_device_offset (cairo_get_target (ctx),
1142 0, 0);
1144 cairo_set_source_surface (native, cairo_get_target (ctx),
1145 0, 0);
1147 region->Offset (off_x, off_y);
1148 region->Offset (-event->area.x, -event->area.y);
1149 region->Draw (native);
1151 cairo_fill (native);
1152 cairo_destroy (native);
1154 cairo_destroy (ctx);
1156 delete region;
1159 #if TIME_REDRAW
1160 ENDTIMER (expose, "redraw");
1161 #endif
1165 /* for emitting focus changed events */
1166 class FocusChangedNode : public List::Node {
1167 public:
1168 UIElement *lost_focus;
1169 UIElement *got_focus;
1171 FocusChangedNode (UIElement *lost_focus, UIElement *got_focus);
1172 virtual ~FocusChangedNode ();
1175 FocusChangedNode::FocusChangedNode (UIElement *lost_focus, UIElement *got_focus)
1177 this->lost_focus = lost_focus;
1178 this->got_focus = got_focus;
1180 if (lost_focus)
1181 lost_focus->ref ();
1182 if (got_focus)
1183 got_focus->ref ();
1186 FocusChangedNode::~FocusChangedNode ()
1188 if (lost_focus)
1189 lost_focus->unref ();
1190 if (got_focus)
1191 got_focus->unref ();
1194 RenderNode::RenderNode (UIElement *el,
1195 Region *region,
1196 bool render_element,
1197 RenderFunc pre,
1198 RenderFunc post)
1201 uielement = el;
1202 uielement->ref();
1203 this->region = region ? region : new Region ();
1204 this->render_element = render_element;
1205 this->pre_render = pre;
1206 this->post_render = post;
1209 void
1210 RenderNode::Render (cairo_t *ctx)
1212 bool front_to_back = uielement->UseBackToFront ();
1214 if (pre_render)
1215 pre_render (ctx, uielement, region, front_to_back);
1217 if (render_element)
1218 uielement->Render (ctx, region);
1220 if (post_render)
1221 post_render (ctx, uielement, region, front_to_back);
1224 RenderNode::~RenderNode ()
1226 if (uielement) {
1227 uielement->unref ();
1228 uielement = NULL;
1231 if (region)
1232 delete region;
1235 UIElementNode::UIElementNode (UIElement *el)
1237 uielement = el;
1238 uielement->ref();
1241 UIElementNode::~UIElementNode ()
1243 uielement->unref();
1244 uielement = NULL;
1247 void
1248 Surface::PerformCapture (UIElement *capture)
1250 // "Capturing" the mouse pointer at an element forces us to
1251 // use the path up the hierarchy from that element to the root
1252 // as the input list, regardless of where the pointer actually
1253 // is.
1255 captured = capture;
1256 List *new_input_list = new List();
1257 while (capture) {
1258 new_input_list->Append (new UIElementNode (capture));
1259 capture = capture->GetVisualParent();
1262 delete input_list;
1263 input_list = new_input_list;
1264 pendingCapture = NULL;
1267 void
1268 Surface::PerformReleaseCapture ()
1270 // These need to be set before calling HandleMouseEvent as
1271 // "captured" determines the input_list calculation, and
1272 // "pendingReleaseCapture", when set, causes an infinite
1273 // recursive loop.
1274 UIElement *old_captured = captured;
1275 captured = NULL;
1276 pendingReleaseCapture = false;
1278 old_captured->EmitLostMouseCapture ();
1280 // this causes any new elements we're over to be Enter'ed. MS
1281 // doesn't Leave the element that had the mouse captured,
1282 // though.
1283 HandleMouseEvent (NO_EVENT_ID, false, true, false, mouse_event);
1286 void
1287 Surface::ReleaseMouseCapture (UIElement *capture)
1289 // Mouse capture is only released when the element owning the capture
1290 // requests it
1291 if (capture != captured && capture != pendingCapture)
1292 return;
1294 if (emittingMouseEvent)
1295 pendingReleaseCapture = true;
1296 else
1297 PerformReleaseCapture ();
1300 bool
1301 Surface::SetMouseCapture (UIElement *capture)
1303 if (captured || pendingCapture)
1304 return capture == captured || capture == pendingCapture;
1306 if (!emittingMouseEvent)
1307 return false;
1309 pendingCapture = capture;
1310 return true;
1313 EventArgs*
1314 Surface::CreateArgsForEvent (int event_id, GdkEvent *event)
1316 if (event_id ==UIElement::InvalidatedEvent
1317 || event_id ==UIElement::GotFocusEvent
1318 || event_id ==UIElement::LostFocusEvent)
1319 return new RoutedEventArgs ();
1320 else if (event_id == UIElement::MouseLeaveEvent
1321 || event_id ==UIElement::MouseMoveEvent
1322 || event_id ==UIElement::MouseEnterEvent)
1323 return new MouseEventArgs(event);
1324 else if (event_id ==UIElement::MouseLeftButtonMultiClickEvent
1325 || event_id ==UIElement::MouseLeftButtonDownEvent
1326 || event_id ==UIElement::MouseLeftButtonUpEvent
1327 || event_id ==UIElement::MouseRightButtonDownEvent
1328 || event_id ==UIElement::MouseRightButtonUpEvent)
1329 return new MouseButtonEventArgs(event);
1330 else if (event_id == UIElement::MouseWheelEvent)
1331 return new MouseWheelEventArgs(event);
1332 else if (event_id == UIElement::KeyDownEvent
1333 || event_id == UIElement::KeyUpEvent)
1334 return new KeyEventArgs((GdkEventKey*)event);
1335 else {
1336 g_warning ("Unknown event id %d\n", event_id);
1337 return new EventArgs();
1341 bool
1342 Surface::EmitEventOnList (int event_id, List *element_list, GdkEvent *event, int end_idx)
1344 bool handled = false;
1346 int idx;
1347 UIElementNode *node;
1349 if (element_list->IsEmpty() || end_idx == 0)
1350 return handled;
1352 if (end_idx == -1)
1353 end_idx = element_list->Length();
1355 EmitContext** emit_ctxs = g_new (EmitContext*, end_idx + 1);
1356 for (node = (UIElementNode*)element_list->First(), idx = 0; node && idx < end_idx; node = (UIElementNode*)node->next, idx++) {
1357 emit_ctxs[idx] = node->uielement->StartEmit (event_id);
1360 EventArgs *args = CreateArgsForEvent(event_id, event);
1361 bool args_are_routed = args->Is (Type::ROUTEDEVENTARGS);
1363 if (args_are_routed && element_list->First())
1364 ((RoutedEventArgs*)args)->SetSource(((UIElementNode*)element_list->First())->uielement);
1366 for (node = (UIElementNode*)element_list->First(), idx = 0; node && idx < end_idx; node = (UIElementNode*)node->next, idx++) {
1367 args->ref ();
1368 bool h = node->uielement->DoEmit (event_id, args);
1369 if (h)
1370 handled = true;
1371 if (zombie) {
1372 handled = false;
1373 break;
1376 if (args_are_routed && ((RoutedEventArgs*)args)->GetHandled())
1377 break;
1380 args->unref();
1382 for (node = (UIElementNode*)element_list->First(), idx = 0; node && idx < end_idx; node = (UIElementNode*)node->next, idx++) {
1383 node->uielement->FinishEmit (event_id, emit_ctxs[idx]);
1385 g_free (emit_ctxs);
1387 return handled;
1390 void
1391 Surface::FindFirstCommonElement (List *l1, int *index1,
1392 List *l2, int *index2)
1394 // we exploit the fact that for a list with any common
1395 // elements, the lists will be identical from the first common
1396 // element to the end of the lists. So, we start from the
1397 // last elements in both lists and walk backward to the start,
1398 // looking for the first elements that don't match.
1400 // this algorithm is O(MAX(n,m)), but it's unclear whether or
1401 // not this is actually better than the O(n*m) approach, since
1402 // the O(n*m) approach will often find a match on the first
1403 // comparison (especially when the user is slowly moving the
1404 // mouse around in the same element), and skip the rest.
1405 int i1, i2;
1406 UIElementNode *ui1, *ui2;
1408 *index1 = -1;
1409 *index2 = -1;
1411 ui1 = (UIElementNode*)l1->Last();
1412 i1 = l1->Length() - 1;
1414 ui2 = (UIElementNode*)l2->Last();
1415 i2 = l2->Length() - 1;
1417 while (ui1 && ui2) {
1419 if (ui1->uielement == ui2->uielement) {
1420 *index1 = i1;
1421 *index2 = i2;
1423 else {
1424 return;
1427 ui1 = (UIElementNode*)ui1->prev;
1428 ui2 = (UIElementNode*)ui2->prev;
1429 i1--;
1430 i2--;
1434 static List*
1435 copy_input_list_from_node (List *input_list, UIElementNode* node)
1437 List *list = new List ();
1439 while (node) {
1440 list->Append (new UIElementNode (node->uielement));
1441 node = (UIElementNode*) node->next;
1444 return list;
1447 bool
1448 Surface::HandleMouseEvent (int event_id, bool emit_leave, bool emit_enter, bool force_emit, GdkEvent *event)
1450 bool handled = false;
1451 bool mouse_down = event_id == UIElement::MouseLeftButtonDownEvent ||
1452 event_id == UIElement::MouseRightButtonDownEvent;
1454 if ((moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) == 0 &&
1455 ((event_id == UIElement::MouseRightButtonDownEvent) || (event_id == UIElement::MouseRightButtonDownEvent)))
1456 event_id = NO_EVENT_ID;
1458 // we can end up here if mozilla pops up the JS timeout
1459 // dialog. The problem is that JS might have registered a
1460 // handler for the event we're going to emit, so when we end
1461 // up tripping the timeout while in JS, mozilla pops up the
1462 // dialog, which causes a crossing-notify event to be emitted.
1463 // This causes HandleMouseEvent to be called, and the original
1464 // input_list is deleted. the crossing-notify event is
1465 // handled, then we return to the event that tripped the
1466 // timeout, we crash.
1467 if (emittingMouseEvent)
1468 return false;
1470 emittingMouseEvent = true;
1471 if (zombie)
1472 return false;
1474 if (toplevel == NULL || event == NULL)
1475 return false;
1477 // FIXME this should probably use mouse event args
1478 ProcessDirtyElements();
1480 if (captured) {
1481 // if the mouse is captured, the input_list doesn't ever
1482 // change, and we don't emit enter/leave events. just emit
1483 // the event on the input_list.
1484 if (event_id != NO_EVENT_ID)
1485 handled = EmitEventOnList (event_id, input_list, event, -1);
1487 else {
1488 int surface_index;
1489 int new_index;
1491 // Accumulate a new input_list, which contains the
1492 // most deeply nested hit testable UIElement covering
1493 // the point (x,y), and all visual parents up the
1494 // hierarchy to the root.
1495 List *new_input_list = new List ();
1496 double x, y;
1498 gdk_event_get_coords (event, &x, &y);
1500 Point p (x,y);
1502 cairo_t *ctx = measuring_context_create ();
1503 for (int i = layers->GetCount () - 1; i >= 0 && new_input_list->IsEmpty (); i--)
1504 layers->GetValueAt (i)->AsUIElement ()->HitTest (ctx, p, new_input_list);
1506 if (mouse_down) {
1507 EmitFocusChangeEvents ();
1508 if (!GetFocusedElement ()) {
1509 int last = layers->GetCount () - 1;
1510 for (int i = last; i >= 0; i--) {
1511 if (TabNavigationWalker::Focus (layers->GetValueAt (i)->AsUIElement (), true))
1512 break;
1514 if (!GetFocusedElement () && last != -1)
1515 FocusElement (layers->GetValueAt (last)->AsUIElement ());
1517 EmitFocusChangeEvents ();
1521 // for 2 lists:
1522 // l1: [a1, a2, a3, a4, ... ]
1523 // l2: [b1, b2, b3, b4, ... ]
1525 // For identical lists:
1527 // only the primary event is emitted for all
1528 // elements of l2, in order.
1530 // For lists that differ, Enter/Leave events must be
1531 // emitted.
1533 // If the first few nodes in each list differ, and,
1534 // for instance bn == am, we know that [am...] ==
1535 // [bn...]
1537 // when emitting a given event on b1, MS generally
1538 // emits Leave events on [a1, a2, a3, ... am-1], and
1539 // Enter events on [b1, b2, ... bn-1].
1541 // For most event types, that's all that happens if
1542 // the lists differ. For MouseLeftButtonDown (we
1543 // also do it for MouseLeftButtonUp), we also emit
1544 // the primary event on l2 after the enter/leave
1545 // events.
1547 FindFirstCommonElement (input_list, &surface_index,
1548 new_input_list, &new_index);
1550 if (emit_leave)
1551 handled = EmitEventOnList (UIElement::MouseLeaveEvent, input_list, event, surface_index);
1553 if (emit_enter)
1554 handled = EmitEventOnList (UIElement::MouseEnterEvent, new_input_list, event, new_index) || handled;
1556 if (event_id != NO_EVENT_ID && ((surface_index == 0 && new_index == 0) || force_emit))
1557 handled = EmitEventOnList (event_id, new_input_list, event, -1) || handled;
1559 // We need to remove from the new_input_list the events which have just
1560 // became invisible/unhittable as the result of the event.
1561 // (ie. element visibility was changed in the mouse enter).
1563 if (handled) {
1564 UIElementNode *node;
1566 for (node = (UIElementNode*)new_input_list->Last(); node; node = (UIElementNode*)node->prev) {
1567 if (! node->uielement->GetRenderVisible () ||
1568 ! node->uielement->GetHitTestVisible ()) {
1569 // Ooops, looks like something changed.
1570 // We need to copy the list with some elements removed.
1571 List *list = copy_input_list_from_node (new_input_list, (UIElementNode*)node->next);
1572 delete new_input_list;
1573 new_input_list = list;
1574 break;
1579 measuring_context_destroy (ctx);
1581 delete input_list;
1582 input_list = new_input_list;
1585 #define SPEW_INPUT_LIST 0
1587 #if SPEW_INPUT_LIST
1589 printf ("input_list: ");
1590 UIElementNode *node;
1591 for (node = (UIElementNode*)input_list->First(); node; node = (UIElementNode*)node->next) {
1592 if (node != input_list->First())
1593 printf ("->");
1594 printf ("(%s)", node->uielement->GetName());
1596 printf ("\n");
1598 #endif
1600 // Perform any captures/releases that are pending after the
1601 // event is bubbled.
1602 if (pendingCapture)
1603 PerformCapture (pendingCapture);
1604 if (pendingReleaseCapture || (captured && !captured->CanCaptureMouse ()))
1605 PerformReleaseCapture ();
1606 emittingMouseEvent = false;
1607 return handled;
1610 void
1611 Surface::UpdateCursorFromInputList ()
1613 MouseCursor new_cursor = MouseCursorDefault;
1615 // loop over the input list in order until we hit a node that
1616 // has its cursor set to the non-default.
1617 UIElementNode *node;
1618 for (node = (UIElementNode*)input_list->First(); node; node = (UIElementNode*)node->next) {
1619 new_cursor = node->uielement->GetCursor ();
1620 if (new_cursor != MouseCursorDefault)
1621 break;
1624 SetCursor (new_cursor);
1627 void
1628 Surface::SetFPSReportFunc (MoonlightFPSReportFunc report, void *user_data)
1630 fps_report = report;
1631 fps_data = user_data;
1634 void
1635 Surface::SetCacheReportFunc (MoonlightCacheReportFunc report, void *user_data)
1637 cache_report = report;
1638 cache_data = user_data;
1641 void
1642 Surface::SetExposeHandoffFunc (MoonlightExposeHandoffFunc func, void *user_data)
1644 expose_handoff = func;
1645 expose_handoff_data = user_data;
1646 expose_handoff_last_timespan = G_MAXINT64;
1649 class DownloaderNode : public List::Node {
1650 public:
1651 Downloader *downloader;
1652 DownloaderNode (Downloader *dl) { downloader = dl; }
1655 void
1656 Surface::DetachDownloaders ()
1658 DownloaderNode *node;
1659 if (downloaders == NULL)
1660 return;
1662 node = (DownloaderNode *) downloaders->First ();
1663 while (node != NULL) {
1664 node->downloader->RemoveHandler (Downloader::DestroyedEvent, OnDownloaderDestroyed, this);
1665 node->downloader->SetIsAttached (false);
1666 node = (DownloaderNode *) node->next;
1668 downloaders->Clear (true);
1671 void
1672 Surface::OnDownloaderDestroyed (EventObject *sender, EventArgs *args, gpointer closure)
1674 DownloaderNode *node;
1675 Surface *surface = (Surface *) closure;
1676 List *downloaders = surface->downloaders;
1678 if (downloaders == NULL) {
1679 printf ("Surface::OnDownloaderDestroyed (): The list of downloaders is empty.\n");
1680 return;
1683 node = (DownloaderNode *) downloaders->First ();
1684 while (node != NULL) {
1685 if (node->downloader == sender) {
1686 downloaders->Remove (node);
1687 return;
1689 node = (DownloaderNode *) node->next;
1692 printf ("Surface::OnDownloaderDestroyed (): Couldn't find the downloader %p in the list of downloaders\n", sender);
1695 Downloader *
1696 Surface::CreateDownloader (void)
1698 if (zombie) {
1699 g_warning ("Surface::CreateDownloader (): Trying to create a downloader on a zombified surface.\n");
1700 return NULL;
1703 Downloader *downloader = new Downloader ();
1704 downloader->SetIsAttached (true);
1705 downloader->SetContext (downloader_context);
1706 downloader->AddHandler (Downloader::DestroyedEvent, OnDownloaderDestroyed, this);
1707 if (downloaders == NULL)
1708 downloaders = new List ();
1709 downloaders->Append (new DownloaderNode (downloader));
1711 return downloader;
1714 Downloader *
1715 Surface::CreateDownloader (EventObject *obj)
1717 Surface *surface;
1719 surface = obj->GetDeployment ()->GetSurface ();
1721 if (surface)
1722 return surface->CreateDownloader ();
1724 g_warning ("Surface::CreateDownloader (%p, ID: %i): Unable to create contextual downloader.\n",
1725 obj, GET_OBJ_ID (obj));
1727 return NULL;
1730 bool
1731 Surface::VerifyWithCacheSizeCounter (int w, int h)
1733 if (! (moonlight_flags & RUNTIME_INIT_USE_SHAPE_CACHE))
1734 return false;
1736 if (cache_size_multiplier == -1)
1737 return false;
1739 if (cache_size_in_bytes + (w * h * cache_size_multiplier) < MAXIMUM_CACHE_SIZE)
1740 return true;
1741 else
1742 return false;
1745 gint64
1746 Surface::AddToCacheSizeCounter (int w, int h)
1748 gint64 new_size = w * h * cache_size_multiplier;
1749 cache_size_in_bytes += new_size;
1750 return new_size;
1753 void
1754 Surface::RemoveFromCacheSizeCounter (gint64 size)
1756 cache_size_in_bytes -= size;
1759 bool
1760 Surface::FullScreenKeyHandled (GdkEventKey *key)
1762 if (!GetFullScreen ())
1763 return false;
1765 // If we're in fullscreen mode no key events are passed through.
1766 // We only handle Esc, to exit fullscreen mode.
1767 if (key->keyval == GDK_Escape)
1768 SetFullScreen (false);
1770 switch (key->keyval) {
1771 case GDK_Down:
1772 case GDK_Up:
1773 case GDK_Left:
1774 case GDK_Right:
1775 case GDK_KP_Space:
1776 case GDK_space:
1777 case GDK_Tab:
1778 case GDK_Page_Down:
1779 case GDK_Page_Up:
1780 case GDK_Home:
1781 case GDK_End:
1782 case GDK_Return:
1783 case GDK_KP_Enter:
1784 return false;
1786 // Explicitly listing GDK_Escape here as it should never bubble up
1787 case GDK_Escape:
1788 default:
1789 return true;
1793 gboolean
1794 Surface::HandleUIFocusIn (GdkEventFocus *event)
1796 if (IsZombie ())
1797 return false;
1799 time_manager->InvokeTickCalls();
1801 if (GetFocusedElement ()) {
1802 List *focus_to_root = ElementPathToRoot (GetFocusedElement ());
1803 EmitEventOnList (UIElement::GotFocusEvent, focus_to_root, (GdkEvent*)event, -1);
1804 delete focus_to_root;
1807 return false;
1810 gboolean
1811 Surface::HandleUIFocusOut (GdkEventFocus *event)
1813 if (IsZombie ())
1814 return false;
1816 time_manager->InvokeTickCalls();
1818 if (GetFocusedElement ()) {
1819 List *focus_to_root = ElementPathToRoot (GetFocusedElement ());
1820 EmitEventOnList (UIElement::LostFocusEvent, focus_to_root, (GdkEvent*)event, -1);
1821 delete focus_to_root;
1824 return false;
1827 gboolean
1828 Surface::HandleUIButtonRelease (GdkEventButton *event)
1830 time_manager->InvokeTickCalls();
1832 if (event->button != 1 && event->button != 3) {
1833 return false;
1836 SetUserInitiatedEvent (true);
1838 if (mouse_event)
1839 gdk_event_free (mouse_event);
1841 mouse_event = gdk_event_copy ((GdkEvent *) event);
1843 HandleMouseEvent (event->button == 1 ? UIElement::MouseLeftButtonUpEvent : UIElement::MouseRightButtonUpEvent,
1844 true, true, true, mouse_event);
1846 UpdateCursorFromInputList ();
1847 SetUserInitiatedEvent (false);
1849 // XXX MS appears to do this here, which is completely stupid.
1850 if (captured)
1851 PerformReleaseCapture ();
1853 return !((moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) == 0 && event->button == 3);
1856 gboolean
1857 Surface::HandleUIButtonPress (GdkEventButton *event)
1859 bool handled;
1860 int event_id;
1862 active_window->GrabFocus ();
1864 time_manager->InvokeTickCalls();
1866 if (event->button != 1 && event->button != 3)
1867 return false;
1869 SetUserInitiatedEvent (true);
1871 if (mouse_event)
1872 gdk_event_free (mouse_event);
1874 mouse_event = gdk_event_copy ((GdkEvent *) event);
1876 switch (event->type) {
1877 case GDK_3BUTTON_PRESS:
1878 case GDK_2BUTTON_PRESS:
1879 if (event->button != 1)
1880 return false;
1882 handled = HandleMouseEvent (UIElement::MouseLeftButtonMultiClickEvent, false, false, true, mouse_event);
1883 break;
1884 default:
1885 if (event->button == 1)
1886 event_id = UIElement::MouseLeftButtonDownEvent;
1887 else
1888 event_id = UIElement::MouseRightButtonDownEvent;
1890 handled = HandleMouseEvent (event_id, true, true, true, mouse_event);
1891 break;
1894 UpdateCursorFromInputList ();
1895 SetUserInitiatedEvent (false);
1897 return handled;
1900 gboolean
1901 Surface::HandleUIScroll (GdkEventScroll *event)
1903 time_manager->InvokeTickCalls();
1905 if (mouse_event)
1906 gdk_event_free (mouse_event);
1908 mouse_event = gdk_event_copy ((GdkEvent *) event);
1910 bool handled = false;
1912 handled = HandleMouseEvent (UIElement::MouseWheelEvent, true, true, true, mouse_event);
1914 UpdateCursorFromInputList ();
1916 return handled;
1919 gboolean
1920 Surface::HandleUIMotion (GdkEventMotion *event)
1922 time_manager->InvokeTickCalls();
1924 if (mouse_event)
1925 gdk_event_free (mouse_event);
1927 mouse_event = gdk_event_copy ((GdkEvent *) event);
1929 bool handled = false;
1931 if (event->is_hint) {
1932 #if GTK_CHECK_VERSION(2,12,0)
1933 if (gtk_check_version (2, 12, 0))
1934 gdk_event_request_motions (event);
1935 else
1936 #endif
1938 int ix, iy;
1939 GdkModifierType state;
1940 gdk_window_get_pointer (event->window, &ix, &iy, (GdkModifierType*)&state);
1941 ((GdkEventMotion *) mouse_event)->x = ix;
1942 ((GdkEventMotion *) mouse_event)->y = iy;
1946 handled = HandleMouseEvent (UIElement::MouseMoveEvent, true, true, true, mouse_event);
1947 UpdateCursorFromInputList ();
1949 return handled;
1952 gboolean
1953 Surface::HandleUICrossing (GdkEventCrossing *event)
1955 bool handled;
1957 time_manager->InvokeTickCalls();
1959 /* FIXME Disabling this for now... causes issues in ink journal
1960 GdkWindow *active_gdk_window = active_window->GetGdkWindow ();
1962 if (event->window && event->window != active_window->GetGdkWindow ()) {
1963 g_object_unref (active_gdk_window);
1964 return TRUE;
1965 } else
1966 g_object_unref (active_gdk_window);
1969 if (event->type == GDK_ENTER_NOTIFY) {
1970 if (mouse_event)
1971 gdk_event_free (mouse_event);
1972 mouse_event = gdk_event_copy ((GdkEvent *) event);
1974 handled = HandleMouseEvent (UIElement::MouseMoveEvent, true, true, false, mouse_event);
1976 UpdateCursorFromInputList ();
1978 } else {
1979 // forceably emit MouseLeave on the current input
1980 // list.. the "new" list computed by HandleMouseEvent
1981 // should be the same as the current one since we pass
1982 // in the same x,y but I'm not sure that's something
1983 // we can rely on.
1984 handled = HandleMouseEvent (UIElement::MouseLeaveEvent, false, false, true, mouse_event);
1986 // MS specifies that mouse capture is lost when you mouse out of the control
1987 if (captured)
1988 PerformReleaseCapture ();
1990 // clear out the input list so we emit the right
1991 // events when the pointer reenters the control.
1992 if (!emittingMouseEvent) {
1993 delete input_list;
1994 input_list = new List();
1998 return handled;
2001 void
2002 Surface::EmitFocusChangeEvents()
2004 while (FocusChangedNode *node = (FocusChangedNode *) focus_changed_events->First ()) {
2005 if (node->lost_focus)
2006 node->lost_focus->EmitLostFocus ();
2007 if (node->got_focus)
2008 node->got_focus->EmitGotFocus ();
2009 focus_changed_events->Remove (node);
2013 bool
2014 Surface::FocusElement (UIElement *focused)
2016 if (focused == focused_element)
2017 return true;
2019 while (focused_element) {
2020 focus_changed_events->Append (new FocusChangedNode (focused_element, NULL));
2021 focused_element = focused_element->GetVisualParent ();
2024 focused_element = focused;
2026 while (focused) {
2027 focus_changed_events->Append (new FocusChangedNode (NULL, focused));
2028 focused = focused->GetVisualParent ();
2031 if (FirstUserInitiatedEvent ())
2032 AddTickCall (Surface::EmitFocusChangeEventsAsync);
2033 return true;
2036 List*
2037 Surface::ElementPathToRoot (UIElement *source)
2039 List *list = new List();
2040 while (source) {
2041 list->Append (new UIElementNode (source));
2042 source = source->GetVisualParent();
2044 return list;
2047 gboolean
2048 Surface::HandleUIKeyPress (GdkEventKey *event)
2050 time_manager->InvokeTickCalls();
2052 Key key = Keyboard::MapKeyValToKey (event->keyval);
2054 if (Keyboard::IsKeyPressed (key)) {
2055 // If we are running an SL 1.0 application, then key repeats are dropped
2056 Deployment *deployment = Deployment::GetCurrent ();
2057 if (!deployment->IsLoadedFromXap ())
2058 return true;
2059 } else if (FullScreenKeyHandled (event)) {
2060 return true;
2063 #if DEBUG_MARKER_KEY
2064 static int debug_marker_key_in = 0;
2065 if (event->keyval == GDK_d || event->keyval == GDK_D) {
2066 if (!debug_marker_key_in)
2067 printf ("<--- DEBUG MARKER KEY IN (%f) --->\n", get_now () / 10000000.0);
2068 else
2069 printf ("<--- DEBUG MARKER KEY OUT (%f) --->\n", get_now () / 10000000.0);
2070 debug_marker_key_in = ! debug_marker_key_in;
2071 return true;
2073 #endif
2075 SetUserInitiatedEvent (true);
2076 bool handled = false;
2078 Keyboard::OnKeyPress (key);
2080 if (focused_element) {
2081 List *focus_to_root = ElementPathToRoot (focused_element);
2082 handled = EmitEventOnList (UIElement::KeyDownEvent, focus_to_root, (GdkEvent*)event, -1);
2083 delete focus_to_root;
2085 else if (toplevel){
2086 // in silverlight 1.0, key events are only ever delivered to the toplevel
2087 toplevel->EmitKeyDown (event);
2088 handled = true;
2091 SetUserInitiatedEvent (false);
2093 return handled;
2096 gboolean
2097 Surface::HandleUIKeyRelease (GdkEventKey *event)
2099 time_manager->InvokeTickCalls();
2101 if (FullScreenKeyHandled (event))
2102 return true;
2104 SetUserInitiatedEvent (true);
2105 bool handled = false;
2107 Key key = Keyboard::MapKeyValToKey (event->keyval);
2108 Keyboard::OnKeyRelease (key);
2110 if (focused_element) {
2111 List *focus_to_root = ElementPathToRoot (focused_element);
2112 handled = EmitEventOnList (UIElement::KeyUpEvent, focus_to_root, (GdkEvent*)event, -1);
2113 delete focus_to_root;
2115 else if (toplevel) {
2116 // in silverlight 1.0, key events are only ever delivered to the toplevel
2117 toplevel->EmitKeyUp (event);
2118 handled = true;
2121 SetUserInitiatedEvent (false);
2123 return handled;
2126 void
2127 Surface::HandleUIWindowAllocation (bool emit_resize)
2129 Realloc ();
2130 if (emit_resize)
2131 Emit (ResizeEvent);
2134 void
2135 Surface::HandleUIWindowDestroyed (MoonWindow *window)
2137 if (window == fullscreen_window) {
2138 // switch out of fullscreen mode, as something has
2139 // destroyed our fullscreen window.
2140 UpdateFullScreen (false);
2142 else if (window == normal_window) {
2143 // something destroyed our normal window
2144 normal_window = NULL;
2147 if (window == active_window)
2148 active_window = NULL;
2151 void
2152 Surface::SetBackgroundColor (Color *color)
2154 if (background_color)
2155 delete background_color;
2157 background_color = new Color (*color);
2159 active_window->SetBackgroundColor (color);
2160 active_window->Invalidate ();
2163 Color *
2164 Surface::GetBackgroundColor ()
2166 return background_color;
2169 bool
2170 Surface::IsVersionSupported (const char *version_list)
2172 /* we support all 0.*, 1.0.*, 1.1.* and 2.0.* versions. */
2173 bool supported = true;
2174 gchar **versions;
2175 char *version = NULL;
2176 gint64 numbers [4];
2178 if (version_list == NULL)
2179 return false;
2181 versions = g_strsplit (version_list, ".", 4);
2183 supported = versions [0] != NULL && versions [1] != NULL;
2185 if (supported) {
2186 for (int k = 0; k < 4; k++) {
2187 numbers [k] = 0;
2188 version = versions [k];
2190 if (version == NULL)
2191 break;
2193 if (version [0] == 0) {
2194 supported = false;
2195 break;
2198 // Only allow ascii 0-9 characters in the numbers
2199 for (int i = 0; version [i] != 0; i++) {
2200 if (version [i] < '0' || version [i] > '9') {
2201 supported = false;
2202 break;
2206 numbers [k] = atoll (version);
2209 switch (numbers [0]) {
2210 case 0: // We support all versions of the format "0.*" and "1.*"
2211 case 1:
2212 break;
2213 case 2:
2214 supported &= numbers [1] == 0; // 2.0.*
2215 break;
2216 case 3:
2217 supported &= numbers [1] == 0; // 3.0.*
2218 break;
2219 default:
2220 supported = false;
2221 break;
2225 g_strfreev (versions);
2227 return supported;
2230 void
2231 runtime_init_browser (const char *plugin_dir)
2233 runtime_init (plugin_dir, RUNTIME_INIT_BROWSER);
2236 void
2237 runtime_init_desktop ()
2239 runtime_init (NULL, RUNTIME_INIT_DESKTOP);
2242 bool
2243 runtime_is_running_out_of_browser ()
2245 return (moonlight_flags & RUNTIME_INIT_OUT_OF_BROWSER) != 0;
2248 static gint32
2249 get_flags (gint32 def, const char *envname, struct env_options options[])
2251 gint32 flags = def;
2252 const char *env;
2254 if (envname && (env = g_getenv (envname))) {
2255 printf ("%s = %s\n", envname, env);
2257 const char *flag = env;
2258 const char *inptr;
2259 bool all = !strcmp ("all", env);
2260 size_t n;
2261 uint i;
2263 while (*flag == ',')
2264 flag++;
2266 inptr = flag;
2268 while (*flag) {
2269 while (*inptr && *inptr != ',')
2270 inptr++;
2272 n = (inptr - flag);
2273 for (i = 0; options[i].name != NULL; i++) {
2274 if (all) {
2275 flags |= options[i].flag;
2276 continue;
2279 if (n != strlen (options[i].name))
2280 continue;
2282 if (!strncmp (options[i].name, flag, n)) {
2283 if (!options[i].set)
2284 flags &= ~options[i].flag;
2285 else
2286 flags |= options[i].flag;
2290 while (*inptr == ',')
2291 inptr++;
2293 flag = inptr;
2296 return flags;
2299 void
2300 runtime_init (const char *platform_dir, guint32 flags)
2302 if (inited)
2303 return;
2305 if (cairo_version () < CAIRO_VERSION_ENCODE(1,4,0)) {
2306 printf ("*** WARNING ***\n");
2307 printf ("*** Cairo versions < 1.4.0 should not be used for Moon.\n");
2308 printf ("*** Moon is being run against version %s.\n", cairo_version_string ());
2309 printf ("*** Proceed at your own risk\n");
2312 if (running_on_nvidia ()) {
2313 printf ("Moonlight: Forcing client-side rendering because we detected binary drivers which are known to suffer performance problems.\n");
2314 flags &= ~RUNTIME_INIT_USE_BACKEND_XLIB;
2317 // Allow the user to override the flags via his/her environment
2318 flags = get_flags (flags, "MOONLIGHT_OVERRIDES", overrides);
2319 #if DEBUG || LOGGING
2320 debug_flags_ex = get_flags (0, "MOONLIGHT_DEBUG", debug_extras);
2321 debug_flags = get_flags (0, "MOONLIGHT_DEBUG", debugs);
2322 #endif
2324 inited = true;
2326 if (!g_type_inited) {
2327 g_type_inited = true;
2328 g_type_init ();
2331 moonlight_flags = flags;
2333 Deployment::Initialize (platform_dir, (flags & RUNTIME_INIT_CREATE_ROOT_DOMAIN) != 0);
2335 xaml_init ();
2336 downloader_init ();
2337 Media::Initialize ();
2340 void
2341 runtime_shutdown (void)
2343 if (!inited)
2344 return;
2346 Media::Shutdown ();
2348 inited = false;