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);
524 List
*list
= canvas
->WalkTreeForLoaded (NULL
);
525 canvas
->PostSubtreeLoad (list
);
526 // PostSubtreeLoad will take care of deleting the list for us.
530 Surface::tick_after_attach_reached (EventObject
*data
)
532 Surface
*surface
= (Surface
*)data
;
534 surface
->ticked_after_attach
= true;
535 surface
->Emit (Surface::LoadEvent
);
539 Surface::toplevel_loaded (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
541 ((Surface
*)closure
)->ToplevelLoaded ((UIElement
*)sender
);
545 Surface::ToplevelLoaded (UIElement
*element
)
547 if (element
== toplevel
) {
548 toplevel
->RemoveHandler (UIElement::LoadedEvent
, toplevel_loaded
, this);
550 if (active_window
&& active_window
->HasFocus())
551 element
->EmitGotFocus ();
554 // If the did not get a size specified
556 if (normal_window
&& normal_window
->GetWidth() == 0 && normal_window
->GetHeight() == 0 && toplevel
) {
558 * this should only be hit in the nonplugin case ans is
559 * simply here to give a reasonable default size
562 vw
= toplevel
->GetValue (FrameworkElement::WidthProperty
);
563 vh
= toplevel
->GetValue (FrameworkElement::HeightProperty
);
565 normal_window
->Resize (MAX (vw
? (int)vw
->AsDouble () : 0, 0),
566 MAX (vh
? (int)vh
->AsDouble () : 0, 0));
571 element
->UpdateTotalRenderVisibility ();
572 element
->UpdateTotalHitTestVisibility ();
573 element
->FullInvalidate (true);
575 // we call this two here so that the layout pass proceeds when
576 // we next process the dirty list.
577 element
->InvalidateMeasure ();
582 Surface::AttachLayer (UIElement
*layer
)
584 if (layer
== toplevel
)
585 layers
->Insert (0, Value(layer
));
587 layers
->Add (Value (layer
));
589 layer
->SetSurface (this);
590 layer
->FullInvalidate (true);
592 List
*list
= layer
->WalkTreeForLoaded (NULL
);
593 layer
->PostSubtreeLoad (list
);
594 // PostSubtreeLoad will take care of deleting the list for us.
598 Surface::DetachLayer (UIElement
*layer
)
600 layers
->Remove (Value (layer
));
601 layer
->SetSurface (NULL
);
603 Invalidate (layer
->GetBounds ());
607 Surface::Invalidate (Rect r
)
609 active_window
->Invalidate (r
);
613 Surface::ProcessUpdates ()
615 active_window
->ProcessUpdates();
619 Surface::Paint (cairo_t
*ctx
, int x
, int y
, int width
, int height
)
621 Rect r
= Rect (x
, y
, width
, height
);
622 Region region
= Region (r
);
623 Paint (ctx
, ®ion
);
627 Surface::Paint (cairo_t
*ctx
, Region
*region
)
629 for (int i
= 0; i
< layers
->GetCount (); i
++) {
630 UIElement
*layer
= layers
->GetValueAt (i
)->AsUIElement ();
631 layer
->Paint (ctx
, region
, NULL
);
635 if (debug_selected_element
) {
636 Rect bounds
= debug_selected_element
->GetSubtreeBounds();
637 // printf ("debug_selected_element is %s\n", debug_selected_element->GetName());
638 // printf ("bounds is %g %g %g %g\n", bounds.x, bounds.y, bounds.w, bounds.h);
640 //RenderClipPath (ctx);
641 cairo_new_path (ctx
);
642 cairo_identity_matrix (ctx
);
643 cairo_set_source_rgba (ctx
, 1.0, 0.5, 0.2, 1.0);
644 cairo_set_line_width (ctx
, 1);
645 cairo_rectangle (ctx
, bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
);
653 // This will resize the surface (merely a convenience function for
654 // resizing the widget area that we have.
656 // This will not change the Width and Height properties of the
657 // toplevel canvas, if you want that, you must do that yourself
660 Surface::Resize (int width
, int height
)
662 if (width
== normal_window
->GetWidth()
663 && height
== normal_window
->GetHeight())
666 normal_window
->Resize (width
, height
);
670 Surface::EmitSourceDownloadComplete ()
672 Emit (SourceDownloadCompleteEvent
, NULL
);
676 Surface::EmitSourceDownloadProgressChanged (DownloadProgressEventArgs
*args
)
678 Emit (SourceDownloadProgressChangedEvent
, args
);
682 Surface::EmitError (ErrorEventArgs
*args
)
684 Emit (ErrorEvent
, args
);
688 Surface::EmitError (int number
, int code
, const char *message
)
690 ErrorEventArgs
*args
= new ErrorEventArgs ((ErrorType
)number
, code
, message
);
691 Emit (ErrorEvent
, args
);
697 for (int i
= 0; i
< layers
->GetCount (); i
++) {
698 UIElement
*layer
= layers
->GetValueAt (i
)->AsUIElement ();
700 layer
->InvalidateMeasure ();
701 //layer->UpdateBounds();
706 Surface::SetFullScreen (bool value
)
708 if (value
&& !IsUserInitiatedEvent ()) {
709 g_warning ("You're not allowed to switch to fullscreen from where you're doing it.");
713 UpdateFullScreen (value
);
717 Surface::SetUserInitiatedEvent (bool value
)
719 GenerateFocusChangeEvents ();
720 first_user_initiated_event
= first_user_initiated_event
| value
;
721 user_initiated_event
= value
;
725 Surface::IsTopLevel (UIElement
* top
)
730 bool ret
= top
== full_screen_message
;
732 for (int i
= 0; i
< layers
->GetCount () && !ret
; i
++)
733 ret
= layers
->GetValueAt (i
)->AsUIElement () == top
;
739 Surface::ShowFullScreenMessage ()
741 g_return_if_fail (full_screen_message
== NULL
);
742 //g_return_if_fail (toplevel && toplevel->Is (Type::PANEL));
745 XamlLoader
*loader
= new XamlLoader (NULL
, FULLSCREEN_MESSAGE
, this);
746 DependencyObject
* message
= loader
->CreateDependencyObjectFromString (FULLSCREEN_MESSAGE
, false, &dummy
);
750 printf ("Unable to create fullscreen message.\n");
754 if (!message
->Is (Type::CANVAS
)) {
755 printf ("Unable to create fullscreen message, got a %s, expected at least a UIElement.\n", message
->GetTypeName ());
760 full_screen_message
= (Canvas
*) message
;
761 AttachLayer (full_screen_message
);
763 DependencyObject
* message_object
= full_screen_message
->FindName ("message");
764 DependencyObject
* url_object
= full_screen_message
->FindName ("url");
765 TextBlock
* message_block
= (message_object
!= NULL
&& message_object
->Is (Type::TEXTBLOCK
)) ? (TextBlock
*) message_object
: NULL
;
766 TextBlock
* url_block
= (url_object
!= NULL
&& url_object
->Is (Type::TEXTBLOCK
)) ? (TextBlock
*) url_object
: NULL
;
768 Transform
* transform
= full_screen_message
->GetRenderTransform ();
770 double box_height
= full_screen_message
->GetHeight ();
771 double box_width
= full_screen_message
->GetWidth ();
773 // Set the url in the box
774 if (url_block
!= NULL
) {
777 if (source_location
) {
778 if (g_str_has_prefix (source_location
, "http://")) {
779 const char *path
= strchr (source_location
+ 7, '/');
781 if (path
!= NULL
&& path
> source_location
+ 7) {
782 url
= g_strndup (source_location
, path
- source_location
);
784 url
= g_strdup (source_location
);
786 } else if (g_str_has_prefix (source_location
, "file://")) {
787 url
= g_strdup ("file://");
789 url
= g_strdup (source_location
);
793 url_block
->SetValue (TextBlock::TextProperty
, url
? url
: (char *) "file://");
797 // The box is not made bigger if the url doesn't fit.
798 // MS has an interesting text rendering if the url doesn't
799 // fit: the text is overflown to the left.
800 // Since only the server is shown, this shouldn't
801 // happen on a regular basis though.
803 // Center the url block
804 if (url_block
!= NULL
) {
805 double url_width
= url_block
->GetActualWidth ();
806 Canvas::SetLeft (url_block
, (box_width
- url_width
) / 2);
809 // Center the message block
810 if (message_block
!= NULL
) {
811 double message_width
= message_block
->GetActualWidth ();
812 Canvas::SetLeft (message_block
, (box_width
- message_width
) / 2);
815 // Put the box in the middle of the screen
816 transform
->SetValue (TranslateTransform::XProperty
, Value ((active_window
->GetWidth() - box_width
) / 2));
817 transform
->SetValue (TranslateTransform::YProperty
, Value ((active_window
->GetHeight() - box_height
) / 2));
821 Surface::GetSourceLocation ()
823 return source_location
;
827 Surface::SetSourceLocation (const char* location
)
829 g_free (source_location
);
830 source_location
= g_strdup (location
);
834 Surface::HideFullScreenMessage ()
836 if (full_screen_message
) {
837 DetachLayer (full_screen_message
);
838 full_screen_message
->unref ();
839 full_screen_message
= NULL
;
844 Surface::UpdateFullScreen (bool value
)
846 if (value
== full_screen
)
850 fullscreen_window
= new MoonWindowGtk (true, -1, -1, normal_window
);
851 fullscreen_window
->SetSurface (this);
853 active_window
= fullscreen_window
;
855 ShowFullScreenMessage ();
857 fullscreen_window
->EnableEvents (false);
859 active_window
= normal_window
;
861 HideFullScreenMessage ();
863 delete fullscreen_window
;
864 fullscreen_window
= NULL
;
871 time_manager
->GetSource()->Stop();
872 Emit (FullScreenChangeEvent
);
876 time_manager
->GetSource()->Start();
880 Surface::render_cb (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
882 Surface
*s
= (Surface
*) closure
;
886 GDK_THREADS_ENTER ();
888 s
->up_dirty
->Clear (true);
889 s
->down_dirty
->Clear (true);
891 dirty
= s
->ProcessDirtyElements ();
894 if (s
->expose_handoff
) {
895 TimeSpan time
= s
->GetTimeManager ()->GetCurrentTime ();
896 if (time
!= s
->expose_handoff_last_timespan
) {
897 s
->expose_handoff (s
, time
, s
->expose_handoff_data
);
898 s
->expose_handoff_last_timespan
= time
;
902 GDK_THREADS_LEAVE ();
904 if ((moonlight_flags
& RUNTIME_INIT_SHOW_FPS
) && s
->fps_start
== 0)
905 s
->fps_start
= get_now ();
908 s
->ProcessUpdates ();
911 if ((moonlight_flags
& RUNTIME_INIT_SHOW_FPS
) && s
->fps_report
) {
914 if ((now
= get_now ()) > (s
->fps_start
+ TIMESPANTICKS_IN_SECOND
)) {
915 float nsecs
= (now
- s
->fps_start
) / TIMESPANTICKS_IN_SECOND_FLOAT
;
917 s
->fps_report (s
, s
->fps_nframes
, nsecs
, s
->fps_data
);
923 if ((moonlight_flags
& RUNTIME_INIT_SHOW_CACHE_SIZE
) && s
->cache_report
) {
924 // By default we report cache status every 50 render's.
925 // Should be enough for everybody, but syncing to ie. 1s sounds
927 if (s
->cache_size_ticker
== 50) {
928 s
->cache_report (s
, s
->cache_size_in_bytes
, s
->cache_data
);
929 s
->cache_size_ticker
= 0;
931 s
->cache_size_ticker
++;
936 Surface::update_input_cb (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
939 Surface
*s
= (Surface
*) closure
;
941 s
->HandleMouseEvent (UIElement::MouseMoveEvent
, true, true, false, s
->mouse_event_state
, s
->mouse_event_x
, s
->mouse_event_y
);
942 s
->UpdateCursorFromInputList ()
947 Surface::HandleUIWindowAvailable ()
949 time_manager
->AddHandler (TimeManager::RenderEvent
, render_cb
, this);
950 time_manager
->AddHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
952 time_manager
->NeedRedraw ();
956 Surface::HandleUIWindowUnavailable ()
958 time_manager
->RemoveHandler (TimeManager::RenderEvent
, render_cb
, this);
959 time_manager
->RemoveHandler (TimeManager::UpdateInputEvent
, update_input_cb
, this);
963 Surface::PaintToDrawable (GdkDrawable
*drawable
, GdkVisual
*visual
, GdkEventExpose
*event
, int off_x
, int off_y
, bool transparent
, bool clear_transparent
)
967 LOG_UI ("Surface::PaintToDrawable (%p, %p, (%d,%d %d,%d), %d, %d, %d, %d)\n",
968 drawable
, visual
, event
->area
.x
, event
->area
.y
, event
->area
.width
, event
->area
.height
,
969 off_x
, off_y
, transparent
, clear_transparent
);
971 if (event
->area
.x
> (off_x
+ active_window
->GetWidth()) || event
->area
.y
> (off_y
+ active_window
->GetHeight()))
974 SetCurrentDeployment ();
977 STARTTIMER (expose
, "redraw");
979 if (cache_size_multiplier
== -1)
980 cache_size_multiplier
= gdk_drawable_get_depth (drawable
) / 8 + 1;
982 #ifdef DEBUG_INVALIDATE
983 printf ("Got a request to repaint at %d %d %d %d\n", event
->area
.x
, event
->area
.y
, event
->area
.width
, event
->area
.height
);
985 cairo_t
*ctx
= runtime_cairo_create (drawable
, visual
, moonlight_flags
& RUNTIME_INIT_USE_BACKEND_XLIB
);
986 Region
*region
= new Region (event
->region
);
988 region
->Offset (-off_x
, -off_y
);
989 cairo_surface_set_device_offset (cairo_get_target (ctx
),
990 off_x
- event
->area
.x
,
991 off_y
- event
->area
.y
);
994 // These are temporary while we change this to paint at the offset position
995 // instead of using the old approach of modifying the topmost Canvas (a no-no),
997 // The flag "transparent" is here because I could not
998 // figure out what is painting the background with white now.
999 // The change that made the white painting implicit instead of
1000 // explicit is patch 80632. I would appreciate any help in tracking down
1001 // the proper way of making the background white when not running in
1002 // "transparent" mode.
1004 // Either exposing surface_set_trans to turn the next code is a hack,
1005 // or it is normal to request all code that wants to paint to manually
1006 // clear the background to white beforehand. For now am going with
1007 // making this an explicit surface API.
1009 // The second part is for coping with the future: when we support being
1012 cairo_set_operator (ctx
, CAIRO_OPERATOR_OVER
);
1015 if (clear_transparent
) {
1016 cairo_set_operator (ctx
, CAIRO_OPERATOR_CLEAR
);
1017 cairo_fill_preserve (ctx
);
1018 cairo_set_operator (ctx
, CAIRO_OPERATOR_OVER
);
1021 cairo_set_source_rgba (ctx
,
1022 background_color
->r
,
1023 background_color
->g
,
1024 background_color
->b
,
1025 background_color
->a
);
1028 cairo_set_source_rgb (ctx
,
1029 background_color
->r
,
1030 background_color
->g
,
1031 background_color
->b
);
1034 cairo_fill_preserve (ctx
);
1038 Paint (ctx
, region
);
1039 cairo_restore (ctx
);
1041 if (RENDER_EXPOSE
) {
1042 cairo_new_path (ctx
);
1044 cairo_set_line_width (ctx
, 2.0);
1045 cairo_set_source_rgb (ctx
, (double)(frames
% 2), (double)((frames
+ 1) % 2), (double)((frames
/ 3) % 2));
1049 if (!(moonlight_flags
& RUNTIME_INIT_USE_BACKEND_XLIB
)) {
1050 cairo_surface_flush (cairo_get_target (ctx
));
1051 cairo_t
*native
= runtime_cairo_create (drawable
, visual
, true);
1053 cairo_surface_set_device_offset (cairo_get_target (native
),
1055 cairo_surface_set_device_offset (cairo_get_target (ctx
),
1058 cairo_set_source_surface (native
, cairo_get_target (ctx
),
1061 region
->Offset (off_x
, off_y
);
1062 region
->Offset (-event
->area
.x
, -event
->area
.y
);
1063 region
->Draw (native
);
1065 cairo_fill (native
);
1066 cairo_destroy (native
);
1068 cairo_destroy (ctx
);
1074 ENDTIMER (expose
, "redraw");
1079 /* for emitting focus changed events */
1080 class FocusChangedNode
: public List::Node
{
1082 UIElement
*lost_focus
;
1083 UIElement
*got_focus
;
1085 FocusChangedNode (UIElement
*lost_focus
, UIElement
*got_focus
);
1086 virtual ~FocusChangedNode ();
1089 FocusChangedNode::FocusChangedNode (UIElement
*lost_focus
, UIElement
*got_focus
)
1091 this->lost_focus
= lost_focus
;
1092 this->got_focus
= got_focus
;
1100 FocusChangedNode::~FocusChangedNode ()
1103 lost_focus
->unref ();
1105 got_focus
->unref ();
1108 RenderNode::RenderNode (UIElement
*el
,
1110 bool render_element
,
1117 this->region
= region
? region
: new Region ();
1118 this->render_element
= render_element
;
1119 this->pre_render
= pre
;
1120 this->post_render
= post
;
1124 RenderNode::Render (cairo_t
*ctx
)
1126 bool front_to_back
= uielement
->UseBackToFront ();
1129 pre_render (ctx
, uielement
, region
, front_to_back
);
1132 uielement
->Render (ctx
, region
);
1135 post_render (ctx
, uielement
, region
, front_to_back
);
1138 RenderNode::~RenderNode ()
1141 uielement
->unref ();
1149 UIElementNode::UIElementNode (UIElement
*el
)
1155 UIElementNode::~UIElementNode ()
1162 Surface::PerformCapture (UIElement
*capture
)
1164 // "Capturing" the mouse pointer at an element forces us to
1165 // use the path up the hierarchy from that element to the root
1166 // as the input list, regardless of where the pointer actually
1170 List
*new_input_list
= new List();
1172 new_input_list
->Append (new UIElementNode (capture
));
1173 capture
= capture
->GetVisualParent();
1177 input_list
= new_input_list
;
1178 pendingCapture
= NULL
;
1182 Surface::PerformReleaseCapture ()
1184 // These need to be set before calling HandleMouseEvent as
1185 // "captured" determines the input_list calculation, and
1186 // "pendingReleaseCapture", when set, causes an infinite
1188 captured
->EmitLostMouseCapture ();
1190 pendingReleaseCapture
= false;
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::MouseLeftButtonMultiClickEvent
1235 || event_id
==UIElement::MouseLeftButtonDownEvent
1236 || event_id
==UIElement::MouseLeftButtonUpEvent
1237 || event_id
==UIElement::MouseRightButtonDownEvent
1238 || event_id
==UIElement::MouseRightButtonUpEvent
1239 || event_id
==UIElement::MouseEnterEvent
)
1240 return new MouseEventArgs(event
);
1241 else if (event_id
== UIElement::MouseWheelEvent
)
1242 return new MouseWheelEventArgs(event
);
1243 else if (event_id
== UIElement::KeyDownEvent
1244 || event_id
== UIElement::KeyUpEvent
)
1245 return new KeyEventArgs((GdkEventKey
*)event
);
1247 g_warning ("Unknown event id %d\n", event_id
);
1248 return new EventArgs();
1253 Surface::EmitEventOnList (int event_id
, List
*element_list
, GdkEvent
*event
, int end_idx
)
1255 bool handled
= false;
1258 UIElementNode
*node
;
1260 if (element_list
->IsEmpty() || end_idx
== 0)
1264 end_idx
= element_list
->Length();
1266 EmitContext
** emit_ctxs
= g_new (EmitContext
*, end_idx
+ 1);
1267 for (node
= (UIElementNode
*)element_list
->First(), idx
= 0; node
&& idx
< end_idx
; node
= (UIElementNode
*)node
->next
, idx
++) {
1268 emit_ctxs
[idx
] = node
->uielement
->StartEmit (event_id
);
1271 EventArgs
*args
= CreateArgsForEvent(event_id
, event
);
1272 bool args_are_routed
= args
->Is (Type::ROUTEDEVENTARGS
);
1274 if (args_are_routed
&& element_list
->First())
1275 ((RoutedEventArgs
*)args
)->SetSource(((UIElementNode
*)element_list
->First())->uielement
);
1277 for (node
= (UIElementNode
*)element_list
->First(), idx
= 0; node
&& idx
< end_idx
; node
= (UIElementNode
*)node
->next
, idx
++) {
1278 bool h
= node
->uielement
->DoEmit (event_id
, emit_ctxs
[idx
], args
);
1286 if (args_are_routed
&& ((RoutedEventArgs
*)args
)->GetHandled())
1292 for (node
= (UIElementNode
*)element_list
->First(), idx
= 0; node
&& idx
< end_idx
; node
= (UIElementNode
*)node
->next
, idx
++) {
1293 node
->uielement
->FinishEmit (event_id
, emit_ctxs
[idx
]);
1301 Surface::FindFirstCommonElement (List
*l1
, int *index1
,
1302 List
*l2
, int *index2
)
1304 // we exploit the fact that for a list with any common
1305 // elements, the lists will be identical from the first common
1306 // element to the end of the lists. So, we start from the
1307 // last elements in both lists and walk backward to the start,
1308 // looking for the first elements that don't match.
1310 // this algorithm is O(MAX(n,m)), but it's unclear whether or
1311 // not this is actually better than the O(n*m) approach, since
1312 // the O(n*m) approach will often find a match on the first
1313 // comparison (especially when the user is slowly moving the
1314 // mouse around in the same element), and skip the rest.
1316 UIElementNode
*ui1
, *ui2
;
1321 ui1
= (UIElementNode
*)l1
->Last();
1322 i1
= l1
->Length() - 1;
1324 ui2
= (UIElementNode
*)l2
->Last();
1325 i2
= l2
->Length() - 1;
1327 while (ui1
&& ui2
) {
1329 if (ui1
->uielement
== ui2
->uielement
) {
1337 ui1
= (UIElementNode
*)ui1
->prev
;
1338 ui2
= (UIElementNode
*)ui2
->prev
;
1345 copy_input_list_from_node (List
*input_list
, UIElementNode
* node
)
1347 List
*list
= new List ();
1350 list
->Append (new UIElementNode (node
->uielement
));
1351 node
= (UIElementNode
*) node
->next
;
1358 Surface::HandleMouseEvent (int event_id
, bool emit_leave
, bool emit_enter
, bool force_emit
, GdkEvent
*event
)
1360 bool handled
= false;
1361 bool mouse_down
= event_id
== UIElement::MouseLeftButtonDownEvent
||
1362 event_id
== UIElement::MouseRightButtonDownEvent
;
1364 if ((moonlight_flags
& RUNTIME_INIT_DESKTOP_EXTENSIONS
) == 0 &&
1365 ((event_id
== UIElement::MouseRightButtonDownEvent
) || (event_id
== UIElement::MouseRightButtonDownEvent
)))
1366 event_id
= NO_EVENT_ID
;
1368 // we can end up here if mozilla pops up the JS timeout
1369 // dialog. The problem is that JS might have registered a
1370 // handler for the event we're going to emit, so when we end
1371 // up tripping the timeout while in JS, mozilla pops up the
1372 // dialog, which causes a crossing-notify event to be emitted.
1373 // This causes HandleMouseEvent to be called, and the original
1374 // input_list is deleted. the crossing-notify event is
1375 // handled, then we return to the event that tripped the
1376 // timeout, we crash.
1377 if (emittingMouseEvent
)
1380 emittingMouseEvent
= true;
1384 if (toplevel
== NULL
|| event
== NULL
)
1387 // FIXME this should probably use mouse event args
1388 ProcessDirtyElements();
1391 // if the mouse is captured, the input_list doesn't ever
1392 // change, and we don't emit enter/leave events. just emit
1393 // the event on the input_list.
1394 if (event_id
!= NO_EVENT_ID
)
1395 handled
= EmitEventOnList (event_id
, input_list
, event
, -1);
1401 // Accumulate a new input_list, which contains the
1402 // most deeply nested hit testable UIElement covering
1403 // the point (x,y), and all visual parents up the
1404 // hierarchy to the root.
1405 List
*new_input_list
= new List ();
1408 gdk_event_get_coords (event
, &x
, &y
);
1412 cairo_t
*ctx
= measuring_context_create ();
1413 for (int i
= layers
->GetCount () - 1; i
>= 0 && new_input_list
->IsEmpty (); i
--)
1414 layers
->GetValueAt (i
)->AsUIElement ()->HitTest (ctx
, p
, new_input_list
);
1417 if (!GetFocusedElement ()) {
1418 for (int i
= layers
->GetCount () - 1; i
>= 0; i
--) {
1419 if (layers
->GetValueAt (i
)->AsUIElement ()->Focus ())
1422 GenerateFocusChangeEvents ();
1425 for (UIElementNode
* node
= (UIElementNode
*) new_input_list
->First (); node
!= NULL
; node
= (UIElementNode
*) node
->next
) {
1426 UIElement
*el
= node
->uielement
;
1427 if (el
->Focus (false))
1430 // Raise any events caused by the focus changing this tick
1431 GenerateFocusChangeEvents ();
1436 // l1: [a1, a2, a3, a4, ... ]
1437 // l2: [b1, b2, b3, b4, ... ]
1439 // For identical lists:
1441 // only the primary event is emitted for all
1442 // elements of l2, in order.
1444 // For lists that differ, Enter/Leave events must be
1447 // If the first few nodes in each list differ, and,
1448 // for instance bn == am, we know that [am...] ==
1451 // when emitting a given event on b1, MS generally
1452 // emits Leave events on [a1, a2, a3, ... am-1], and
1453 // Enter events on [b1, b2, ... bn-1].
1455 // For most event types, that's all that happens if
1456 // the lists differ. For MouseLeftButtonDown (we
1457 // also do it for MouseLeftButtonUp), we also emit
1458 // the primary event on l2 after the enter/leave
1461 FindFirstCommonElement (input_list
, &surface_index
,
1462 new_input_list
, &new_index
);
1465 handled
= EmitEventOnList (UIElement::MouseLeaveEvent
, input_list
, event
, surface_index
);
1468 handled
= EmitEventOnList (UIElement::MouseEnterEvent
, new_input_list
, event
, new_index
) || handled
;
1470 if (event_id
!= NO_EVENT_ID
&& ((surface_index
== 0 && new_index
== 0) || force_emit
))
1471 handled
= EmitEventOnList (event_id
, new_input_list
, event
, -1) || handled
;
1473 // We need to remove from the new_input_list the events which have just
1474 // became invisible/unhittable as the result of the event.
1475 // (ie. element visibility was changed in the mouse enter).
1478 UIElementNode
*node
;
1480 for (node
= (UIElementNode
*)new_input_list
->Last(); node
; node
= (UIElementNode
*)node
->prev
) {
1481 if (! node
->uielement
->GetRenderVisible () ||
1482 ! node
->uielement
->GetHitTestVisible ()) {
1483 // Ooops, looks like something changed.
1484 // We need to copy the list with some elements removed.
1485 List
*list
= copy_input_list_from_node (new_input_list
, (UIElementNode
*)node
->next
);
1486 delete new_input_list
;
1487 new_input_list
= list
;
1493 measuring_context_destroy (ctx
);
1496 input_list
= new_input_list
;
1499 #define SPEW_INPUT_LIST 0
1503 printf ("input_list: ");
1504 UIElementNode
*node
;
1505 for (node
= (UIElementNode
*)input_list
->First(); node
; node
= (UIElementNode
*)node
->next
) {
1506 if (node
!= input_list
->First())
1508 printf ("(%s)", node
->uielement
->GetName());
1514 // Perform any captures/releases that are pending after the
1515 // event is bubbled.
1517 PerformCapture (pendingCapture
);
1518 if (pendingReleaseCapture
|| (captured
&& !captured
->CanCaptureMouse ()))
1519 PerformReleaseCapture ();
1520 emittingMouseEvent
= false;
1525 Surface::UpdateCursorFromInputList ()
1527 MouseCursor new_cursor
= MouseCursorDefault
;
1529 // loop over the input list in order until we hit a node that
1530 // has its cursor set to the non-default.
1531 UIElementNode
*node
;
1532 for (node
= (UIElementNode
*)input_list
->First(); node
; node
= (UIElementNode
*)node
->next
) {
1533 new_cursor
= node
->uielement
->GetCursor ();
1534 if (new_cursor
!= MouseCursorDefault
)
1538 SetCursor (new_cursor
);
1542 Surface::SetFPSReportFunc (MoonlightFPSReportFunc report
, void *user_data
)
1544 fps_report
= report
;
1545 fps_data
= user_data
;
1549 Surface::SetCacheReportFunc (MoonlightCacheReportFunc report
, void *user_data
)
1551 cache_report
= report
;
1552 cache_data
= user_data
;
1556 Surface::SetExposeHandoffFunc (MoonlightExposeHandoffFunc func
, void *user_data
)
1558 expose_handoff
= func
;
1559 expose_handoff_data
= user_data
;
1560 expose_handoff_last_timespan
= G_MAXINT64
;
1563 class DownloaderNode
: public List::Node
{
1565 Downloader
*downloader
;
1566 DownloaderNode (Downloader
*dl
) { downloader
= dl
; }
1570 Surface::DetachDownloaders ()
1572 DownloaderNode
*node
;
1573 if (downloaders
== NULL
)
1576 node
= (DownloaderNode
*) downloaders
->First ();
1577 while (node
!= NULL
) {
1578 node
->downloader
->RemoveHandler (Downloader::DestroyedEvent
, OnDownloaderDestroyed
, this);
1579 node
->downloader
->SetSurface (NULL
);
1580 node
= (DownloaderNode
*) node
->next
;
1582 downloaders
->Clear (true);
1586 Surface::OnDownloaderDestroyed (EventObject
*sender
, EventArgs
*args
, gpointer closure
)
1588 DownloaderNode
*node
;
1589 Surface
*surface
= (Surface
*) closure
;
1590 List
*downloaders
= surface
->downloaders
;
1592 if (downloaders
== NULL
) {
1593 printf ("Surface::OnDownloaderDestroyed (): The list of downloaders is empty.\n");
1597 node
= (DownloaderNode
*) downloaders
->First ();
1598 while (node
!= NULL
) {
1599 if (node
->downloader
== sender
) {
1600 downloaders
->Remove (node
);
1603 node
= (DownloaderNode
*) node
->next
;
1606 printf ("Surface::OnDownloaderDestroyed (): Couldn't find the downloader %p in the list of downloaders\n", sender
);
1610 Surface::CreateDownloader (void)
1613 g_warning ("Surface::CreateDownloader (): Trying to create a downloader on a zombified surface.\n");
1617 Downloader
*downloader
= new Downloader ();
1618 downloader
->SetSurface (this);
1619 downloader
->SetContext (downloader_context
);
1620 downloader
->AddHandler (Downloader::DestroyedEvent
, OnDownloaderDestroyed
, this);
1621 if (downloaders
== NULL
)
1622 downloaders
= new List ();
1623 downloaders
->Append (new DownloaderNode (downloader
));
1629 Surface::CreateDownloader (EventObject
*obj
)
1631 Surface
*surface
= obj
? obj
->GetSurface () : NULL
;
1633 if (surface
== NULL
)
1634 surface
= Deployment::GetCurrent ()->GetSurface ();
1637 return surface
->CreateDownloader ();
1639 g_warning ("Surface::CreateDownloader (%p, ID: %i): Unable to create contextual downloader.\n",
1640 obj
, GET_OBJ_ID (obj
));
1646 Surface::VerifyWithCacheSizeCounter (int w
, int h
)
1648 if (! (moonlight_flags
& RUNTIME_INIT_USE_SHAPE_CACHE
))
1651 if (cache_size_multiplier
== -1)
1654 if (cache_size_in_bytes
+ (w
* h
* cache_size_multiplier
) < MAXIMUM_CACHE_SIZE
)
1661 Surface::AddToCacheSizeCounter (int w
, int h
)
1663 gint64 new_size
= w
* h
* cache_size_multiplier
;
1664 cache_size_in_bytes
+= new_size
;
1669 Surface::RemoveFromCacheSizeCounter (gint64 size
)
1671 cache_size_in_bytes
-= size
;
1675 Surface::FullScreenKeyHandled (GdkEventKey
*key
)
1677 if (!GetFullScreen ())
1680 // If we're in fullscreen mode no key events are passed through.
1681 // We only handle Esc, to exit fullscreen mode.
1682 if (key
->keyval
== GDK_Escape
)
1683 SetFullScreen (false);
1685 switch (key
->keyval
) {
1701 // Explicitly listing GDK_Escape here as it should never bubble up
1709 Surface::HandleUIFocusIn (GdkEventFocus
*event
)
1711 time_manager
->InvokeTickCalls();
1714 toplevel
->EmitGotFocus ();
1720 Surface::HandleUIFocusOut (GdkEventFocus
*event
)
1722 time_manager
->InvokeTickCalls();
1725 toplevel
->EmitLostFocus ();
1731 Surface::HandleUIButtonRelease (GdkEventButton
*event
)
1733 time_manager
->InvokeTickCalls();
1735 if (event
->button
!= 1 && event
->button
!= 3) {
1739 SetUserInitiatedEvent (true);
1742 gdk_event_free (mouse_event
);
1744 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1746 HandleMouseEvent (event
->button
== 1 ? UIElement::MouseLeftButtonUpEvent
: UIElement::MouseRightButtonUpEvent
,
1747 true, true, true, mouse_event
);
1749 UpdateCursorFromInputList ();
1750 SetUserInitiatedEvent (false);
1752 // XXX MS appears to do this here, which is completely stupid.
1754 PerformReleaseCapture ();
1756 return !((moonlight_flags
& RUNTIME_INIT_DESKTOP_EXTENSIONS
) == 0 && event
->button
== 3);
1760 Surface::HandleUIButtonPress (GdkEventButton
*event
)
1765 active_window
->GrabFocus ();
1767 time_manager
->InvokeTickCalls();
1769 if (event
->button
!= 1 && event
->button
!= 3)
1772 SetUserInitiatedEvent (true);
1775 gdk_event_free (mouse_event
);
1777 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1779 switch (event
->type
) {
1780 case GDK_3BUTTON_PRESS
:
1781 case GDK_2BUTTON_PRESS
:
1782 if (event
->button
!= 1)
1785 handled
= HandleMouseEvent (UIElement::MouseLeftButtonMultiClickEvent
, false, false, true, mouse_event
);
1788 if (event
->button
== 1)
1789 event_id
= UIElement::MouseLeftButtonDownEvent
;
1791 event_id
= UIElement::MouseRightButtonDownEvent
;
1793 handled
= HandleMouseEvent (event_id
, true, true, true, mouse_event
);
1797 UpdateCursorFromInputList ();
1798 SetUserInitiatedEvent (false);
1804 Surface::HandleUIScroll (GdkEventScroll
*event
)
1806 time_manager
->InvokeTickCalls();
1809 gdk_event_free (mouse_event
);
1811 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1813 bool handled
= false;
1815 handled
= HandleMouseEvent (UIElement::MouseWheelEvent
, true, true, true, mouse_event
);
1817 UpdateCursorFromInputList ();
1823 Surface::HandleUIMotion (GdkEventMotion
*event
)
1825 time_manager
->InvokeTickCalls();
1828 gdk_event_free (mouse_event
);
1830 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1832 bool handled
= false;
1834 if (event
->is_hint
) {
1835 #if GTK_CHECK_VERSION(2,12,0)
1836 if (gtk_check_version (2, 12, 0))
1837 gdk_event_request_motions (event
);
1842 GdkModifierType state
;
1843 gdk_window_get_pointer (event
->window
, &ix
, &iy
, (GdkModifierType
*)&state
);
1844 ((GdkEventMotion
*) mouse_event
)->x
= ix
;
1845 ((GdkEventMotion
*) mouse_event
)->y
= iy
;
1849 handled
= HandleMouseEvent (UIElement::MouseMoveEvent
, true, true, true, mouse_event
);
1850 UpdateCursorFromInputList ();
1856 Surface::HandleUICrossing (GdkEventCrossing
*event
)
1860 time_manager
->InvokeTickCalls();
1862 /* FIXME Disabling this for now... causes issues in ink journal
1863 GdkWindow *active_gdk_window = active_window->GetGdkWindow ();
1865 if (event->window && event->window != active_window->GetGdkWindow ()) {
1866 g_object_unref (active_gdk_window);
1869 g_object_unref (active_gdk_window);
1872 if (event
->type
== GDK_ENTER_NOTIFY
) {
1874 gdk_event_free (mouse_event
);
1875 mouse_event
= gdk_event_copy ((GdkEvent
*) event
);
1877 handled
= HandleMouseEvent (UIElement::MouseMoveEvent
, true, true, false, mouse_event
);
1879 UpdateCursorFromInputList ();
1882 // forceably emit MouseLeave on the current input
1883 // list.. the "new" list computed by HandleMouseEvent
1884 // should be the same as the current one since we pass
1885 // in the same x,y but I'm not sure that's something
1887 handled
= HandleMouseEvent (UIElement::MouseLeaveEvent
, false, false, true, mouse_event
);
1889 // MS specifies that mouse capture is lost when you mouse out of the control
1891 PerformReleaseCapture ();
1893 // clear out the input list so we emit the right
1894 // events when the pointer reenters the control.
1895 if (!emittingMouseEvent
) {
1897 input_list
= new List();
1905 Surface::GenerateFocusChangeEvents()
1907 while (!focus_changed_events
->IsEmpty ()) {
1908 FocusChangedNode
*node
= (FocusChangedNode
*) focus_changed_events
->Pop ();
1911 if (node
->lost_focus
) {
1912 el_list
= ElementPathToRoot (node
->lost_focus
);
1913 EmitEventOnList (UIElement::LostFocusEvent
, el_list
, NULL
, -1);
1917 if (node
->got_focus
) {
1918 el_list
= ElementPathToRoot (node
->got_focus
);
1919 EmitEventOnList (UIElement::GotFocusEvent
, el_list
, NULL
, -1);
1927 Surface::FocusElement (UIElement
*focused
)
1929 bool queue_emit
= FirstUserInitiatedEvent () && (focused
== NULL
|| focused_element
== NULL
|| focused_element
== GetToplevel ());
1930 if (focused
== focused_element
)
1933 focus_changed_events
->Push (new FocusChangedNode (focused_element
, focused
));
1934 focused_element
= focused
;
1937 AddTickCall (Surface::AutoFocusAsync
);
1942 Surface::ElementPathToRoot (UIElement
*source
)
1944 List
*list
= new List();
1946 list
->Append (new UIElementNode (source
));
1947 source
= source
->GetVisualParent();
1953 Surface::HandleUIKeyPress (GdkEventKey
*event
)
1955 time_manager
->InvokeTickCalls();
1957 Key key
= Keyboard::MapKeyValToKey (event
->keyval
);
1959 if (Keyboard::IsKeyPressed (key
))
1962 if (FullScreenKeyHandled (event
))
1965 #if DEBUG_MARKER_KEY
1966 static int debug_marker_key_in
= 0;
1967 if (event
->keyval
== GDK_d
|| event
->keyval
== GDK_D
) {
1968 if (!debug_marker_key_in
)
1969 printf ("<--- DEBUG MARKER KEY IN (%f) --->\n", get_now () / 10000000.0);
1971 printf ("<--- DEBUG MARKER KEY OUT (%f) --->\n", get_now () / 10000000.0);
1972 debug_marker_key_in
= ! debug_marker_key_in
;
1977 SetUserInitiatedEvent (true);
1980 Keyboard::OnKeyPress (key
);
1982 if (focused_element
) {
1983 List
*focus_to_root
= ElementPathToRoot (focused_element
);
1984 handled
= EmitEventOnList (UIElement::KeyDownEvent
, focus_to_root
, (GdkEvent
*)event
, -1);
1985 delete focus_to_root
;
1988 // in silverlight 1.0, key events are only ever delivered to the toplevel
1989 toplevel
->EmitKeyDown (event
);
1993 SetUserInitiatedEvent (false);
1999 Surface::HandleUIKeyRelease (GdkEventKey
*event
)
2001 time_manager
->InvokeTickCalls();
2003 if (FullScreenKeyHandled (event
))
2006 SetUserInitiatedEvent (true);
2009 Key key
= Keyboard::MapKeyValToKey (event
->keyval
);
2010 Keyboard::OnKeyRelease (key
);
2012 if (focused_element
) {
2013 List
*focus_to_root
= ElementPathToRoot (focused_element
);
2014 handled
= EmitEventOnList (UIElement::KeyUpEvent
, focus_to_root
, (GdkEvent
*)event
, -1);
2015 delete focus_to_root
;
2017 else if (toplevel
) {
2018 // in silverlight 1.0, key events are only ever delivered to the toplevel
2019 toplevel
->EmitKeyUp (event
);
2023 SetUserInitiatedEvent (false);
2029 Surface::HandleUIWindowAllocation (bool emit_resize
)
2037 Surface::HandleUIWindowDestroyed (MoonWindow
*window
)
2039 if (window
== fullscreen_window
) {
2040 // switch out of fullscreen mode, as something has
2041 // destroyed our fullscreen window.
2042 UpdateFullScreen (false);
2044 else if (window
== normal_window
) {
2045 // something destroyed our normal window
2046 normal_window
= NULL
;
2049 if (window
== active_window
)
2050 active_window
= NULL
;
2054 Surface::SetBackgroundColor (Color
*color
)
2056 if (background_color
)
2057 delete background_color
;
2059 background_color
= new Color (*color
);
2061 active_window
->SetBackgroundColor (color
);
2062 active_window
->Invalidate ();
2066 Surface::GetBackgroundColor ()
2068 return background_color
;
2072 Surface::IsVersionSupported (const char *version_list
)
2074 /* we support all 0.*, 1.0.*, 1.1.* and 2.0.* versions. */
2075 bool supported
= true;
2077 char *version
= NULL
;
2080 if (version_list
== NULL
)
2083 versions
= g_strsplit (version_list
, ".", 4);
2085 supported
= versions
[0] != NULL
&& versions
[1] != NULL
;
2088 for (int k
= 0; k
< 4; k
++) {
2090 version
= versions
[k
];
2092 if (version
== NULL
)
2095 if (version
[0] == 0) {
2100 // Only allow ascii 0-9 characters in the numbers
2101 for (int i
= 0; version
[i
] != 0; i
++) {
2102 if (version
[i
] < '0' || version
[i
] > '9') {
2108 numbers
[k
] = atoll (version
);
2111 switch (numbers
[0]) {
2112 case 0: // We support all versions of the format "0.*" and "1.*"
2116 supported
&= numbers
[1] == 0; // 2.0.*
2124 g_strfreev (versions
);
2130 runtime_init_browser (const char *plugin_dir
)
2132 runtime_init (plugin_dir
, RUNTIME_INIT_BROWSER
);
2136 runtime_init_desktop ()
2138 runtime_init (NULL
, RUNTIME_INIT_DESKTOP
);
2142 get_flags (gint32 def
, const char *envname
, struct env_options options
[])
2147 if (envname
&& (env
= g_getenv (envname
))) {
2148 printf ("%s = %s\n", envname
, env
);
2150 const char *flag
= env
;
2152 bool all
= !strcmp ("all", env
);
2156 while (*flag
== ',')
2162 while (*inptr
&& *inptr
!= ',')
2166 for (i
= 0; options
[i
].name
!= NULL
; i
++) {
2168 flags
|= options
[i
].flag
;
2172 if (n
!= strlen (options
[i
].name
))
2175 if (!strncmp (options
[i
].name
, flag
, n
)) {
2176 if (!options
[i
].set
)
2177 flags
&= ~options
[i
].flag
;
2179 flags
|= options
[i
].flag
;
2183 while (*inptr
== ',')
2193 runtime_init (const char *platform_dir
, guint32 flags
)
2198 if (cairo_version () < CAIRO_VERSION_ENCODE(1,4,0)) {
2199 printf ("*** WARNING ***\n");
2200 printf ("*** Cairo versions < 1.4.0 should not be used for Moon.\n");
2201 printf ("*** Moon is being run against version %s.\n", cairo_version_string ());
2202 printf ("*** Proceed at your own risk\n");
2205 if (running_on_nvidia ()) {
2206 printf ("Moonlight: Forcing client-side rendering because we detected binary drivers which are known to suffer performance problems.\n");
2207 flags
&= ~RUNTIME_INIT_USE_BACKEND_XLIB
;
2210 // Allow the user to override the flags via his/her environment
2211 flags
= get_flags (flags
, "MOONLIGHT_OVERRIDES", overrides
);
2213 debug_flags_ex
= get_flags (0, "MOONLIGHT_DEBUG", debug_extras
);
2214 debug_flags
= get_flags (0, "MOONLIGHT_DEBUG", debugs
);
2219 if (!g_type_inited
) {
2220 g_type_inited
= true;
2224 moonlight_flags
= flags
;
2226 Deployment::Initialize (platform_dir
, (flags
& RUNTIME_INIT_CREATE_ROOT_DOMAIN
) != 0);
2230 Media::Initialize ();
2234 runtime_shutdown (void)