1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * runtime.cpp: Core surface
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.
23 #define Visual _XxVisual
24 #define Region _XxRegion
26 #include <gdk/gdkkeysyms.h>
27 #include <cairo-xlib.h>
35 #include "transform.h"
36 #include "animation.h"
37 #include "downloader.h"
38 #include "frameworkelement.h"
39 #include "textblock.h"
45 #include "namescope.h"
48 #include "fullscreen.h"
49 #include "incomplete-support.h"
51 #include "window-gtk.h"
52 #include "timemanager.h"
54 #include "contentcontrol.h"
55 #include "usercontrol.h"
56 #include "deployment.h"
59 #include "tabnavigationwalker.h"
61 //#define DEBUG_INVALIDATE 1
62 //#define RENDER_INDIVIDUALLY 1
63 #define DEBUG_REFCNT 0
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;
79 guint32 debug_flags_ex
= 0;
80 guint32 debug_flags
= 0;
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)
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 },
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 },
187 #define RENDER_EXPOSE (moonlight_flags & RUNTIME_INIT_SHOW_EXPOSE)
192 int event
, error
, opcode
;
194 Display
*display
= XOpenDisplay (NULL
);
195 bool result
= XQueryExtension (display
, "NV-GLX", &opcode
, &event
, &error
);
196 XCloseDisplay (display
);
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
);
208 cache_report_default (Surface
*surface
, long bytes
, void *user_data
)
210 printf ("Cache size is ~%.3f MB\n", bytes
/ 1048576.0);
214 runtime_get_surface_list (void)
216 if (!Surface::InMainThread ()) {
217 g_warning ("This method can be only called from the main thread!\n");
225 runtime_cairo_create (GdkWindow
*drawable
, GdkVisual
*visual
, bool native
)
228 cairo_surface_t
*surface
;
231 gdk_drawable_get_size (drawable
, &width
, &height
);
234 surface
= cairo_xlib_surface_create (gdk_x11_drawable_get_xdisplay (drawable
),
235 gdk_x11_drawable_get_xid (drawable
),
236 GDK_VISUAL_XVISUAL (visual
),
239 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
241 cr
= cairo_create (surface
);
242 cairo_surface_destroy (surface
);
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!");
253 } else if (inited
== FALSE
) {
254 g_warning ("Runtime has not been initialized yet, your flags will be overriden!");
261 runtime_flags_set_manual_timesource (gboolean flag
)
263 if (flags_can_be_modifed ())
264 moonlight_flags
|= RUNTIME_INIT_MANUAL_TIMESOURCE
;
268 runtime_flags_set_use_shapecache (gboolean flag
)
270 if (flags_can_be_modifed ())
271 moonlight_flags
|= RUNTIME_INIT_USE_SHAPE_CACHE
;
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;
292 downloader_context
= NULL
;
294 background_color
= NULL
;
295 cursor
= MouseCursorDefault
;
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 ();
312 input_list
= new List ();
315 focused_element
= NULL
;
316 focus_changed_events
= new List ();
319 first_user_initiated_event
= false;
320 user_initiated_event
= false;
324 incomplete_support_message
= NULL
;
325 full_screen_message
= NULL
;
326 source_location
= NULL
;
328 fps_report
= fps_report_default
;
335 cache_report
= cache_report_default
;
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;
351 debug_selected_element
= NULL
;
354 up_dirty
= new DirtyLists (true);
355 down_dirty
= new DirtyLists (false);
357 surface_list
= g_list_append (surface_list
, this);
362 time_manager
->RemoveHandler (TimeManager::RenderEvent
, render_cb
, this);
363 time_manager
->RemoveHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
366 toplevel
->SetIsAttached (false);
371 if (debug_selected_element
) {
372 debug_selected_element
->unref ();
373 debug_selected_element
= NULL
;
377 HideFullScreenMessage ();
379 delete focus_changed_events
;
382 g_free (source_location
);
384 if (fullscreen_window
)
385 delete fullscreen_window
;
388 delete normal_window
;
390 delete background_color
;
392 time_manager
->unref ();
400 surface_list
= g_list_remove (surface_list
, this);
407 toplevel
->SetIsAttached (false);
408 toplevel
->Dispose ();
411 EventObject::Dispose ();
417 time_manager
->Shutdown ();
418 DetachDownloaders ();
423 Surface::DetachWindow ()
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
;
437 Surface::SetCursor (MouseCursor new_cursor
)
439 if (new_cursor
!= cursor
) {
442 active_window
->SetCursor (cursor
);
447 Surface::GetTimeManagerReffed ()
450 time_manager_mutex
.Lock ();
451 result
= time_manager
;
454 time_manager_mutex
.Unlock ();
459 Surface::EmitFocusChangeEventsAsync (EventObject
*sender
)
461 ((Surface
*)sender
)->EmitFocusChangeEvents ();
465 Surface::Attach (UIElement
*element
)
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.");
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 ());
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 ();
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 ();
509 DetachDownloaders ();
512 active_window
->EnableEvents (first
);
515 active_window
->Invalidate();
521 if (!element
->Is (Type::UIELEMENT
)) {
522 printf ("Surface::Attach Unsupported toplevel %s\n", element
->GetTypeName ());
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
);
542 toplevel
= new_toplevel
;
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 ();
565 Surface::tick_after_attach_reached (EventObject
*data
)
567 Surface
*surface
= (Surface
*)data
;
569 surface
->ticked_after_attach
= true;
570 surface
->Emit (Surface::LoadEvent
);
574 Surface::toplevel_loaded (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
576 ((Surface
*)closure
)->ToplevelLoaded ((UIElement
*)sender
);
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
599 vw
= toplevel
->GetValue (FrameworkElement::WidthProperty
);
600 vh
= toplevel
->GetValue (FrameworkElement::HeightProperty
);
602 normal_window
->Resize (MAX (vw
? (int)vw
->AsDouble () : 0, 0),
603 MAX (vh
? (int)vh
->AsDouble () : 0, 0));
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 ();
619 Surface::AttachLayer (UIElement
*layer
)
621 if (layer
== toplevel
)
622 layers
->Insert (0, Value(layer
));
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 ();
634 Surface::DetachLayer (UIElement
*layer
)
636 layers
->Remove (Value (layer
));
637 layer
->SetIsAttached (false);
639 Invalidate (layer
->GetBounds ());
643 Surface::Invalidate (Rect r
)
645 active_window
->Invalidate (r
);
649 Surface::ProcessUpdates ()
651 active_window
->ProcessUpdates();
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
, ®ion
);
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
);
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);
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
);
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
696 Surface::Resize (int width
, int height
)
698 if (width
== normal_window
->GetWidth()
699 && height
== normal_window
->GetHeight())
702 normal_window
->Resize (width
, height
);
706 Surface::EmitSourceDownloadComplete ()
708 Emit (SourceDownloadCompleteEvent
, NULL
);
712 Surface::EmitSourceDownloadProgressChanged (DownloadProgressEventArgs
*args
)
714 Emit (SourceDownloadProgressChangedEvent
, args
);
718 Surface::EmitError (ErrorEventArgs
*args
)
720 Emit (ErrorEvent
, args
);
724 Surface::EmitError (int number
, int code
, const char *message
)
726 EmitError (new ErrorEventArgs ((ErrorEventArgsType
)number
,
727 MoonError (MoonError::EXCEPTION
, code
, message
)));
733 for (int i
= 0; i
< layers
->GetCount (); i
++) {
734 UIElement
*layer
= layers
->GetValueAt (i
)->AsUIElement ();
736 layer
->InvalidateMeasure ();
737 //layer->UpdateBounds();
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.");
749 UpdateFullScreen (value
);
753 Surface::SetZoomFactor (double value
)
755 // FIXME: implement surface zooming
758 Emit (ZoomedEvent
, new EventArgs ());
762 Surface::SetUserInitiatedEvent (bool value
)
764 EmitFocusChangeEvents ();
765 first_user_initiated_event
= first_user_initiated_event
| value
;
766 user_initiated_event
= value
;
770 Surface::IsTopLevel (UIElement
* top
)
775 bool ret
= top
== full_screen_message
;
777 for (int i
= 0; i
< layers
->GetCount () && !ret
; i
++)
778 ret
= layers
->GetValueAt (i
)->AsUIElement () == top
;
784 Surface::ShowIncompleteSilverlightSupportMessage ()
786 g_return_if_fail (incomplete_support_message
== NULL
);
789 XamlLoader
*loader
= new XamlLoader (NULL
, INCOMPLETE_SUPPORT_MESSAGE
, this);
790 DependencyObject
* message
= loader
->CreateDependencyObjectFromString (INCOMPLETE_SUPPORT_MESSAGE
, false, &dummy
);
794 g_warning ("Unable to create incomplete support message.\n");
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 ());
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()));
825 Surface::HideIncompleteSilverlightSupportMessageCallback (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
827 ((Surface
*)closure
)->HideIncompleteSilverlightSupportMessage ();
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 ();
844 Surface::ShowFullScreenMessage ()
846 g_return_if_fail (full_screen_message
== NULL
);
849 XamlLoader
*loader
= new XamlLoader (NULL
, FULLSCREEN_MESSAGE
, this);
850 DependencyObject
* message
= loader
->CreateDependencyObjectFromString (FULLSCREEN_MESSAGE
, false, &dummy
);
854 g_warning ("Unable to create fullscreen message.\n");
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
) {
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
);
877 url
= g_strdup (source_location
);
879 } else if (g_str_has_prefix (source_location
, "file://")) {
880 url
= g_strdup ("file://");
882 url
= g_strdup (source_location
);
886 url_block
->SetValue (TextBlock::TextProperty
, url
? url
: (char *) "file://");
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);
897 Surface::HideFullScreenMessageCallback (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
899 ((Surface
*)closure
)->HideFullScreenMessage ();
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 ();
915 Surface::GetSourceLocation ()
917 return source_location
;
921 Surface::SetSourceLocation (const char* location
)
923 g_free (source_location
);
924 source_location
= g_strdup (location
);
928 Surface::UpdateFullScreen (bool value
)
930 if (value
== full_screen
)
934 fullscreen_window
= new MoonWindowGtk (true, -1, -1, normal_window
, this);
936 active_window
= fullscreen_window
;
938 ShowFullScreenMessage ();
940 fullscreen_window
->EnableEvents (false);
942 active_window
= normal_window
;
944 HideFullScreenMessage ();
946 delete fullscreen_window
;
947 fullscreen_window
= NULL
;
954 time_manager
->GetSource()->Stop();
955 Emit (FullScreenChangeEvent
);
959 time_manager
->GetSource()->Start();
963 Surface::render_cb (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
965 Surface
*s
= (Surface
*) closure
;
969 if (s
->active_window
== NULL
)
970 return; /* no active window to render to */
972 GDK_THREADS_ENTER ();
974 s
->up_dirty
->Clear (true);
975 s
->down_dirty
->Clear (true);
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 ();
994 s
->ProcessUpdates ();
997 if ((moonlight_flags
& RUNTIME_INIT_SHOW_FPS
) && s
->fps_report
) {
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
);
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
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;
1017 s
->cache_size_ticker
++;
1022 Surface::update_input_cb (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
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 ()
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 ();
1042 Surface::HandleUIWindowUnavailable ()
1044 time_manager
->RemoveHandler (TimeManager::RenderEvent
, render_cb
, this);
1045 time_manager
->RemoveHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
1049 Surface::PaintToDrawable (GdkDrawable
*drawable
, GdkVisual
*visual
, GdkEventExpose
*event
, int off_x
, int off_y
, bool transparent
, bool clear_transparent
)
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()))
1060 SetCurrentDeployment ();
1063 STARTTIMER (expose
, "redraw");
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
);
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
);
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
1098 cairo_set_operator (ctx
, CAIRO_OPERATOR_OVER
);
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
);
1114 cairo_set_source_rgb (ctx
,
1115 background_color
->r
,
1116 background_color
->g
,
1117 background_color
->b
);
1120 cairo_fill_preserve (ctx
);
1124 Paint (ctx
, region
);
1125 cairo_restore (ctx
);
1127 if (RENDER_EXPOSE
) {
1128 cairo_new_path (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));
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
),
1141 cairo_surface_set_device_offset (cairo_get_target (ctx
),
1144 cairo_set_source_surface (native
, cairo_get_target (ctx
),
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
);
1160 ENDTIMER (expose
, "redraw");
1165 /* for emitting focus changed events */
1166 class FocusChangedNode
: public List::Node
{
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
;
1186 FocusChangedNode::~FocusChangedNode ()
1189 lost_focus
->unref ();
1191 got_focus
->unref ();
1194 RenderNode::RenderNode (UIElement
*el
,
1196 bool render_element
,
1203 this->region
= region
? region
: new Region ();
1204 this->render_element
= render_element
;
1205 this->pre_render
= pre
;
1206 this->post_render
= post
;
1210 RenderNode::Render (cairo_t
*ctx
)
1212 bool front_to_back
= uielement
->UseBackToFront ();
1215 pre_render (ctx
, uielement
, region
, front_to_back
);
1218 uielement
->Render (ctx
, region
);
1221 post_render (ctx
, uielement
, region
, front_to_back
);
1224 RenderNode::~RenderNode ()
1227 uielement
->unref ();
1235 UIElementNode::UIElementNode (UIElement
*el
)
1241 UIElementNode::~UIElementNode ()
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
1256 List
*new_input_list
= new List();
1258 new_input_list
->Append (new UIElementNode (capture
));
1259 capture
= capture
->GetVisualParent();
1263 input_list
= new_input_list
;
1264 pendingCapture
= NULL
;
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
1274 UIElement
*old_captured
= captured
;
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,
1283 HandleMouseEvent (NO_EVENT_ID
, false, true, false, mouse_event
);
1287 Surface::ReleaseMouseCapture (UIElement
*capture
)
1289 // Mouse capture is only released when the element owning the capture
1291 if (capture
!= captured
&& capture
!= pendingCapture
)
1294 if (emittingMouseEvent
)
1295 pendingReleaseCapture
= true;
1297 PerformReleaseCapture ();
1301 Surface::SetMouseCapture (UIElement
*capture
)
1303 if (captured
|| pendingCapture
)
1304 return capture
== captured
|| capture
== pendingCapture
;
1306 if (!emittingMouseEvent
)
1309 pendingCapture
= capture
;
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
);
1336 g_warning ("Unknown event id %d\n", event_id
);
1337 return new EventArgs();
1342 Surface::EmitEventOnList (int event_id
, List
*element_list
, GdkEvent
*event
, int end_idx
)
1344 bool handled
= false;
1347 UIElementNode
*node
;
1349 if (element_list
->IsEmpty() || end_idx
== 0)
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
++) {
1368 bool h
= node
->uielement
->DoEmit (event_id
, args
);
1376 if (args_are_routed
&& ((RoutedEventArgs
*)args
)->GetHandled())
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
]);
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.
1406 UIElementNode
*ui1
, *ui2
;
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
) {
1427 ui1
= (UIElementNode
*)ui1
->prev
;
1428 ui2
= (UIElementNode
*)ui2
->prev
;
1435 copy_input_list_from_node (List
*input_list
, UIElementNode
* node
)
1437 List
*list
= new List ();
1440 list
->Append (new UIElementNode (node
->uielement
));
1441 node
= (UIElementNode
*) node
->next
;
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
)
1470 emittingMouseEvent
= true;
1474 if (toplevel
== NULL
|| event
== NULL
)
1477 // FIXME this should probably use mouse event args
1478 ProcessDirtyElements();
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);
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 ();
1498 gdk_event_get_coords (event
, &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
);
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))
1514 if (!GetFocusedElement () && last
!= -1)
1515 FocusElement (layers
->GetValueAt (last
)->AsUIElement ());
1517 EmitFocusChangeEvents ();
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
1533 // If the first few nodes in each list differ, and,
1534 // for instance bn == am, we know that [am...] ==
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
1547 FindFirstCommonElement (input_list
, &surface_index
,
1548 new_input_list
, &new_index
);
1551 handled
= EmitEventOnList (UIElement::MouseLeaveEvent
, input_list
, event
, surface_index
);
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).
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
;
1579 measuring_context_destroy (ctx
);
1582 input_list
= new_input_list
;
1585 #define SPEW_INPUT_LIST 0
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())
1594 printf ("(%s)", node
->uielement
->GetName());
1600 // Perform any captures/releases that are pending after the
1601 // event is bubbled.
1603 PerformCapture (pendingCapture
);
1604 if (pendingReleaseCapture
|| (captured
&& !captured
->CanCaptureMouse ()))
1605 PerformReleaseCapture ();
1606 emittingMouseEvent
= false;
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
)
1624 SetCursor (new_cursor
);
1628 Surface::SetFPSReportFunc (MoonlightFPSReportFunc report
, void *user_data
)
1630 fps_report
= report
;
1631 fps_data
= user_data
;
1635 Surface::SetCacheReportFunc (MoonlightCacheReportFunc report
, void *user_data
)
1637 cache_report
= report
;
1638 cache_data
= user_data
;
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
{
1651 Downloader
*downloader
;
1652 DownloaderNode (Downloader
*dl
) { downloader
= dl
; }
1656 Surface::DetachDownloaders ()
1658 DownloaderNode
*node
;
1659 if (downloaders
== NULL
)
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);
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");
1683 node
= (DownloaderNode
*) downloaders
->First ();
1684 while (node
!= NULL
) {
1685 if (node
->downloader
== sender
) {
1686 downloaders
->Remove (node
);
1689 node
= (DownloaderNode
*) node
->next
;
1692 printf ("Surface::OnDownloaderDestroyed (): Couldn't find the downloader %p in the list of downloaders\n", sender
);
1696 Surface::CreateDownloader (void)
1699 g_warning ("Surface::CreateDownloader (): Trying to create a downloader on a zombified surface.\n");
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
));
1715 Surface::CreateDownloader (EventObject
*obj
)
1719 surface
= obj
->GetDeployment ()->GetSurface ();
1722 return surface
->CreateDownloader ();
1724 g_warning ("Surface::CreateDownloader (%p, ID: %i): Unable to create contextual downloader.\n",
1725 obj
, GET_OBJ_ID (obj
));
1731 Surface::VerifyWithCacheSizeCounter (int w
, int h
)
1733 if (! (moonlight_flags
& RUNTIME_INIT_USE_SHAPE_CACHE
))
1736 if (cache_size_multiplier
== -1)
1739 if (cache_size_in_bytes
+ (w
* h
* cache_size_multiplier
) < MAXIMUM_CACHE_SIZE
)
1746 Surface::AddToCacheSizeCounter (int w
, int h
)
1748 gint64 new_size
= w
* h
* cache_size_multiplier
;
1749 cache_size_in_bytes
+= new_size
;
1754 Surface::RemoveFromCacheSizeCounter (gint64 size
)
1756 cache_size_in_bytes
-= size
;
1760 Surface::FullScreenKeyHandled (GdkEventKey
*key
)
1762 if (!GetFullScreen ())
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
) {
1786 // Explicitly listing GDK_Escape here as it should never bubble up
1794 Surface::HandleUIFocusIn (GdkEventFocus
*event
)
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
;
1811 Surface::HandleUIFocusOut (GdkEventFocus
*event
)
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
;
1828 Surface::HandleUIButtonRelease (GdkEventButton
*event
)
1830 time_manager
->InvokeTickCalls();
1832 if (event
->button
!= 1 && event
->button
!= 3) {
1836 SetUserInitiatedEvent (true);
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.
1851 PerformReleaseCapture ();
1853 return !((moonlight_flags
& RUNTIME_INIT_DESKTOP_EXTENSIONS
) == 0 && event
->button
== 3);
1857 Surface::HandleUIButtonPress (GdkEventButton
*event
)
1862 active_window
->GrabFocus ();
1864 time_manager
->InvokeTickCalls();
1866 if (event
->button
!= 1 && event
->button
!= 3)
1869 SetUserInitiatedEvent (true);
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)
1882 handled
= HandleMouseEvent (UIElement::MouseLeftButtonMultiClickEvent
, false, false, true, mouse_event
);
1885 if (event
->button
== 1)
1886 event_id
= UIElement::MouseLeftButtonDownEvent
;
1888 event_id
= UIElement::MouseRightButtonDownEvent
;
1890 handled
= HandleMouseEvent (event_id
, true, true, true, mouse_event
);
1894 UpdateCursorFromInputList ();
1895 SetUserInitiatedEvent (false);
1901 Surface::HandleUIScroll (GdkEventScroll
*event
)
1903 time_manager
->InvokeTickCalls();
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 ();
1920 Surface::HandleUIMotion (GdkEventMotion
*event
)
1922 time_manager
->InvokeTickCalls();
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
);
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 ();
1953 Surface::HandleUICrossing (GdkEventCrossing
*event
)
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);
1966 g_object_unref (active_gdk_window);
1969 if (event
->type
== GDK_ENTER_NOTIFY
) {
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 ();
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
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
1988 PerformReleaseCapture ();
1990 // clear out the input list so we emit the right
1991 // events when the pointer reenters the control.
1992 if (!emittingMouseEvent
) {
1994 input_list
= new List();
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
);
2014 Surface::FocusElement (UIElement
*focused
)
2016 if (focused
== focused_element
)
2019 while (focused_element
) {
2020 focus_changed_events
->Append (new FocusChangedNode (focused_element
, NULL
));
2021 focused_element
= focused_element
->GetVisualParent ();
2024 focused_element
= focused
;
2027 focus_changed_events
->Append (new FocusChangedNode (NULL
, focused
));
2028 focused
= focused
->GetVisualParent ();
2031 if (FirstUserInitiatedEvent ())
2032 AddTickCall (Surface::EmitFocusChangeEventsAsync
);
2037 Surface::ElementPathToRoot (UIElement
*source
)
2039 List
*list
= new List();
2041 list
->Append (new UIElementNode (source
));
2042 source
= source
->GetVisualParent();
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 ())
2059 } else if (FullScreenKeyHandled (event
)) {
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);
2069 printf ("<--- DEBUG MARKER KEY OUT (%f) --->\n", get_now () / 10000000.0);
2070 debug_marker_key_in
= ! debug_marker_key_in
;
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
;
2086 // in silverlight 1.0, key events are only ever delivered to the toplevel
2087 toplevel
->EmitKeyDown (event
);
2091 SetUserInitiatedEvent (false);
2097 Surface::HandleUIKeyRelease (GdkEventKey
*event
)
2099 time_manager
->InvokeTickCalls();
2101 if (FullScreenKeyHandled (event
))
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
);
2121 SetUserInitiatedEvent (false);
2127 Surface::HandleUIWindowAllocation (bool emit_resize
)
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
;
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 ();
2164 Surface::GetBackgroundColor ()
2166 return background_color
;
2170 Surface::IsVersionSupported (const char *version_list
)
2172 /* we support all 0.*, 1.0.*, 1.1.* and 2.0.* versions. */
2173 bool supported
= true;
2175 char *version
= NULL
;
2178 if (version_list
== NULL
)
2181 versions
= g_strsplit (version_list
, ".", 4);
2183 supported
= versions
[0] != NULL
&& versions
[1] != NULL
;
2186 for (int k
= 0; k
< 4; k
++) {
2188 version
= versions
[k
];
2190 if (version
== NULL
)
2193 if (version
[0] == 0) {
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') {
2206 numbers
[k
] = atoll (version
);
2209 switch (numbers
[0]) {
2210 case 0: // We support all versions of the format "0.*" and "1.*"
2214 supported
&= numbers
[1] == 0; // 2.0.*
2217 supported
&= numbers
[1] == 0; // 3.0.*
2225 g_strfreev (versions
);
2231 runtime_init_browser (const char *plugin_dir
)
2233 runtime_init (plugin_dir
, RUNTIME_INIT_BROWSER
);
2237 runtime_init_desktop ()
2239 runtime_init (NULL
, RUNTIME_INIT_DESKTOP
);
2243 runtime_is_running_out_of_browser ()
2245 return (moonlight_flags
& RUNTIME_INIT_OUT_OF_BROWSER
) != 0;
2249 get_flags (gint32 def
, const char *envname
, struct env_options options
[])
2254 if (envname
&& (env
= g_getenv (envname
))) {
2255 printf ("%s = %s\n", envname
, env
);
2257 const char *flag
= env
;
2259 bool all
= !strcmp ("all", env
);
2263 while (*flag
== ',')
2269 while (*inptr
&& *inptr
!= ',')
2273 for (i
= 0; options
[i
].name
!= NULL
; i
++) {
2275 flags
|= options
[i
].flag
;
2279 if (n
!= strlen (options
[i
].name
))
2282 if (!strncmp (options
[i
].name
, flag
, n
)) {
2283 if (!options
[i
].set
)
2284 flags
&= ~options
[i
].flag
;
2286 flags
|= options
[i
].flag
;
2290 while (*inptr
== ',')
2300 runtime_init (const char *platform_dir
, guint32 flags
)
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
);
2326 if (!g_type_inited
) {
2327 g_type_inited
= true;
2331 moonlight_flags
= flags
;
2333 Deployment::Initialize (platform_dir
, (flags
& RUNTIME_INIT_CREATE_ROOT_DOMAIN
) != 0);
2337 Media::Initialize ();
2341 runtime_shutdown (void)