1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * runtime.cpp: Core surface and canvas definitions.
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"
50 #include "window-gtk.h"
51 #include "timemanager.h"
53 #include "contentcontrol.h"
54 #include "usercontrol.h"
55 #include "deployment.h"
58 #include "tabnavigationwalker.h"
60 //#define DEBUG_INVALIDATE 1
61 //#define RENDER_INDIVIDUALLY 1
62 #define DEBUG_REFCNT 0
68 #define NO_EVENT_ID -1
70 bool Surface::main_thread_inited
= false;
71 pthread_t
Surface::main_thread
= 0;
73 static bool inited
= false;
74 static bool g_type_inited
= false;
75 static GList
* surface_list
= NULL
;
76 guint32 moonlight_flags
= 0;
78 guint32 debug_flags_ex
= 0;
79 guint32 debug_flags
= 0;
89 static struct env_options overrides
[] = {
90 // There's no "ms-codecs=yes" option to not allow enabling them from the command line.
91 { "ms-codecs=no", RUNTIME_INIT_ENABLE_MS_CODECS
, false },
92 { "ffmpeg-codecs=no", RUNTIME_INIT_DISABLE_FFMPEG_CODECS
, true },
93 { "ffmpeg-codecs=yes", RUNTIME_INIT_DISABLE_FFMPEG_CODECS
, false },
94 { "timesource=manual", RUNTIME_INIT_MANUAL_TIMESOURCE
, true },
95 { "timesource=system", RUNTIME_INIT_MANUAL_TIMESOURCE
, false },
96 { "expose=show", RUNTIME_INIT_SHOW_EXPOSE
, true },
97 { "expose=hide", RUNTIME_INIT_SHOW_EXPOSE
, false },
98 { "clipping=show", RUNTIME_INIT_SHOW_CLIPPING
, true },
99 { "clipping=hide", RUNTIME_INIT_SHOW_CLIPPING
, false },
100 { "bbox=show", RUNTIME_INIT_SHOW_BOUNDING_BOXES
, true },
101 { "bbox=hide", RUNTIME_INIT_SHOW_BOUNDING_BOXES
, false },
102 { "textbox=show", RUNTIME_INIT_SHOW_TEXTBOXES
, true },
103 { "textbox=hide", RUNTIME_INIT_SHOW_TEXTBOXES
, false },
104 { "fps=show", RUNTIME_INIT_SHOW_FPS
, true },
105 { "fps=hide", RUNTIME_INIT_SHOW_FPS
, false },
106 { "render=ftb", RUNTIME_INIT_RENDER_FRONT_TO_BACK
, true },
107 { "render=btf", RUNTIME_INIT_RENDER_FRONT_TO_BACK
, false },
108 { "cache=show", RUNTIME_INIT_SHOW_CACHE_SIZE
, true },
109 { "cache=hide", RUNTIME_INIT_SHOW_CACHE_SIZE
, false },
110 { "converter=default", RUNTIME_INIT_FFMPEG_YUV_CONVERTER
, false },
111 { "converter=ffmpeg", RUNTIME_INIT_FFMPEG_YUV_CONVERTER
, true },
112 { "shapecache=yes", RUNTIME_INIT_USE_SHAPE_CACHE
, true },
113 { "shapecache=no", RUNTIME_INIT_USE_SHAPE_CACHE
, false },
114 { "updatepos=yes", RUNTIME_INIT_USE_UPDATE_POSITION
, true },
115 { "updatepos=no", RUNTIME_INIT_USE_UPDATE_POSITION
, false },
116 { "windowless=yes", RUNTIME_INIT_ALLOW_WINDOWLESS
, true },
117 { "windowless=no", RUNTIME_INIT_ALLOW_WINDOWLESS
, false },
118 { "audio=alsa", RUNTIME_INIT_AUDIO_ALSA
, true },
119 { "audio=alsa-mmap", RUNTIME_INIT_AUDIO_ALSA_MMAP
, true },
120 { "audio=alsa-rw", RUNTIME_INIT_AUDIO_ALSA_RW
, true },
121 { "audio=pulseaudio", RUNTIME_INIT_AUDIO_PULSE
, true },
122 { "idlehint=yes", RUNTIME_INIT_USE_IDLE_HINT
, false },
123 { "idlehint=no", RUNTIME_INIT_USE_IDLE_HINT
, true },
124 { "backend=xlib", RUNTIME_INIT_USE_BACKEND_XLIB
, true },
125 { "backend=image", RUNTIME_INIT_USE_BACKEND_XLIB
, false },
126 { "keepmedia=no", RUNTIME_INIT_KEEP_MEDIA
, false },
127 { "keepmedia=yes", RUNTIME_INIT_KEEP_MEDIA
, true },
128 { "allimages=no", RUNTIME_INIT_ALL_IMAGE_FORMATS
, false },
129 { "allimages=yes", RUNTIME_INIT_ALL_IMAGE_FORMATS
, true },
132 #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_DESKTOP_EXTENSIONS)
133 #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)
136 static struct env_options debugs
[] = {
137 { "alsa", RUNTIME_DEBUG_ALSA
, true },
138 { "audio", RUNTIME_DEBUG_AUDIO
, true },
139 { "pulse", RUNTIME_DEBUG_PULSE
, true },
140 { "httpstreaming", RUNTIME_DEBUG_HTTPSTREAMING
, true },
141 { "markers", RUNTIME_DEBUG_MARKERS
, true },
142 { "mms", RUNTIME_DEBUG_MMS
, true },
143 { "mediaplayer", RUNTIME_DEBUG_MEDIAPLAYER
, true },
144 { "pipeline", RUNTIME_DEBUG_PIPELINE
, true },
145 { "pipeline-error", RUNTIME_DEBUG_PIPELINE_ERROR
, true },
146 { "framereaderloop", RUNTIME_DEBUG_FRAMEREADERLOOP
, true },
147 { "ui", RUNTIME_DEBUG_UI
, true },
148 { "ffmpeg", RUNTIME_DEBUG_FFMPEG
, true },
149 { "codecs", RUNTIME_DEBUG_CODECS
, true },
150 { "dependencyobject", RUNTIME_DEBUG_DP
, true },
151 { "downloader", RUNTIME_DEBUG_DOWNLOADER
, true },
152 { "font", RUNTIME_DEBUG_FONT
, true },
153 { "layout", RUNTIME_DEBUG_LAYOUT
, true },
154 { "media", RUNTIME_DEBUG_MEDIA
, true },
155 { "mediaelement", RUNTIME_DEBUG_MEDIAELEMENT
, true },
156 { "msi", RUNTIME_DEBUG_MSI
, true },
157 { "buffering", RUNTIME_DEBUG_BUFFERING
, true },
158 { "asf", RUNTIME_DEBUG_ASF
, true },
159 { "playlist", RUNTIME_DEBUG_PLAYLIST
, true },
160 { "text", RUNTIME_DEBUG_TEXT
, true },
161 { "xaml", RUNTIME_DEBUG_XAML
, true },
162 { "deployment", RUNTIME_DEBUG_DEPLOYMENT
, true },
163 { "mp3", RUNTIME_DEBUG_MP3
, true },
164 { "asf", RUNTIME_DEBUG_ASF
, true },
165 { "value", RUNTIME_DEBUG_VALUE
, true },
169 static struct env_options debug_extras
[] = {
170 { "alsa-ex", RUNTIME_DEBUG_ALSA_EX
, true },
171 { "audio-ex", RUNTIME_DEBUG_AUDIO_EX
, true },
172 { "pulse-ex", RUNTIME_DEBUG_PULSE_EX
, true },
173 { "markers-ex", RUNTIME_DEBUG_MARKERS_EX
, true },
174 { "mediaplayer-ex", RUNTIME_DEBUG_MEDIAPLAYER_EX
, true },
175 { "mediaelement-ex", RUNTIME_DEBUG_MEDIAELEMENT_EX
, true },
176 { "playlist-ex", RUNTIME_DEBUG_PLAYLIST_EX
, true },
177 { "pipeline-ex", RUNTIME_DEBUG_PIPELINE_EX
, true },
184 #define RENDER_EXPOSE (moonlight_flags & RUNTIME_INIT_SHOW_EXPOSE)
189 int event
, error
, opcode
;
191 Display
*display
= XOpenDisplay (NULL
);
192 bool result
= XQueryExtension (display
, "NV-GLX", &opcode
, &event
, &error
);
193 XCloseDisplay (display
);
199 fps_report_default (Surface
*surface
, int nframes
, float nsecs
, void *user_data
)
201 printf ("Rendered %d frames in %.3fs = %.3f FPS\n", nframes
, nsecs
, nframes
/ nsecs
);
205 cache_report_default (Surface
*surface
, long bytes
, void *user_data
)
207 printf ("Cache size is ~%.3f MB\n", bytes
/ 1048576.0);
211 runtime_get_surface_list (void)
213 if (!Surface::InMainThread ()) {
214 g_warning ("This method can be only called from the main thread!\n");
222 runtime_cairo_create (GdkWindow
*drawable
, GdkVisual
*visual
, bool native
)
225 cairo_surface_t
*surface
;
228 gdk_drawable_get_size (drawable
, &width
, &height
);
231 surface
= cairo_xlib_surface_create (gdk_x11_drawable_get_xdisplay (drawable
),
232 gdk_x11_drawable_get_xid (drawable
),
233 GDK_VISUAL_XVISUAL (visual
),
236 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, width
, height
);
238 cr
= cairo_create (surface
);
239 cairo_surface_destroy (surface
);
245 flags_can_be_modifed (void)
247 if (g_list_length (surface_list
) != 0) {
248 g_warning ("Flags can be dynamically modified only when there are no surfaces created!");
250 } else if (inited
== FALSE
) {
251 g_warning ("Runtime has not been initialized yet, your flags will be overriden!");
258 runtime_flags_set_manual_timesource (gboolean flag
)
260 if (flags_can_be_modifed ())
261 moonlight_flags
|= RUNTIME_INIT_MANUAL_TIMESOURCE
;
265 runtime_flags_set_use_shapecache (gboolean flag
)
267 if (flags_can_be_modifed ())
268 moonlight_flags
|= RUNTIME_INIT_USE_SHAPE_CACHE
;
272 runtime_flags_set_show_fps (gboolean flag
)
274 if (flags_can_be_modifed ())
275 moonlight_flags
|= RUNTIME_INIT_SHOW_FPS
;
278 /* FIXME More flag setters here */
280 Surface::Surface (MoonWindow
*window
)
282 SetObjectType (Type::SURFACE
);
284 GetDeployment ()->SetSurface (this);
286 main_thread
= pthread_self ();
287 main_thread_inited
= true;
290 needs_measure
= false;
291 needs_arrange
= false;
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 Queue ();
319 first_user_initiated_event
= false;
320 user_initiated_event
= false;
322 full_screen_message
= NULL
;
323 source_location
= NULL
;
325 fps_report
= fps_report_default
;
331 cache_report
= cache_report_default
;
334 cache_size_in_bytes
= 0;
335 cache_size_ticker
= 0;
336 cache_size_multiplier
= -1;
338 expose_handoff
= NULL
;
339 expose_handoff_data
= NULL
;
340 expose_handoff_last_timespan
= G_MAXINT64
;
342 emittingMouseEvent
= false;
343 pendingCapture
= NULL
;
344 pendingReleaseCapture
= false;
347 debug_selected_element
= NULL
;
350 up_dirty
= new DirtyLists (true);
351 down_dirty
= new DirtyLists (false);
353 surface_list
= g_list_append (surface_list
, this);
358 time_manager
->RemoveHandler (TimeManager::RenderEvent
, render_cb
, this);
359 time_manager
->RemoveHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
362 toplevel
->SetSurface (NULL
);
367 if (debug_selected_element
) {
368 debug_selected_element
->unref ();
369 debug_selected_element
= NULL
;
373 HideFullScreenMessage ();
377 g_free (source_location
);
379 if (fullscreen_window
)
380 delete fullscreen_window
;
383 delete normal_window
;
385 delete background_color
;
387 time_manager
->unref ();
395 surface_list
= g_list_remove (surface_list
, this);
402 toplevel
->SetSurface (NULL
);
403 toplevel
->Dispose ();
406 EventObject::Dispose ();
412 time_manager
->Shutdown ();
413 DetachDownloaders ();
418 Surface::SetCursor (MouseCursor new_cursor
)
420 if (new_cursor
!= cursor
) {
423 active_window
->SetCursor (cursor
);
428 Surface::AutoFocus ()
430 GenerateFocusChangeEvents ();
434 Surface::AutoFocusAsync (EventObject
*sender
)
436 ((Surface
*)sender
)->AutoFocus ();
440 Surface::Attach (UIElement
*element
)
445 // Attach must be called with NULL to clear out the old canvas
446 // before attaching another canvas, otherwise the new canvas
447 // might get loaded with data from the old canvas (when parsing
448 // xaml ticks will get added to the timemanager of the surface,
449 // if the old canvas isn't gone when the new canvas is parsed,
450 // the ticks will be added to the old timemanager).
451 if (toplevel
!= NULL
&& element
!= NULL
)
452 g_warning ("Surface::Attach (NULL) should be called to clear out the old canvas before adding a new canvas.");
456 if (element
!= NULL
&& element
->GetDeployment () != GetDeployment ())
457 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 ());
458 if (GetDeployment () != Deployment::GetCurrent ())
459 g_warning ("Surface::Attach (%p): current deployment is %p, surface deployment is %p\n", element
, GetDeployment (), Deployment::GetCurrent ());
464 toplevel
->RemoveHandler (UIElement::LoadedEvent
, toplevel_loaded
, this);
465 DetachLayer (toplevel
);
466 time_manager
->RemoveHandler (TimeManager::RenderEvent
, render_cb
, this);
467 time_manager
->RemoveHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
468 time_manager
->Stop ();
469 int maxframerate
= time_manager
->GetMaximumRefreshRate ();
471 time_manager
->unref ();
472 time_manager
= new TimeManager ();
473 time_manager
->AddHandler (TimeManager::RenderEvent
, render_cb
, this);
474 time_manager
->AddHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
475 time_manager
->SetMaximumRefreshRate (maxframerate
);
476 time_manager
->NeedRedraw ();
477 time_manager
->Start ();
482 DetachDownloaders ();
485 active_window
->EnableEvents (first
);
487 active_window
->Invalidate();
493 if (!element
->Is (Type::UIELEMENT
)) {
494 printf ("Surface::Attach Unsupported toplevel %s\n", Type::Find (element
->GetObjectType ())->GetName ());
498 UIElement
*canvas
= element
;
501 // make sure we have a namescope at the toplevel so that names
502 // can be registered/resolved properly.
503 if (NameScope::GetNameScope (canvas
) == NULL
) {
504 NameScope::SetNameScope (canvas
, new NameScope());
507 // First time we connect the surface, start responding to events
509 active_window
->EnableEvents (first
);
515 AttachLayer (canvas
);
518 canvas
->AddHandler (UIElement::LoadedEvent
, toplevel_loaded
, this, (GDestroyNotify
)event_object_unref
);
520 ticked_after_attach
= false;
521 time_manager
->RemoveTickCall (tick_after_attach_reached
, this);
522 time_manager
->AddTickCall (tick_after_attach_reached
, this);
526 Surface::tick_after_attach_reached (EventObject
*data
)
528 Surface
*surface
= (Surface
*)data
;
530 surface
->ticked_after_attach
= true;
531 surface
->Emit (Surface::LoadEvent
);
535 Surface::toplevel_loaded (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
537 ((Surface
*)closure
)->ToplevelLoaded ((UIElement
*)sender
);
541 Surface::ToplevelLoaded (UIElement
*element
)
543 if (element
== toplevel
) {
544 toplevel
->RemoveHandler (UIElement::LoadedEvent
, toplevel_loaded
, this);
546 // FIXME: If the element is supposed to be focused, FocusElement (element)
547 // should be used. I think this is unnecessary anyway.
548 //if (active_window && active_window->HasFocus())
549 // element->EmitGotFocus ();
552 // If the did not get a size specified
554 if (normal_window
&& normal_window
->GetWidth() == 0 && normal_window
->GetHeight() == 0 && toplevel
) {
556 * this should only be hit in the nonplugin case ans is
557 * simply here to give a reasonable default size
560 vw
= toplevel
->GetValue (FrameworkElement::WidthProperty
);
561 vh
= toplevel
->GetValue (FrameworkElement::HeightProperty
);
563 normal_window
->Resize (MAX (vw
? (int)vw
->AsDouble () : 0, 0),
564 MAX (vh
? (int)vh
->AsDouble () : 0, 0));
569 element
->UpdateTotalRenderVisibility ();
570 element
->UpdateTotalHitTestVisibility ();
571 element
->FullInvalidate (true);
573 // we call this two here so that the layout pass proceeds when
574 // we next process the dirty list.
575 element
->InvalidateMeasure ();
580 Surface::AttachLayer (UIElement
*layer
)
582 if (layer
== toplevel
)
583 layers
->Insert (0, Value(layer
));
585 layers
->Add (Value (layer
));
587 layer
->SetSurface (this);
588 layer
->FullInvalidate (true);
590 List
*list
= layer
->WalkTreeForLoaded (NULL
);
591 layer
->PostSubtreeLoad (list
);
592 // PostSubtreeLoad will take care of deleting the list for us.
596 Surface::DetachLayer (UIElement
*layer
)
598 layers
->Remove (Value (layer
));
599 layer
->SetSurface (NULL
);
601 Invalidate (layer
->GetBounds ());
605 Surface::Invalidate (Rect r
)
607 active_window
->Invalidate (r
);
611 Surface::ProcessUpdates ()
613 active_window
->ProcessUpdates();
617 Surface::Paint (cairo_t
*ctx
, int x
, int y
, int width
, int height
)
619 Rect r
= Rect (x
, y
, width
, height
);
620 Region region
= Region (r
);
621 Paint (ctx
, ®ion
);
625 Surface::Paint (cairo_t
*ctx
, Region
*region
)
627 for (int i
= 0; i
< layers
->GetCount (); i
++) {
628 UIElement
*layer
= layers
->GetValueAt (i
)->AsUIElement ();
629 layer
->Paint (ctx
, region
, NULL
);
633 if (debug_selected_element
) {
634 Rect bounds
= debug_selected_element
->GetSubtreeBounds();
635 // printf ("debug_selected_element is %s\n", debug_selected_element->GetName());
636 // printf ("bounds is %g %g %g %g\n", bounds.x, bounds.y, bounds.w, bounds.h);
638 //RenderClipPath (ctx);
639 cairo_new_path (ctx
);
640 cairo_identity_matrix (ctx
);
641 cairo_set_source_rgba (ctx
, 1.0, 0.5, 0.2, 1.0);
642 cairo_set_line_width (ctx
, 1);
643 cairo_rectangle (ctx
, bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
);
651 // This will resize the surface (merely a convenience function for
652 // resizing the widget area that we have.
654 // This will not change the Width and Height properties of the
655 // toplevel canvas, if you want that, you must do that yourself
658 Surface::Resize (int width
, int height
)
660 if (width
== normal_window
->GetWidth()
661 && height
== normal_window
->GetHeight())
664 normal_window
->Resize (width
, height
);
668 Surface::EmitSourceDownloadComplete ()
670 Emit (SourceDownloadCompleteEvent
, NULL
);
674 Surface::EmitSourceDownloadProgressChanged (DownloadProgressEventArgs
*args
)
676 Emit (SourceDownloadProgressChangedEvent
, args
);
680 Surface::EmitError (ErrorEventArgs
*args
)
682 Emit (ErrorEvent
, args
);
686 Surface::EmitError (int number
, int code
, const char *message
)
688 EmitError (new ErrorEventArgs ((ErrorEventArgsType
)number
,
689 MoonError (MoonError::EXCEPTION
, code
, message
)));
695 for (int i
= 0; i
< layers
->GetCount (); i
++) {
696 UIElement
*layer
= layers
->GetValueAt (i
)->AsUIElement ();
698 layer
->InvalidateMeasure ();
699 //layer->UpdateBounds();
704 Surface::SetFullScreen (bool value
)
706 if (value
&& !IsUserInitiatedEvent ()) {
707 g_warning ("You're not allowed to switch to fullscreen from where you're doing it.");
711 UpdateFullScreen (value
);
715 Surface::SetUserInitiatedEvent (bool value
)
717 GenerateFocusChangeEvents ();
718 first_user_initiated_event
= first_user_initiated_event
| value
;
719 user_initiated_event
= value
;
723 Surface::IsTopLevel (UIElement
* top
)
728 bool ret
= top
== full_screen_message
;
730 for (int i
= 0; i
< layers
->GetCount () && !ret
; i
++)
731 ret
= layers
->GetValueAt (i
)->AsUIElement () == top
;
737 Surface::ShowFullScreenMessage ()
739 g_return_if_fail (full_screen_message
== NULL
);
740 //g_return_if_fail (toplevel && toplevel->Is (Type::PANEL));
743 XamlLoader
*loader
= new XamlLoader (NULL
, FULLSCREEN_MESSAGE
, this);
744 DependencyObject
* message
= loader
->CreateDependencyObjectFromString (FULLSCREEN_MESSAGE
, false, &dummy
);
748 printf ("Unable to create fullscreen message.\n");
752 if (!message
->Is (Type::CANVAS
)) {
753 printf ("Unable to create fullscreen message, got a %s, expected at least a UIElement.\n", message
->GetTypeName ());
758 full_screen_message
= (Canvas
*) message
;
759 AttachLayer (full_screen_message
);
761 DependencyObject
* message_object
= full_screen_message
->FindName ("message");
762 DependencyObject
* url_object
= full_screen_message
->FindName ("url");
763 TextBlock
* message_block
= (message_object
!= NULL
&& message_object
->Is (Type::TEXTBLOCK
)) ? (TextBlock
*) message_object
: NULL
;
764 TextBlock
* url_block
= (url_object
!= NULL
&& url_object
->Is (Type::TEXTBLOCK
)) ? (TextBlock
*) url_object
: NULL
;
766 Transform
* transform
= full_screen_message
->GetRenderTransform ();
768 double box_height
= full_screen_message
->GetHeight ();
769 double box_width
= full_screen_message
->GetWidth ();
771 // Set the url in the box
772 if (url_block
!= NULL
) {
775 if (source_location
) {
776 if (g_str_has_prefix (source_location
, "http://")) {
777 const char *path
= strchr (source_location
+ 7, '/');
779 if (path
!= NULL
&& path
> source_location
+ 7) {
780 url
= g_strndup (source_location
, path
- source_location
);
782 url
= g_strdup (source_location
);
784 } else if (g_str_has_prefix (source_location
, "file://")) {
785 url
= g_strdup ("file://");
787 url
= g_strdup (source_location
);
791 url_block
->SetValue (TextBlock::TextProperty
, url
? url
: (char *) "file://");
795 // The box is not made bigger if the url doesn't fit.
796 // MS has an interesting text rendering if the url doesn't
797 // fit: the text is overflown to the left.
798 // Since only the server is shown, this shouldn't
799 // happen on a regular basis though.
801 // Center the url block
802 if (url_block
!= NULL
) {
803 double url_width
= url_block
->GetActualWidth ();
804 Canvas::SetLeft (url_block
, (box_width
- url_width
) / 2);
807 // Center the message block
808 if (message_block
!= NULL
) {
809 double message_width
= message_block
->GetActualWidth ();
810 Canvas::SetLeft (message_block
, (box_width
- message_width
) / 2);
813 // Put the box in the middle of the screen
814 transform
->SetValue (TranslateTransform::XProperty
, Value ((active_window
->GetWidth() - box_width
) / 2));
815 transform
->SetValue (TranslateTransform::YProperty
, Value ((active_window
->GetHeight() - box_height
) / 2));
819 Surface::GetSourceLocation ()
821 return source_location
;
825 Surface::SetSourceLocation (const char* location
)
827 g_free (source_location
);
828 source_location
= g_strdup (location
);
832 Surface::HideFullScreenMessage ()
834 if (full_screen_message
) {
835 DetachLayer (full_screen_message
);
836 full_screen_message
->unref ();
837 full_screen_message
= NULL
;
842 Surface::UpdateFullScreen (bool value
)
844 if (value
== full_screen
)
848 fullscreen_window
= new MoonWindowGtk (true, -1, -1, normal_window
);
849 fullscreen_window
->SetSurface (this);
851 active_window
= fullscreen_window
;
853 ShowFullScreenMessage ();
855 fullscreen_window
->EnableEvents (false);
857 active_window
= normal_window
;
859 HideFullScreenMessage ();
861 delete fullscreen_window
;
862 fullscreen_window
= NULL
;
869 time_manager
->GetSource()->Stop();
870 Emit (FullScreenChangeEvent
);
874 time_manager
->GetSource()->Start();
878 Surface::render_cb (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
880 Surface
*s
= (Surface
*) closure
;
884 GDK_THREADS_ENTER ();
886 s
->up_dirty
->Clear (true);
887 s
->down_dirty
->Clear (true);
889 dirty
= s
->ProcessDirtyElements ();
892 if (s
->expose_handoff
) {
893 TimeSpan time
= s
->GetTimeManager ()->GetCurrentTime ();
894 if (time
!= s
->expose_handoff_last_timespan
) {
895 s
->expose_handoff (s
, time
, s
->expose_handoff_data
);
896 s
->expose_handoff_last_timespan
= time
;
900 GDK_THREADS_LEAVE ();
902 if ((moonlight_flags
& RUNTIME_INIT_SHOW_FPS
) && s
->fps_start
== 0)
903 s
->fps_start
= get_now ();
906 s
->ProcessUpdates ();
909 if ((moonlight_flags
& RUNTIME_INIT_SHOW_FPS
) && s
->fps_report
) {
912 if ((now
= get_now ()) > (s
->fps_start
+ TIMESPANTICKS_IN_SECOND
)) {
913 float nsecs
= (now
- s
->fps_start
) / TIMESPANTICKS_IN_SECOND_FLOAT
;
915 s
->fps_report (s
, s
->fps_nframes
, nsecs
, s
->fps_data
);
921 if ((moonlight_flags
& RUNTIME_INIT_SHOW_CACHE_SIZE
) && s
->cache_report
) {
922 // By default we report cache status every 50 render's.
923 // Should be enough for everybody, but syncing to ie. 1s sounds
925 if (s
->cache_size_ticker
== 50) {
926 s
->cache_report (s
, s
->cache_size_in_bytes
, s
->cache_data
);
927 s
->cache_size_ticker
= 0;
929 s
->cache_size_ticker
++;
934 Surface::update_input_cb (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
937 Surface
*s
= (Surface
*) closure
;
939 s
->HandleMouseEvent (UIElement::MouseMoveEvent
, true, true, false, s
->mouse_event_state
, s
->mouse_event_x
, s
->mouse_event_y
);
940 s
->UpdateCursorFromInputList ()
945 Surface::HandleUIWindowAvailable ()
947 time_manager
->AddHandler (TimeManager::RenderEvent
, render_cb
, this);
948 time_manager
->AddHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
950 time_manager
->NeedRedraw ();
954 Surface::HandleUIWindowUnavailable ()
956 time_manager
->RemoveHandler (TimeManager::RenderEvent
, render_cb
, this);
957 time_manager
->RemoveHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
961 Surface::PaintToDrawable (GdkDrawable
*drawable
, GdkVisual
*visual
, GdkEventExpose
*event
, int off_x
, int off_y
, bool transparent
, bool clear_transparent
)
965 LOG_UI ("Surface::PaintToDrawable (%p, %p, (%d,%d %d,%d), %d, %d, %d, %d)\n",
966 drawable
, visual
, event
->area
.x
, event
->area
.y
, event
->area
.width
, event
->area
.height
,
967 off_x
, off_y
, transparent
, clear_transparent
);
969 if (event
->area
.x
> (off_x
+ active_window
->GetWidth()) || event
->area
.y
> (off_y
+ active_window
->GetHeight()))
972 SetCurrentDeployment ();
975 STARTTIMER (expose
, "redraw");
977 if (cache_size_multiplier
== -1)
978 cache_size_multiplier
= gdk_drawable_get_depth (drawable
) / 8 + 1;
980 #ifdef DEBUG_INVALIDATE
981 printf ("Got a request to repaint at %d %d %d %d\n", event
->area
.x
, event
->area
.y
, event
->area
.width
, event
->area
.height
);
983 cairo_t
*ctx
= runtime_cairo_create (drawable
, visual
, moonlight_flags
& RUNTIME_INIT_USE_BACKEND_XLIB
);
984 Region
*region
= new Region (event
->region
);
986 region
->Offset (-off_x
, -off_y
);
987 cairo_surface_set_device_offset (cairo_get_target (ctx
),
988 off_x
- event
->area
.x
,
989 off_y
- event
->area
.y
);
992 // These are temporary while we change this to paint at the offset position
993 // instead of using the old approach of modifying the topmost Canvas (a no-no),
995 // The flag "transparent" is here because I could not
996 // figure out what is painting the background with white now.
997 // The change that made the white painting implicit instead of
998 // explicit is patch 80632. I would appreciate any help in tracking down
999 // the proper way of making the background white when not running in
1000 // "transparent" mode.
1002 // Either exposing surface_set_trans to turn the next code is a hack,
1003 // or it is normal to request all code that wants to paint to manually
1004 // clear the background to white beforehand. For now am going with
1005 // making this an explicit surface API.
1007 // The second part is for coping with the future: when we support being
1010 cairo_set_operator (ctx
, CAIRO_OPERATOR_OVER
);
1013 if (clear_transparent
) {
1014 cairo_set_operator (ctx
, CAIRO_OPERATOR_CLEAR
);
1015 cairo_fill_preserve (ctx
);
1016 cairo_set_operator (ctx
, CAIRO_OPERATOR_OVER
);
1019 cairo_set_source_rgba (ctx
,
1020 background_color
->r
,
1021 background_color
->g
,
1022 background_color
->b
,
1023 background_color
->a
);
1026 cairo_set_source_rgb (ctx
,
1027 background_color
->r
,
1028 background_color
->g
,
1029 background_color
->b
);
1032 cairo_fill_preserve (ctx
);
1036 Paint (ctx
, region
);
1037 cairo_restore (ctx
);
1039 if (RENDER_EXPOSE
) {
1040 cairo_new_path (ctx
);
1042 cairo_set_line_width (ctx
, 2.0);
1043 cairo_set_source_rgb (ctx
, (double)(frames
% 2), (double)((frames
+ 1) % 2), (double)((frames
/ 3) % 2));
1047 if (!(moonlight_flags
& RUNTIME_INIT_USE_BACKEND_XLIB
)) {
1048 cairo_surface_flush (cairo_get_target (ctx
));
1049 cairo_t
*native
= runtime_cairo_create (drawable
, visual
, true);
1051 cairo_surface_set_device_offset (cairo_get_target (native
),
1053 cairo_surface_set_device_offset (cairo_get_target (ctx
),
1056 cairo_set_source_surface (native
, cairo_get_target (ctx
),
1059 region
->Offset (off_x
, off_y
);
1060 region
->Offset (-event
->area
.x
, -event
->area
.y
);
1061 region
->Draw (native
);
1063 cairo_fill (native
);
1064 cairo_destroy (native
);
1066 cairo_destroy (ctx
);
1072 ENDTIMER (expose
, "redraw");
1077 /* for emitting focus changed events */
1078 class FocusChangedNode
: public List::Node
{
1080 UIElement
*lost_focus
;
1081 UIElement
*got_focus
;
1083 FocusChangedNode (UIElement
*lost_focus
, UIElement
*got_focus
);
1084 virtual ~FocusChangedNode ();
1087 FocusChangedNode::FocusChangedNode (UIElement
*lost_focus
, UIElement
*got_focus
)
1089 this->lost_focus
= lost_focus
;
1090 this->got_focus
= got_focus
;
1098 FocusChangedNode::~FocusChangedNode ()
1101 lost_focus
->unref ();
1103 got_focus
->unref ();
1106 RenderNode::RenderNode (UIElement
*el
,
1108 bool render_element
,
1115 this->region
= region
? region
: new Region ();
1116 this->render_element
= render_element
;
1117 this->pre_render
= pre
;
1118 this->post_render
= post
;
1122 RenderNode::Render (cairo_t
*ctx
)
1124 bool front_to_back
= uielement
->UseBackToFront ();
1127 pre_render (ctx
, uielement
, region
, front_to_back
);
1130 uielement
->Render (ctx
, region
);
1133 post_render (ctx
, uielement
, region
, front_to_back
);
1136 RenderNode::~RenderNode ()
1139 uielement
->unref ();
1147 UIElementNode::UIElementNode (UIElement
*el
)
1153 UIElementNode::~UIElementNode ()
1160 Surface::PerformCapture (UIElement
*capture
)
1162 // "Capturing" the mouse pointer at an element forces us to
1163 // use the path up the hierarchy from that element to the root
1164 // as the input list, regardless of where the pointer actually
1168 List
*new_input_list
= new List();
1170 new_input_list
->Append (new UIElementNode (capture
));
1171 capture
= capture
->GetVisualParent();
1175 input_list
= new_input_list
;
1176 pendingCapture
= NULL
;
1180 Surface::PerformReleaseCapture ()
1182 // These need to be set before calling HandleMouseEvent as
1183 // "captured" determines the input_list calculation, and
1184 // "pendingReleaseCapture", when set, causes an infinite
1186 UIElement
*old_captured
= captured
;
1188 pendingReleaseCapture
= false;
1190 old_captured
->EmitLostMouseCapture ();
1192 // this causes any new elements we're over to be Enter'ed. MS
1193 // doesn't Leave the element that had the mouse captured,
1195 HandleMouseEvent (NO_EVENT_ID
, false, true, false, mouse_event
);
1199 Surface::ReleaseMouseCapture (UIElement
*capture
)
1201 // Mouse capture is only released when the element owning the capture
1203 if (capture
!= captured
&& capture
!= pendingCapture
)
1206 if (emittingMouseEvent
)
1207 pendingReleaseCapture
= true;
1209 PerformReleaseCapture ();
1213 Surface::SetMouseCapture (UIElement
*capture
)
1215 if (captured
|| pendingCapture
)
1216 return capture
== captured
|| capture
== pendingCapture
;
1218 if (!emittingMouseEvent
)
1221 pendingCapture
= capture
;
1226 Surface::CreateArgsForEvent (int event_id
, GdkEvent
*event
)
1228 if (event_id
==UIElement::InvalidatedEvent
1229 || event_id
==UIElement::GotFocusEvent
1230 || event_id
==UIElement::LostFocusEvent
)
1231 return new RoutedEventArgs ();
1232 else if (event_id
== UIElement::MouseLeaveEvent
1233 || event_id
==UIElement::MouseMoveEvent
1234 || event_id
==UIElement::MouseEnterEvent
)
1235 return new MouseEventArgs(event
);
1236 else if (event_id
==UIElement::MouseLeftButtonMultiClickEvent
1237 || event_id
==UIElement::MouseLeftButtonDownEvent
1238 || event_id
==UIElement::MouseLeftButtonUpEvent
1239 || event_id
==UIElement::MouseRightButtonDownEvent
1240 || event_id
==UIElement::MouseRightButtonUpEvent
)
1241 return new MouseButtonEventArgs(event
);
1242 else if (event_id
== UIElement::MouseWheelEvent
)
1243 return new MouseWheelEventArgs(event
);
1244 else if (event_id
== UIElement::KeyDownEvent
1245 || event_id
== UIElement::KeyUpEvent
)
1246 return new KeyEventArgs((GdkEventKey
*)event
);
1248 g_warning ("Unknown event id %d\n", event_id
);
1249 return new EventArgs();
1254 Surface::EmitEventOnList (int event_id
, List
*element_list
, GdkEvent
*event
, int end_idx
)
1256 bool handled
= false;
1259 UIElementNode
*node
;
1261 if (element_list
->IsEmpty() || end_idx
== 0)
1265 end_idx
= element_list
->Length();
1267 EmitContext
** emit_ctxs
= g_new (EmitContext
*, end_idx
+ 1);
1268 for (node
= (UIElementNode
*)element_list
->First(), idx
= 0; node
&& idx
< end_idx
; node
= (UIElementNode
*)node
->next
, idx
++) {
1269 emit_ctxs
[idx
] = node
->uielement
->StartEmit (event_id
);
1272 EventArgs
*args
= CreateArgsForEvent(event_id
, event
);
1273 bool args_are_routed
= args
->Is (Type::ROUTEDEVENTARGS
);
1275 if (args_are_routed
&& element_list
->First())
1276 ((RoutedEventArgs
*)args
)->SetSource(((UIElementNode
*)element_list
->First())->uielement
);
1278 for (node
= (UIElementNode
*)element_list
->First(), idx
= 0; node
&& idx
< end_idx
; node
= (UIElementNode
*)node
->next
, idx
++) {
1280 bool h
= node
->uielement
->DoEmit (event_id
, args
);
1288 if (args_are_routed
&& ((RoutedEventArgs
*)args
)->GetHandled())
1294 for (node
= (UIElementNode
*)element_list
->First(), idx
= 0; node
&& idx
< end_idx
; node
= (UIElementNode
*)node
->next
, idx
++) {
1295 node
->uielement
->FinishEmit (event_id
, emit_ctxs
[idx
]);
1303 Surface::FindFirstCommonElement (List
*l1
, int *index1
,
1304 List
*l2
, int *index2
)
1306 // we exploit the fact that for a list with any common
1307 // elements, the lists will be identical from the first common
1308 // element to the end of the lists. So, we start from the
1309 // last elements in both lists and walk backward to the start,
1310 // looking for the first elements that don't match.
1312 // this algorithm is O(MAX(n,m)), but it's unclear whether or
1313 // not this is actually better than the O(n*m) approach, since
1314 // the O(n*m) approach will often find a match on the first
1315 // comparison (especially when the user is slowly moving the
1316 // mouse around in the same element), and skip the rest.
1318 UIElementNode
*ui1
, *ui2
;
1323 ui1
= (UIElementNode
*)l1
->Last();
1324 i1
= l1
->Length() - 1;
1326 ui2
= (UIElementNode
*)l2
->Last();
1327 i2
= l2
->Length() - 1;
1329 while (ui1
&& ui2
) {
1331 if (ui1
->uielement
== ui2
->uielement
) {
1339 ui1
= (UIElementNode
*)ui1
->prev
;
1340 ui2
= (UIElementNode
*)ui2
->prev
;
1347 copy_input_list_from_node (List
*input_list
, UIElementNode
* node
)
1349 List
*list
= new List ();
1352 list
->Append (new UIElementNode (node
->uielement
));
1353 node
= (UIElementNode
*) node
->next
;
1360 Surface::HandleMouseEvent (int event_id
, bool emit_leave
, bool emit_enter
, bool force_emit
, GdkEvent
*event
)
1362 bool handled
= false;
1363 bool mouse_down
= event_id
== UIElement::MouseLeftButtonDownEvent
||
1364 event_id
== UIElement::MouseRightButtonDownEvent
;
1366 if ((moonlight_flags
& RUNTIME_INIT_DESKTOP_EXTENSIONS
) == 0 &&
1367 ((event_id
== UIElement::MouseRightButtonDownEvent
) || (event_id
== UIElement::MouseRightButtonDownEvent
)))
1368 event_id
= NO_EVENT_ID
;
1370 // we can end up here if mozilla pops up the JS timeout
1371 // dialog. The problem is that JS might have registered a
1372 // handler for the event we're going to emit, so when we end
1373 // up tripping the timeout while in JS, mozilla pops up the
1374 // dialog, which causes a crossing-notify event to be emitted.
1375 // This causes HandleMouseEvent to be called, and the original
1376 // input_list is deleted. the crossing-notify event is
1377 // handled, then we return to the event that tripped the
1378 // timeout, we crash.
1379 if (emittingMouseEvent
)
1382 emittingMouseEvent
= true;
1386 if (toplevel
== NULL
|| event
== NULL
)
1389 // FIXME this should probably use mouse event args
1390 ProcessDirtyElements();
1393 // if the mouse is captured, the input_list doesn't ever
1394 // change, and we don't emit enter/leave events. just emit
1395 // the event on the input_list.
1396 if (event_id
!= NO_EVENT_ID
)
1397 handled
= EmitEventOnList (event_id
, input_list
, event
, -1);
1403 // Accumulate a new input_list, which contains the
1404 // most deeply nested hit testable UIElement covering
1405 // the point (x,y), and all visual parents up the
1406 // hierarchy to the root.
1407 List
*new_input_list
= new List ();
1410 gdk_event_get_coords (event
, &x
, &y
);
1414 cairo_t
*ctx
= measuring_context_create ();
1415 for (int i
= layers
->GetCount () - 1; i
>= 0 && new_input_list
->IsEmpty (); i
--)
1416 layers
->GetValueAt (i
)->AsUIElement ()->HitTest (ctx
, p
, new_input_list
);
1419 GenerateFocusChangeEvents ();
1420 if (!GetFocusedElement ()) {
1421 for (int i
= layers
->GetCount () - 1; i
>= 0; i
--) {
1422 if (layers
->GetValueAt (i
)->AsUIElement ()->Focus ())
1426 GenerateFocusChangeEvents ();
1431 // l1: [a1, a2, a3, a4, ... ]
1432 // l2: [b1, b2, b3, b4, ... ]
1434 // For identical lists:
1436 // only the primary event is emitted for all
1437 // elements of l2, in order.
1439 // For lists that differ, Enter/Leave events must be
1442 // If the first few nodes in each list differ, and,
1443 // for instance bn == am, we know that [am...] ==
1446 // when emitting a given event on b1, MS generally
1447 // emits Leave events on [a1, a2, a3, ... am-1], and
1448 // Enter events on [b1, b2, ... bn-1].
1450 // For most event types, that's all that happens if
1451 // the lists differ. For MouseLeftButtonDown (we
1452 // also do it for MouseLeftButtonUp), we also emit
1453 // the primary event on l2 after the enter/leave
1456 FindFirstCommonElement (input_list
, &surface_index
,
1457 new_input_list
, &new_index
);
1460 handled
= EmitEventOnList (UIElement::MouseLeaveEvent
, input_list
, event
, surface_index
);
1463 handled
= EmitEventOnList (UIElement::MouseEnterEvent
, new_input_list
, event
, new_index
) || handled
;
1465 if (event_id
!= NO_EVENT_ID
&& ((surface_index
== 0 && new_index
== 0) || force_emit
))
1466 handled
= EmitEventOnList (event_id
, new_input_list
, event
, -1) || handled
;
1468 // We need to remove from the new_input_list the events which have just
1469 // became invisible/unhittable as the result of the event.
1470 // (ie. element visibility was changed in the mouse enter).
1473 UIElementNode
*node
;
1475 for (node
= (UIElementNode
*)new_input_list
->Last(); node
; node
= (UIElementNode
*)node
->prev
) {
1476 if (! node
->uielement
->GetRenderVisible () ||
1477 ! node
->uielement
->GetHitTestVisible ()) {
1478 // Ooops, looks like something changed.
1479 // We need to copy the list with some elements removed.
1480 List
*list
= copy_input_list_from_node (new_input_list
, (UIElementNode
*)node
->next
);
1481 delete new_input_list
;
1482 new_input_list
= list
;
1488 measuring_context_destroy (ctx
);
1491 input_list
= new_input_list
;
1494 #define SPEW_INPUT_LIST 0
1498 printf ("input_list: ");
1499 UIElementNode
*node
;
1500 for (node
= (UIElementNode
*)input_list
->First(); node
; node
= (UIElementNode
*)node
->next
) {
1501 if (node
!= input_list
->First())
1503 printf ("(%s)", node
->uielement
->GetName());
1509 // Perform any captures/releases that are pending after the
1510 // event is bubbled.
1512 PerformCapture (pendingCapture
);
1513 if (pendingReleaseCapture
|| (captured
&& !captured
->CanCaptureMouse ()))
1514 PerformReleaseCapture ();
1515 emittingMouseEvent
= false;
1520 Surface::UpdateCursorFromInputList ()
1522 MouseCursor new_cursor
= MouseCursorDefault
;
1524 // loop over the input list in order until we hit a node that
1525 // has its cursor set to the non-default.
1526 UIElementNode
*node
;
1527 for (node
= (UIElementNode
*)input_list
->First(); node
; node
= (UIElementNode
*)node
->next
) {
1528 new_cursor
= node
->uielement
->GetCursor ();
1529 if (new_cursor
!= MouseCursorDefault
)
1533 SetCursor (new_cursor
);
1537 Surface::SetFPSReportFunc (MoonlightFPSReportFunc report
, void *user_data
)
1539 fps_report
= report
;
1540 fps_data
= user_data
;
1544 Surface::SetCacheReportFunc (MoonlightCacheReportFunc report
, void *user_data
)
1546 cache_report
= report
;
1547 cache_data
= user_data
;
1551 Surface::SetExposeHandoffFunc (MoonlightExposeHandoffFunc func
, void *user_data
)
1553 expose_handoff
= func
;
1554 expose_handoff_data
= user_data
;
1555 expose_handoff_last_timespan
= G_MAXINT64
;
1558 class DownloaderNode
: public List::Node
{
1560 Downloader
*downloader
;
1561 DownloaderNode (Downloader
*dl
) { downloader
= dl
; }
1565 Surface::DetachDownloaders ()
1567 DownloaderNode
*node
;
1568 if (downloaders
== NULL
)
1571 node
= (DownloaderNode
*) downloaders
->First ();
1572 while (node
!= NULL
) {
1573 node
->downloader
->RemoveHandler (Downloader::DestroyedEvent
, OnDownloaderDestroyed
, this);
1574 node
->downloader
->SetSurface (NULL
);
1575 node
= (DownloaderNode
*) node
->next
;
1577 downloaders
->Clear (true);
1581 Surface::OnDownloaderDestroyed (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
1583 DownloaderNode
*node
;
1584 Surface
*surface
= (Surface
*) closure
;
1585 List
*downloaders
= surface
->downloaders
;
1587 if (downloaders
== NULL
) {
1588 printf ("Surface::OnDownloaderDestroyed (): The list of downloaders is empty.\n");
1592 node
= (DownloaderNode
*) downloaders
->First ();
1593 while (node
!= NULL
) {
1594 if (node
->downloader
== sender
) {
1595 downloaders
->Remove (node
);
1598 node
= (DownloaderNode
*) node
->next
;
1601 printf ("Surface::OnDownloaderDestroyed (): Couldn't find the downloader %p in the list of downloaders\n", sender
);
1605 Surface::CreateDownloader (void)
1608 g_warning ("Surface::CreateDownloader (): Trying to create a downloader on a zombified surface.\n");
1612 Downloader
*downloader
= new Downloader ();
1613 downloader
->SetSurface (this);
1614 downloader
->SetContext (downloader_context
);
1615 downloader
->AddHandler (Downloader::DestroyedEvent
, OnDownloaderDestroyed
, this);
1616 if (downloaders
== NULL
)
1617 downloaders
= new List ();
1618 downloaders
->Append (new DownloaderNode (downloader
));
1624 Surface::CreateDownloader (EventObject
*obj
)
1626 Surface
*surface
= obj
? obj
->GetSurface () : NULL
;
1628 if (surface
== NULL
)
1629 surface
= Deployment::GetCurrent ()->GetSurface ();
1632 return surface
->CreateDownloader ();
1634 g_warning ("Surface::CreateDownloader (%p, ID: %i): Unable to create contextual downloader.\n",
1635 obj
, GET_OBJ_ID (obj
));
1641 Surface::VerifyWithCacheSizeCounter (int w
, int h
)
1643 if (! (moonlight_flags
& RUNTIME_INIT_USE_SHAPE_CACHE
))
1646 if (cache_size_multiplier
== -1)
1649 if (cache_size_in_bytes
+ (w
* h
* cache_size_multiplier
) < MAXIMUM_CACHE_SIZE
)
1656 Surface::AddToCacheSizeCounter (int w
, int h
)
1658 gint64 new_size
= w
* h
* cache_size_multiplier
;
1659 cache_size_in_bytes
+= new_size
;
1664 Surface::RemoveFromCacheSizeCounter (gint64 size
)
1666 cache_size_in_bytes
-= size
;
1670 Surface::FullScreenKeyHandled (GdkEventKey
*key
)
1672 if (!GetFullScreen ())
1675 // If we're in fullscreen mode no key events are passed through.
1676 // We only handle Esc, to exit fullscreen mode.
1677 if (key
->keyval
== GDK_Escape
)
1678 SetFullScreen (false);
1680 switch (key
->keyval
) {
1696 // Explicitly listing GDK_Escape here as it should never bubble up
1704 Surface::HandleUIFocusIn (GdkEventFocus
*event
)
1709 time_manager
->InvokeTickCalls();
1711 if (GetFocusedElement ()) {
1712 List
*focus_to_root
= ElementPathToRoot (GetFocusedElement ());
1713 EmitEventOnList (UIElement::GotFocusEvent
, focus_to_root
, (GdkEvent
*)event
, -1);
1714 delete focus_to_root
;
1721 Surface::HandleUIFocusOut (GdkEventFocus
*event
)
1726 time_manager
->InvokeTickCalls();
1728 if (GetFocusedElement ()) {
1729 List
*focus_to_root
= ElementPathToRoot (GetFocusedElement ());
1730 EmitEventOnList (UIElement::LostFocusEvent
, focus_to_root
, (GdkEvent
*)event
, -1);
1731 delete focus_to_root
;
1738 Surface::HandleUIButtonRelease (GdkEventButton
*event
)
1740 time_manager
->InvokeTickCalls();
1742 if (event
->button
!= 1 && event
->button
!= 3) {
1746 SetUserInitiatedEvent (true);
1749 gdk_event_free (mouse_event
);
1751 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1753 HandleMouseEvent (event
->button
== 1 ? UIElement::MouseLeftButtonUpEvent
: UIElement::MouseRightButtonUpEvent
,
1754 true, true, true, mouse_event
);
1756 UpdateCursorFromInputList ();
1757 SetUserInitiatedEvent (false);
1759 // XXX MS appears to do this here, which is completely stupid.
1761 PerformReleaseCapture ();
1763 return !((moonlight_flags
& RUNTIME_INIT_DESKTOP_EXTENSIONS
) == 0 && event
->button
== 3);
1767 Surface::HandleUIButtonPress (GdkEventButton
*event
)
1772 active_window
->GrabFocus ();
1774 time_manager
->InvokeTickCalls();
1776 if (event
->button
!= 1 && event
->button
!= 3)
1779 SetUserInitiatedEvent (true);
1782 gdk_event_free (mouse_event
);
1784 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1786 switch (event
->type
) {
1787 case GDK_3BUTTON_PRESS
:
1788 case GDK_2BUTTON_PRESS
:
1789 if (event
->button
!= 1)
1792 handled
= HandleMouseEvent (UIElement::MouseLeftButtonMultiClickEvent
, false, false, true, mouse_event
);
1795 if (event
->button
== 1)
1796 event_id
= UIElement::MouseLeftButtonDownEvent
;
1798 event_id
= UIElement::MouseRightButtonDownEvent
;
1800 handled
= HandleMouseEvent (event_id
, true, true, true, mouse_event
);
1804 UpdateCursorFromInputList ();
1805 SetUserInitiatedEvent (false);
1811 Surface::HandleUIScroll (GdkEventScroll
*event
)
1813 time_manager
->InvokeTickCalls();
1816 gdk_event_free (mouse_event
);
1818 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1820 bool handled
= false;
1822 handled
= HandleMouseEvent (UIElement::MouseWheelEvent
, true, true, true, mouse_event
);
1824 UpdateCursorFromInputList ();
1830 Surface::HandleUIMotion (GdkEventMotion
*event
)
1832 time_manager
->InvokeTickCalls();
1835 gdk_event_free (mouse_event
);
1837 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1839 bool handled
= false;
1841 if (event
->is_hint
) {
1842 #if GTK_CHECK_VERSION(2,12,0)
1843 if (gtk_check_version (2, 12, 0))
1844 gdk_event_request_motions (event
);
1849 GdkModifierType state
;
1850 gdk_window_get_pointer (event
->window
, &ix
, &iy
, (GdkModifierType
*)&state
);
1851 ((GdkEventMotion
*) mouse_event
)->x
= ix
;
1852 ((GdkEventMotion
*) mouse_event
)->y
= iy
;
1856 handled
= HandleMouseEvent (UIElement::MouseMoveEvent
, true, true, true, mouse_event
);
1857 UpdateCursorFromInputList ();
1863 Surface::HandleUICrossing (GdkEventCrossing
*event
)
1867 time_manager
->InvokeTickCalls();
1869 /* FIXME Disabling this for now... causes issues in ink journal
1870 GdkWindow *active_gdk_window = active_window->GetGdkWindow ();
1872 if (event->window && event->window != active_window->GetGdkWindow ()) {
1873 g_object_unref (active_gdk_window);
1876 g_object_unref (active_gdk_window);
1879 if (event
->type
== GDK_ENTER_NOTIFY
) {
1881 gdk_event_free (mouse_event
);
1882 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1884 handled
= HandleMouseEvent (UIElement::MouseMoveEvent
, true, true, false, mouse_event
);
1886 UpdateCursorFromInputList ();
1889 // forceably emit MouseLeave on the current input
1890 // list.. the "new" list computed by HandleMouseEvent
1891 // should be the same as the current one since we pass
1892 // in the same x,y but I'm not sure that's something
1894 handled
= HandleMouseEvent (UIElement::MouseLeaveEvent
, false, false, true, mouse_event
);
1896 // MS specifies that mouse capture is lost when you mouse out of the control
1898 PerformReleaseCapture ();
1900 // clear out the input list so we emit the right
1901 // events when the pointer reenters the control.
1902 if (!emittingMouseEvent
) {
1904 input_list
= new List();
1912 Surface::GenerateFocusChangeEvents()
1914 while (!focus_changed_events
->IsEmpty ()) {
1915 FocusChangedNode
*node
= (FocusChangedNode
*) focus_changed_events
->Pop ();
1918 if (node
->lost_focus
) {
1919 el_list
= ElementPathToRoot (node
->lost_focus
);
1920 EmitEventOnList (UIElement::LostFocusEvent
, el_list
, NULL
, -1);
1924 if (node
->got_focus
) {
1925 el_list
= ElementPathToRoot (node
->got_focus
);
1926 EmitEventOnList (UIElement::GotFocusEvent
, el_list
, NULL
, -1);
1934 Surface::FocusElement (UIElement
*focused
)
1936 if (focused
== focused_element
)
1939 focus_changed_events
->Push (new FocusChangedNode (focused_element
, focused
));
1940 focused_element
= focused
;
1942 if (FirstUserInitiatedEvent ())
1943 AddTickCall (Surface::AutoFocusAsync
);
1948 Surface::ElementPathToRoot (UIElement
*source
)
1950 List
*list
= new List();
1952 list
->Append (new UIElementNode (source
));
1953 source
= source
->GetVisualParent();
1959 Surface::HandleUIKeyPress (GdkEventKey
*event
)
1961 time_manager
->InvokeTickCalls();
1963 Key key
= Keyboard::MapKeyValToKey (event
->keyval
);
1965 if (Keyboard::IsKeyPressed (key
)) {
1966 // If we are running an SL 1.0 application, then key repeats are dropped
1967 Deployment
*deployment
= Deployment::GetCurrent ();
1968 if (!deployment
->IsLoadedFromXap ())
1970 } else if (FullScreenKeyHandled (event
)) {
1974 #if DEBUG_MARKER_KEY
1975 static int debug_marker_key_in
= 0;
1976 if (event
->keyval
== GDK_d
|| event
->keyval
== GDK_D
) {
1977 if (!debug_marker_key_in
)
1978 printf ("<--- DEBUG MARKER KEY IN (%f) --->\n", get_now () / 10000000.0);
1980 printf ("<--- DEBUG MARKER KEY OUT (%f) --->\n", get_now () / 10000000.0);
1981 debug_marker_key_in
= ! debug_marker_key_in
;
1986 SetUserInitiatedEvent (true);
1989 Keyboard::OnKeyPress (key
);
1991 if (focused_element
) {
1992 List
*focus_to_root
= ElementPathToRoot (focused_element
);
1993 handled
= EmitEventOnList (UIElement::KeyDownEvent
, focus_to_root
, (GdkEvent
*)event
, -1);
1994 delete focus_to_root
;
1997 // in silverlight 1.0, key events are only ever delivered to the toplevel
1998 toplevel
->EmitKeyDown (event
);
2002 SetUserInitiatedEvent (false);
2008 Surface::HandleUIKeyRelease (GdkEventKey
*event
)
2010 time_manager
->InvokeTickCalls();
2012 if (FullScreenKeyHandled (event
))
2015 SetUserInitiatedEvent (true);
2018 Key key
= Keyboard::MapKeyValToKey (event
->keyval
);
2019 Keyboard::OnKeyRelease (key
);
2021 if (focused_element
) {
2022 List
*focus_to_root
= ElementPathToRoot (focused_element
);
2023 handled
= EmitEventOnList (UIElement::KeyUpEvent
, focus_to_root
, (GdkEvent
*)event
, -1);
2024 delete focus_to_root
;
2026 else if (toplevel
) {
2027 // in silverlight 1.0, key events are only ever delivered to the toplevel
2028 toplevel
->EmitKeyUp (event
);
2032 SetUserInitiatedEvent (false);
2038 Surface::HandleUIWindowAllocation (bool emit_resize
)
2046 Surface::HandleUIWindowDestroyed (MoonWindow
*window
)
2048 if (window
== fullscreen_window
) {
2049 // switch out of fullscreen mode, as something has
2050 // destroyed our fullscreen window.
2051 UpdateFullScreen (false);
2053 else if (window
== normal_window
) {
2054 // something destroyed our normal window
2055 normal_window
= NULL
;
2058 if (window
== active_window
)
2059 active_window
= NULL
;
2063 Surface::SetBackgroundColor (Color
*color
)
2065 if (background_color
)
2066 delete background_color
;
2068 background_color
= new Color (*color
);
2070 active_window
->SetBackgroundColor (color
);
2071 active_window
->Invalidate ();
2075 Surface::GetBackgroundColor ()
2077 return background_color
;
2081 Surface::IsVersionSupported (const char *version_list
)
2083 /* we support all 0.*, 1.0.*, 1.1.* and 2.0.* versions. */
2084 bool supported
= true;
2086 char *version
= NULL
;
2089 if (version_list
== NULL
)
2092 versions
= g_strsplit (version_list
, ".", 4);
2094 supported
= versions
[0] != NULL
&& versions
[1] != NULL
;
2097 for (int k
= 0; k
< 4; k
++) {
2099 version
= versions
[k
];
2101 if (version
== NULL
)
2104 if (version
[0] == 0) {
2109 // Only allow ascii 0-9 characters in the numbers
2110 for (int i
= 0; version
[i
] != 0; i
++) {
2111 if (version
[i
] < '0' || version
[i
] > '9') {
2117 numbers
[k
] = atoll (version
);
2120 switch (numbers
[0]) {
2121 case 0: // We support all versions of the format "0.*" and "1.*"
2125 supported
&= numbers
[1] == 0; // 2.0.*
2133 g_strfreev (versions
);
2139 runtime_init_browser (const char *plugin_dir
)
2141 runtime_init (plugin_dir
, RUNTIME_INIT_BROWSER
);
2145 runtime_init_desktop ()
2147 runtime_init (NULL
, RUNTIME_INIT_DESKTOP
);
2151 get_flags (gint32 def
, const char *envname
, struct env_options options
[])
2156 if (envname
&& (env
= g_getenv (envname
))) {
2157 printf ("%s = %s\n", envname
, env
);
2159 const char *flag
= env
;
2161 bool all
= !strcmp ("all", env
);
2165 while (*flag
== ',')
2171 while (*inptr
&& *inptr
!= ',')
2175 for (i
= 0; options
[i
].name
!= NULL
; i
++) {
2177 flags
|= options
[i
].flag
;
2181 if (n
!= strlen (options
[i
].name
))
2184 if (!strncmp (options
[i
].name
, flag
, n
)) {
2185 if (!options
[i
].set
)
2186 flags
&= ~options
[i
].flag
;
2188 flags
|= options
[i
].flag
;
2192 while (*inptr
== ',')
2202 runtime_init (const char *platform_dir
, guint32 flags
)
2207 if (cairo_version () < CAIRO_VERSION_ENCODE(1,4,0)) {
2208 printf ("*** WARNING ***\n");
2209 printf ("*** Cairo versions < 1.4.0 should not be used for Moon.\n");
2210 printf ("*** Moon is being run against version %s.\n", cairo_version_string ());
2211 printf ("*** Proceed at your own risk\n");
2214 if (running_on_nvidia ()) {
2215 printf ("Moonlight: Forcing client-side rendering because we detected binary drivers which are known to suffer performance problems.\n");
2216 flags
&= ~RUNTIME_INIT_USE_BACKEND_XLIB
;
2219 // Allow the user to override the flags via his/her environment
2220 flags
= get_flags (flags
, "MOONLIGHT_OVERRIDES", overrides
);
2222 debug_flags_ex
= get_flags (0, "MOONLIGHT_DEBUG", debug_extras
);
2223 debug_flags
= get_flags (0, "MOONLIGHT_DEBUG", debugs
);
2228 if (!g_type_inited
) {
2229 g_type_inited
= true;
2233 moonlight_flags
= flags
;
2235 Deployment::Initialize (platform_dir
, (flags
& RUNTIME_INIT_CREATE_ROOT_DOMAIN
) != 0);
2239 Media::Initialize ();
2243 runtime_shutdown (void)