2009-09-12 Chris Toshok <toshok@ximian.com>
[moon.git] / src / runtime.cpp
blob9fad75016312a9cadb5a40a4d00a8550ce5c39cc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * runtime.cpp: Core surface and canvas definitions.
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include <gtk/gtk.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <malloc.h>
21 #include <math.h>
23 #define Visual _XxVisual
24 #define Region _XxRegion
25 #include <gdk/gdkx.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <cairo-xlib.h>
28 #undef Visual
29 #undef Region
31 #include "runtime.h"
32 #include "canvas.h"
33 #include "color.h"
34 #include "shape.h"
35 #include "transform.h"
36 #include "animation.h"
37 #include "downloader.h"
38 #include "frameworkelement.h"
39 #include "textblock.h"
40 #include "media.h"
41 #include "stylus.h"
42 #include "rect.h"
43 #include "panel.h"
44 #include "value.h"
45 #include "namescope.h"
46 #include "xaml.h"
47 #include "dirty.h"
48 #include "fullscreen.h"
49 #include "utils.h"
50 #include "window-gtk.h"
51 #include "timemanager.h"
53 #include "contentcontrol.h"
54 #include "usercontrol.h"
55 #include "deployment.h"
56 #include "grid.h"
57 #include "cbinding.h"
58 #include "tabnavigationwalker.h"
60 //#define DEBUG_INVALIDATE 1
61 //#define RENDER_INDIVIDUALLY 1
62 #define DEBUG_REFCNT 0
64 #define CAIRO_CLIP 0
65 #define TIME_CLIP 0
66 #define TIME_REDRAW 1
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;
77 #if DEBUG
78 guint32 debug_flags_ex = 0;
79 guint32 debug_flags = 0;
80 #endif
83 struct env_options {
84 const char *name;
85 guint64 flag;
86 bool set;
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)
135 #if DEBUG
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 },
166 { NULL, 0, false }
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 },
178 { NULL, 0, false }
180 #endif
184 #define RENDER_EXPOSE (moonlight_flags & RUNTIME_INIT_SHOW_EXPOSE)
186 static bool
187 running_on_nvidia ()
189 int event, error, opcode;
191 Display *display = XOpenDisplay (NULL);
192 bool result = XQueryExtension (display, "NV-GLX", &opcode, &event, &error);
193 XCloseDisplay (display);
195 return result;
198 static void
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);
204 static void
205 cache_report_default (Surface *surface, long bytes, void *user_data)
207 printf ("Cache size is ~%.3f MB\n", bytes / 1048576.0);
210 GList *
211 runtime_get_surface_list (void)
213 if (!Surface::InMainThread ()) {
214 g_warning ("This method can be only called from the main thread!\n");
215 return NULL;
218 return surface_list;
221 static cairo_t *
222 runtime_cairo_create (GdkWindow *drawable, GdkVisual *visual, bool native)
224 int width, height;
225 cairo_surface_t *surface;
226 cairo_t *cr;
228 gdk_drawable_get_size (drawable, &width, &height);
230 if (native)
231 surface = cairo_xlib_surface_create (gdk_x11_drawable_get_xdisplay (drawable),
232 gdk_x11_drawable_get_xid (drawable),
233 GDK_VISUAL_XVISUAL (visual),
234 width, height);
235 else
236 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
238 cr = cairo_create (surface);
239 cairo_surface_destroy (surface);
241 return cr;
244 static gboolean
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!");
249 return FALSE;
250 } else if (inited == FALSE) {
251 g_warning ("Runtime has not been initialized yet, your flags will be overriden!");
252 return FALSE;
253 } else
254 return TRUE;
257 void
258 runtime_flags_set_manual_timesource (gboolean flag)
260 if (flags_can_be_modifed ())
261 moonlight_flags |= RUNTIME_INIT_MANUAL_TIMESOURCE;
264 void
265 runtime_flags_set_use_shapecache (gboolean flag)
267 if (flags_can_be_modifed ())
268 moonlight_flags |= RUNTIME_INIT_USE_SHAPE_CACHE;
271 void
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;
289 zombie = false;
290 needs_measure = false;
291 needs_arrange = false;
292 downloader_context = NULL;
293 downloaders = NULL;
294 background_color = NULL;
295 cursor = MouseCursorDefault;
296 mouse_event = NULL;
298 background_color = new Color (1, 1, 1, 0);
300 time_manager = new TimeManager ();
301 time_manager->Start ();
302 ticked_after_attach = false;
304 fullscreen_window = NULL;
305 normal_window = active_window = window;
306 if (active_window->IsFullScreen())
307 g_warning ("Surfaces cannot be initialized with fullscreen windows.");
308 window->SetSurface (this);
310 layers = new HitTestCollection ();
311 toplevel = NULL;
312 input_list = new List ();
313 captured = NULL;
315 focused_element = NULL;
316 focus_changed_events = new Queue ();
318 full_screen = false;
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;
326 fps_data = NULL;
328 fps_nframes = 0;
329 fps_start = 0;
331 cache_report = cache_report_default;
332 cache_data = NULL;
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;
346 #ifdef DEBUG
347 debug_selected_element = NULL;
348 #endif
350 up_dirty = new DirtyLists (true);
351 down_dirty = new DirtyLists (false);
353 surface_list = g_list_append (surface_list, this);
356 Surface::~Surface ()
358 time_manager->RemoveHandler (TimeManager::RenderEvent, render_cb, this);
359 time_manager->RemoveHandler (TimeManager::UpdateInputEvent, update_input_cb, this);
361 if (toplevel) {
362 toplevel->SetSurface (NULL);
363 toplevel->unref ();
366 #if DEBUG
367 if (debug_selected_element) {
368 debug_selected_element->unref ();
369 debug_selected_element = NULL;
371 #endif
373 HideFullScreenMessage ();
375 delete input_list;
377 g_free (source_location);
379 if (fullscreen_window)
380 delete fullscreen_window;
382 if (normal_window)
383 delete normal_window;
385 delete background_color;
387 time_manager->unref ();
389 delete up_dirty;
390 delete down_dirty;
392 delete downloaders;
393 layers->unref ();
395 surface_list = g_list_remove (surface_list, this);
398 void
399 Surface::Dispose ()
401 if (toplevel) {
402 toplevel->SetSurface (NULL);
403 toplevel->Dispose ();
406 EventObject::Dispose ();
409 void
410 Surface::Zombify ()
412 time_manager->Shutdown ();
413 DetachDownloaders ();
414 zombie = true;
417 void
418 Surface::SetCursor (MouseCursor new_cursor)
420 if (new_cursor != cursor) {
421 cursor = new_cursor;
423 active_window->SetCursor (cursor);
427 void
428 Surface::AutoFocus ()
430 GenerateFocusChangeEvents ();
433 void
434 Surface::AutoFocusAsync (EventObject *sender)
436 ((Surface *)sender)->AutoFocus ();
439 void
440 Surface::Attach (UIElement *element)
442 bool first = false;
444 #if DEBUG
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.");
453 #endif
455 #if SANITY
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 ());
460 #endif
463 if (toplevel) {
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 ();
470 toplevel->unref ();
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 ();
478 } else
479 first = true;
481 if (!element) {
482 DetachDownloaders ();
484 if (first)
485 active_window->EnableEvents (first);
487 active_window->Invalidate();
489 toplevel = NULL;
490 return;
493 if (!element->Is (Type::UIELEMENT)) {
494 printf ("Surface::Attach Unsupported toplevel %s\n", Type::Find (element->GetObjectType ())->GetName ());
495 return;
498 UIElement *canvas = element;
499 canvas->ref ();
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
508 if (first)
509 active_window->EnableEvents (first);
511 if (zombie)
512 return;
514 toplevel = canvas;
515 AttachLayer (canvas);
517 this->ref ();
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.
529 void
530 Surface::tick_after_attach_reached (EventObject *data)
532 Surface *surface = (Surface*)data;
534 surface->ticked_after_attach = true;
535 surface->Emit (Surface::LoadEvent);
538 void
539 Surface::toplevel_loaded (EventObject *sender, EventArgs *args, gpointer closure)
541 ((Surface*)closure)->ToplevelLoaded ((UIElement*)sender);
544 void
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
561 Value *vh, *vw;
562 vw = toplevel->GetValue (FrameworkElement::WidthProperty);
563 vh = toplevel->GetValue (FrameworkElement::HeightProperty);
564 if (vh || vw)
565 normal_window->Resize (MAX (vw ? (int)vw->AsDouble () : 0, 0),
566 MAX (vh ? (int)vh->AsDouble () : 0, 0));
569 Emit (ResizeEvent);
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 ();
581 void
582 Surface::AttachLayer (UIElement *layer)
584 if (layer == toplevel)
585 layers->Insert (0, Value(layer));
586 else
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.
597 void
598 Surface::DetachLayer (UIElement *layer)
600 layers->Remove (Value (layer));
601 layer->SetSurface (NULL);
602 if (active_window)
603 Invalidate (layer->GetBounds ());
606 void
607 Surface::Invalidate (Rect r)
609 active_window->Invalidate (r);
612 void
613 Surface::ProcessUpdates ()
615 active_window->ProcessUpdates();
618 void
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, &region);
626 void
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);
634 #ifdef DEBUG
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);
639 cairo_save (ctx);
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);
646 cairo_stroke (ctx);
647 cairo_restore (ctx);
649 #endif
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
659 void
660 Surface::Resize (int width, int height)
662 if (width == normal_window->GetWidth()
663 && height == normal_window->GetHeight())
664 return;
666 normal_window->Resize (width, height);
669 void
670 Surface::EmitSourceDownloadComplete ()
672 Emit (SourceDownloadCompleteEvent, NULL);
675 void
676 Surface::EmitSourceDownloadProgressChanged (DownloadProgressEventArgs *args)
678 Emit (SourceDownloadProgressChangedEvent, args);
681 void
682 Surface::EmitError (ErrorEventArgs *args)
684 Emit (ErrorEvent, args);
687 void
688 Surface::EmitError (int number, int code, const char *message)
690 ErrorEventArgs *args = new ErrorEventArgs ((ErrorType)number, code, message);
691 Emit (ErrorEvent, args);
694 void
695 Surface::Realloc ()
697 for (int i = 0; i < layers->GetCount (); i++) {
698 UIElement *layer = layers->GetValueAt (i)->AsUIElement ();
700 layer->InvalidateMeasure ();
701 //layer->UpdateBounds();
705 void
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.");
710 return;
713 UpdateFullScreen (value);
716 void
717 Surface::SetUserInitiatedEvent (bool value)
719 GenerateFocusChangeEvents ();
720 first_user_initiated_event = first_user_initiated_event | value;
721 user_initiated_event = value;
724 bool
725 Surface::IsTopLevel (UIElement* top)
727 if (top == NULL)
728 return false;
730 bool ret = top == full_screen_message;
732 for (int i = 0; i < layers->GetCount () && !ret; i++)
733 ret = layers->GetValueAt (i)->AsUIElement () == top;
735 return ret;
738 void
739 Surface::ShowFullScreenMessage ()
741 g_return_if_fail (full_screen_message == NULL);
742 //g_return_if_fail (toplevel && toplevel->Is (Type::PANEL));
744 Type::Kind dummy;
745 XamlLoader *loader = new XamlLoader (NULL, FULLSCREEN_MESSAGE, this);
746 DependencyObject* message = loader->CreateDependencyObjectFromString (FULLSCREEN_MESSAGE, false, &dummy);
747 delete loader;
749 if (!message) {
750 printf ("Unable to create fullscreen message.\n");
751 return;
754 if (!message->Is (Type::CANVAS)) {
755 printf ("Unable to create fullscreen message, got a %s, expected at least a UIElement.\n", message->GetTypeName ());
756 message->unref ();
757 return;
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) {
775 char *url = 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);
783 } else {
784 url = g_strdup (source_location);
786 } else if (g_str_has_prefix (source_location, "file://")) {
787 url = g_strdup ("file://");
788 } else {
789 url = g_strdup (source_location);
793 url_block->SetValue (TextBlock::TextProperty, url ? url : (char *) "file://");
794 g_free (url);
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));
820 const char*
821 Surface::GetSourceLocation ()
823 return source_location;
826 void
827 Surface::SetSourceLocation (const char* location)
829 g_free (source_location);
830 source_location = g_strdup (location);
833 void
834 Surface::HideFullScreenMessage ()
836 if (full_screen_message) {
837 DetachLayer (full_screen_message);
838 full_screen_message->unref ();
839 full_screen_message = NULL;
843 void
844 Surface::UpdateFullScreen (bool value)
846 if (value == full_screen)
847 return;
849 if (value) {
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);
858 } else {
859 active_window = normal_window;
861 HideFullScreenMessage ();
863 delete fullscreen_window;
864 fullscreen_window = NULL;
867 full_screen = value;
869 Realloc ();
871 time_manager->GetSource()->Stop();
872 Emit (FullScreenChangeEvent);
874 if (!value)
875 Emit (ResizeEvent);
876 time_manager->GetSource()->Start();
879 void
880 Surface::render_cb (EventObject *sender, EventArgs *calldata, gpointer closure)
882 Surface *s = (Surface *) closure;
883 gint64 now;
884 bool dirty = false;
886 GDK_THREADS_ENTER ();
887 if (s->zombie) {
888 s->up_dirty->Clear (true);
889 s->down_dirty->Clear (true);
890 } else {
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 ();
907 if (dirty) {
908 s->ProcessUpdates ();
911 if ((moonlight_flags & RUNTIME_INIT_SHOW_FPS) && s->fps_report) {
912 s->fps_nframes++;
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);
918 s->fps_nframes = 0;
919 s->fps_start = now;
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
926 // better.
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;
930 } else
931 s->cache_size_ticker++;
935 void
936 Surface::update_input_cb (EventObject *sender, EventArgs *calldata, gpointer closure)
938 #if notyet
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 ()
943 #endif
946 void
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 ();
955 void
956 Surface::HandleUIWindowUnavailable ()
958 time_manager->RemoveHandler (TimeManager::RenderEvent, render_cb, this);
959 time_manager->RemoveHandler (TimeManager::UpdateInputEvent, update_input_cb, this);
962 void
963 Surface::PaintToDrawable (GdkDrawable *drawable, GdkVisual *visual, GdkEventExpose *event, int off_x, int off_y, bool transparent, bool clear_transparent)
965 frames++;
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()))
972 return;
974 SetCurrentDeployment ();
976 #if TIME_REDRAW
977 STARTTIMER (expose, "redraw");
978 #endif
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);
984 #endif
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);
992 region->Draw (ctx);
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
1010 // windowless
1012 cairo_set_operator (ctx, CAIRO_OPERATOR_OVER);
1014 if (transparent) {
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);
1027 else {
1028 cairo_set_source_rgb (ctx,
1029 background_color->r,
1030 background_color->g,
1031 background_color->b);
1034 cairo_fill_preserve (ctx);
1035 cairo_clip (ctx);
1037 cairo_save (ctx);
1038 Paint (ctx, region);
1039 cairo_restore (ctx);
1041 if (RENDER_EXPOSE) {
1042 cairo_new_path (ctx);
1043 region->Draw (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));
1046 cairo_stroke (ctx);
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),
1054 0, 0);
1055 cairo_surface_set_device_offset (cairo_get_target (ctx),
1056 0, 0);
1058 cairo_set_source_surface (native, cairo_get_target (ctx),
1059 0, 0);
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);
1070 delete region;
1073 #if TIME_REDRAW
1074 ENDTIMER (expose, "redraw");
1075 #endif
1079 /* for emitting focus changed events */
1080 class FocusChangedNode : public List::Node {
1081 public:
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;
1094 if (lost_focus)
1095 lost_focus->ref ();
1096 if (got_focus)
1097 got_focus->ref ();
1100 FocusChangedNode::~FocusChangedNode ()
1102 if (lost_focus)
1103 lost_focus->unref ();
1104 if (got_focus)
1105 got_focus->unref ();
1108 RenderNode::RenderNode (UIElement *el,
1109 Region *region,
1110 bool render_element,
1111 RenderFunc pre,
1112 RenderFunc post)
1115 uielement = el;
1116 uielement->ref();
1117 this->region = region ? region : new Region ();
1118 this->render_element = render_element;
1119 this->pre_render = pre;
1120 this->post_render = post;
1123 void
1124 RenderNode::Render (cairo_t *ctx)
1126 bool front_to_back = uielement->UseBackToFront ();
1128 if (pre_render)
1129 pre_render (ctx, uielement, region, front_to_back);
1131 if (render_element)
1132 uielement->Render (ctx, region);
1134 if (post_render)
1135 post_render (ctx, uielement, region, front_to_back);
1138 RenderNode::~RenderNode ()
1140 if (uielement) {
1141 uielement->unref ();
1142 uielement = NULL;
1145 if (region)
1146 delete region;
1149 UIElementNode::UIElementNode (UIElement *el)
1151 uielement = el;
1152 uielement->ref();
1155 UIElementNode::~UIElementNode ()
1157 uielement->unref();
1158 uielement = NULL;
1161 void
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
1167 // is.
1169 captured = capture;
1170 List *new_input_list = new List();
1171 while (capture) {
1172 new_input_list->Append (new UIElementNode (capture));
1173 capture = capture->GetVisualParent();
1176 delete input_list;
1177 input_list = new_input_list;
1178 pendingCapture = NULL;
1181 void
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
1187 // recursive loop.
1188 captured->EmitLostMouseCapture ();
1189 captured = NULL;
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,
1194 // though.
1195 HandleMouseEvent (NO_EVENT_ID, false, true, false, mouse_event);
1198 void
1199 Surface::ReleaseMouseCapture (UIElement *capture)
1201 // Mouse capture is only released when the element owning the capture
1202 // requests it
1203 if (capture != captured && capture != pendingCapture)
1204 return;
1206 if (emittingMouseEvent)
1207 pendingReleaseCapture = true;
1208 else
1209 PerformReleaseCapture ();
1212 bool
1213 Surface::SetMouseCapture (UIElement *capture)
1215 if (captured || pendingCapture)
1216 return capture == captured || capture == pendingCapture;
1218 if (!emittingMouseEvent)
1219 return false;
1221 pendingCapture = capture;
1222 return true;
1225 EventArgs*
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);
1246 else {
1247 g_warning ("Unknown event id %d\n", event_id);
1248 return new EventArgs();
1252 bool
1253 Surface::EmitEventOnList (int event_id, List *element_list, GdkEvent *event, int end_idx)
1255 bool handled = false;
1257 int idx;
1258 UIElementNode *node;
1260 if (element_list->IsEmpty() || end_idx == 0)
1261 return handled;
1263 if (end_idx == -1)
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);
1279 if (h)
1280 handled = true;
1281 if (zombie) {
1282 handled = false;
1283 break;
1286 if (args_are_routed && ((RoutedEventArgs*)args)->GetHandled())
1287 break;
1290 args->unref();
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]);
1295 g_free (emit_ctxs);
1297 return handled;
1300 void
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.
1315 int i1, i2;
1316 UIElementNode *ui1, *ui2;
1318 *index1 = -1;
1319 *index2 = -1;
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) {
1330 *index1 = i1;
1331 *index2 = i2;
1333 else {
1334 return;
1337 ui1 = (UIElementNode*)ui1->prev;
1338 ui2 = (UIElementNode*)ui2->prev;
1339 i1--;
1340 i2--;
1344 static List*
1345 copy_input_list_from_node (List *input_list, UIElementNode* node)
1347 List *list = new List ();
1349 while (node) {
1350 list->Append (new UIElementNode (node->uielement));
1351 node = (UIElementNode*) node->next;
1354 return list;
1357 bool
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)
1378 return false;
1380 emittingMouseEvent = true;
1381 if (zombie)
1382 return false;
1384 if (toplevel == NULL || event == NULL)
1385 return false;
1387 // FIXME this should probably use mouse event args
1388 ProcessDirtyElements();
1390 if (captured) {
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);
1397 else {
1398 int surface_index;
1399 int new_index;
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 ();
1406 double x, y;
1408 gdk_event_get_coords (event, &x, &y);
1410 Point p (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);
1416 if (mouse_down) {
1417 if (!GetFocusedElement ()) {
1418 for (int i = layers->GetCount () - 1; i >= 0; i--) {
1419 if (layers->GetValueAt (i)->AsUIElement ()->Focus ())
1420 break;
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))
1428 break;
1430 // Raise any events caused by the focus changing this tick
1431 GenerateFocusChangeEvents ();
1435 // for 2 lists:
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
1445 // emitted.
1447 // If the first few nodes in each list differ, and,
1448 // for instance bn == am, we know that [am...] ==
1449 // [bn...]
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
1459 // events.
1461 FindFirstCommonElement (input_list, &surface_index,
1462 new_input_list, &new_index);
1464 if (emit_leave)
1465 handled = EmitEventOnList (UIElement::MouseLeaveEvent, input_list, event, surface_index);
1467 if (emit_enter)
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).
1477 if (handled) {
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;
1488 break;
1493 measuring_context_destroy (ctx);
1495 delete input_list;
1496 input_list = new_input_list;
1499 #define SPEW_INPUT_LIST 0
1501 #if SPEW_INPUT_LIST
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())
1507 printf ("->");
1508 printf ("(%s)", node->uielement->GetName());
1510 printf ("\n");
1512 #endif
1514 // Perform any captures/releases that are pending after the
1515 // event is bubbled.
1516 if (pendingCapture)
1517 PerformCapture (pendingCapture);
1518 if (pendingReleaseCapture || (captured && !captured->CanCaptureMouse ()))
1519 PerformReleaseCapture ();
1520 emittingMouseEvent = false;
1521 return handled;
1524 void
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)
1535 break;
1538 SetCursor (new_cursor);
1541 void
1542 Surface::SetFPSReportFunc (MoonlightFPSReportFunc report, void *user_data)
1544 fps_report = report;
1545 fps_data = user_data;
1548 void
1549 Surface::SetCacheReportFunc (MoonlightCacheReportFunc report, void *user_data)
1551 cache_report = report;
1552 cache_data = user_data;
1555 void
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 {
1564 public:
1565 Downloader *downloader;
1566 DownloaderNode (Downloader *dl) { downloader = dl; }
1569 void
1570 Surface::DetachDownloaders ()
1572 DownloaderNode *node;
1573 if (downloaders == NULL)
1574 return;
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);
1585 void
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");
1594 return;
1597 node = (DownloaderNode *) downloaders->First ();
1598 while (node != NULL) {
1599 if (node->downloader == sender) {
1600 downloaders->Remove (node);
1601 return;
1603 node = (DownloaderNode *) node->next;
1606 printf ("Surface::OnDownloaderDestroyed (): Couldn't find the downloader %p in the list of downloaders\n", sender);
1609 Downloader *
1610 Surface::CreateDownloader (void)
1612 if (zombie) {
1613 g_warning ("Surface::CreateDownloader (): Trying to create a downloader on a zombified surface.\n");
1614 return NULL;
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));
1625 return downloader;
1628 Downloader *
1629 Surface::CreateDownloader (EventObject *obj)
1631 Surface *surface = obj ? obj->GetSurface () : NULL;
1633 if (surface == NULL)
1634 surface = Deployment::GetCurrent ()->GetSurface ();
1636 if (surface)
1637 return surface->CreateDownloader ();
1639 g_warning ("Surface::CreateDownloader (%p, ID: %i): Unable to create contextual downloader.\n",
1640 obj, GET_OBJ_ID (obj));
1642 return NULL;
1645 bool
1646 Surface::VerifyWithCacheSizeCounter (int w, int h)
1648 if (! (moonlight_flags & RUNTIME_INIT_USE_SHAPE_CACHE))
1649 return false;
1651 if (cache_size_multiplier == -1)
1652 return false;
1654 if (cache_size_in_bytes + (w * h * cache_size_multiplier) < MAXIMUM_CACHE_SIZE)
1655 return true;
1656 else
1657 return false;
1660 gint64
1661 Surface::AddToCacheSizeCounter (int w, int h)
1663 gint64 new_size = w * h * cache_size_multiplier;
1664 cache_size_in_bytes += new_size;
1665 return new_size;
1668 void
1669 Surface::RemoveFromCacheSizeCounter (gint64 size)
1671 cache_size_in_bytes -= size;
1674 bool
1675 Surface::FullScreenKeyHandled (GdkEventKey *key)
1677 if (!GetFullScreen ())
1678 return false;
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) {
1686 case GDK_Down:
1687 case GDK_Up:
1688 case GDK_Left:
1689 case GDK_Right:
1690 case GDK_KP_Space:
1691 case GDK_space:
1692 case GDK_Tab:
1693 case GDK_Page_Down:
1694 case GDK_Page_Up:
1695 case GDK_Home:
1696 case GDK_End:
1697 case GDK_Return:
1698 case GDK_KP_Enter:
1699 return false;
1701 // Explicitly listing GDK_Escape here as it should never bubble up
1702 case GDK_Escape:
1703 default:
1704 return true;
1708 gboolean
1709 Surface::HandleUIFocusIn (GdkEventFocus *event)
1711 time_manager->InvokeTickCalls();
1713 if (toplevel)
1714 toplevel->EmitGotFocus ();
1716 return false;
1719 gboolean
1720 Surface::HandleUIFocusOut (GdkEventFocus *event)
1722 time_manager->InvokeTickCalls();
1724 if (toplevel)
1725 toplevel->EmitLostFocus ();
1727 return false;
1730 gboolean
1731 Surface::HandleUIButtonRelease (GdkEventButton *event)
1733 time_manager->InvokeTickCalls();
1735 if (event->button != 1 && event->button != 3) {
1736 return false;
1739 SetUserInitiatedEvent (true);
1741 if (mouse_event)
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.
1753 if (captured)
1754 PerformReleaseCapture ();
1756 return !((moonlight_flags & RUNTIME_INIT_DESKTOP_EXTENSIONS) == 0 && event->button == 3);
1759 gboolean
1760 Surface::HandleUIButtonPress (GdkEventButton *event)
1762 bool handled;
1763 int event_id;
1765 active_window->GrabFocus ();
1767 time_manager->InvokeTickCalls();
1769 if (event->button != 1 && event->button != 3)
1770 return false;
1772 SetUserInitiatedEvent (true);
1774 if (mouse_event)
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)
1783 return false;
1785 handled = HandleMouseEvent (UIElement::MouseLeftButtonMultiClickEvent, false, false, true, mouse_event);
1786 break;
1787 default:
1788 if (event->button == 1)
1789 event_id = UIElement::MouseLeftButtonDownEvent;
1790 else
1791 event_id = UIElement::MouseRightButtonDownEvent;
1793 handled = HandleMouseEvent (event_id, true, true, true, mouse_event);
1794 break;
1797 UpdateCursorFromInputList ();
1798 SetUserInitiatedEvent (false);
1800 return handled;
1803 gboolean
1804 Surface::HandleUIScroll (GdkEventScroll *event)
1806 time_manager->InvokeTickCalls();
1808 if (mouse_event)
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 ();
1819 return handled;
1822 gboolean
1823 Surface::HandleUIMotion (GdkEventMotion *event)
1825 time_manager->InvokeTickCalls();
1827 if (mouse_event)
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);
1838 else
1839 #endif
1841 int ix, iy;
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 ();
1852 return handled;
1855 gboolean
1856 Surface::HandleUICrossing (GdkEventCrossing *event)
1858 bool handled;
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);
1867 return TRUE;
1868 } else
1869 g_object_unref (active_gdk_window);
1872 if (event->type == GDK_ENTER_NOTIFY) {
1873 if (mouse_event)
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 ();
1881 } else {
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
1886 // we can rely on.
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
1890 if (captured)
1891 PerformReleaseCapture ();
1893 // clear out the input list so we emit the right
1894 // events when the pointer reenters the control.
1895 if (!emittingMouseEvent) {
1896 delete input_list;
1897 input_list = new List();
1901 return handled;
1904 void
1905 Surface::GenerateFocusChangeEvents()
1907 while (!focus_changed_events->IsEmpty ()) {
1908 FocusChangedNode *node = (FocusChangedNode *) focus_changed_events->Pop ();
1910 List *el_list;
1911 if (node->lost_focus) {
1912 el_list = ElementPathToRoot (node->lost_focus);
1913 EmitEventOnList (UIElement::LostFocusEvent, el_list, NULL, -1);
1914 delete (el_list);
1917 if (node->got_focus) {
1918 el_list = ElementPathToRoot (node->got_focus);
1919 EmitEventOnList (UIElement::GotFocusEvent, el_list, NULL, -1);
1920 delete (el_list);
1922 delete node;
1926 bool
1927 Surface::FocusElement (UIElement *focused)
1929 bool queue_emit = FirstUserInitiatedEvent () && (focused == NULL || focused_element == NULL || focused_element == GetToplevel ());
1930 if (focused == focused_element)
1931 return true;
1933 focus_changed_events->Push (new FocusChangedNode (focused_element, focused));
1934 focused_element = focused;
1936 if (queue_emit)
1937 AddTickCall (Surface::AutoFocusAsync);
1938 return true;
1941 List*
1942 Surface::ElementPathToRoot (UIElement *source)
1944 List *list = new List();
1945 while (source) {
1946 list->Append (new UIElementNode (source));
1947 source = source->GetVisualParent();
1949 return list;
1952 gboolean
1953 Surface::HandleUIKeyPress (GdkEventKey *event)
1955 time_manager->InvokeTickCalls();
1957 Key key = Keyboard::MapKeyValToKey (event->keyval);
1959 if (Keyboard::IsKeyPressed (key))
1960 return true;
1962 if (FullScreenKeyHandled (event))
1963 return true;
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);
1970 else
1971 printf ("<--- DEBUG MARKER KEY OUT (%f) --->\n", get_now () / 10000000.0);
1972 debug_marker_key_in = ! debug_marker_key_in;
1973 return true;
1975 #endif
1977 SetUserInitiatedEvent (true);
1978 bool handled;
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;
1987 else if (toplevel){
1988 // in silverlight 1.0, key events are only ever delivered to the toplevel
1989 toplevel->EmitKeyDown (event);
1990 handled = true;
1993 SetUserInitiatedEvent (false);
1995 return handled;
1998 gboolean
1999 Surface::HandleUIKeyRelease (GdkEventKey *event)
2001 time_manager->InvokeTickCalls();
2003 if (FullScreenKeyHandled (event))
2004 return true;
2006 SetUserInitiatedEvent (true);
2007 bool handled;
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);
2020 handled = true;
2023 SetUserInitiatedEvent (false);
2025 return handled;
2028 void
2029 Surface::HandleUIWindowAllocation (bool emit_resize)
2031 Realloc ();
2032 if (emit_resize)
2033 Emit (ResizeEvent);
2036 void
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;
2053 void
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 ();
2065 Color *
2066 Surface::GetBackgroundColor ()
2068 return background_color;
2071 bool
2072 Surface::IsVersionSupported (const char *version_list)
2074 /* we support all 0.*, 1.0.*, 1.1.* and 2.0.* versions. */
2075 bool supported = true;
2076 gchar **versions;
2077 char *version = NULL;
2078 gint64 numbers [4];
2080 if (version_list == NULL)
2081 return false;
2083 versions = g_strsplit (version_list, ".", 4);
2085 supported = versions [0] != NULL && versions [1] != NULL;
2087 if (supported) {
2088 for (int k = 0; k < 4; k++) {
2089 numbers [k] = 0;
2090 version = versions [k];
2092 if (version == NULL)
2093 break;
2095 if (version [0] == 0) {
2096 supported = false;
2097 break;
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') {
2103 supported = false;
2104 break;
2108 numbers [k] = atoll (version);
2111 switch (numbers [0]) {
2112 case 0: // We support all versions of the format "0.*" and "1.*"
2113 case 1:
2114 break;
2115 case 2:
2116 supported &= numbers [1] == 0; // 2.0.*
2117 break;
2118 default:
2119 supported = false;
2120 break;
2124 g_strfreev (versions);
2126 return supported;
2129 void
2130 runtime_init_browser (const char *plugin_dir)
2132 runtime_init (plugin_dir, RUNTIME_INIT_BROWSER);
2135 void
2136 runtime_init_desktop ()
2138 runtime_init (NULL, RUNTIME_INIT_DESKTOP);
2141 static gint32
2142 get_flags (gint32 def, const char *envname, struct env_options options[])
2144 gint32 flags = def;
2145 const char *env;
2147 if (envname && (env = g_getenv (envname))) {
2148 printf ("%s = %s\n", envname, env);
2150 const char *flag = env;
2151 const char *inptr;
2152 bool all = !strcmp ("all", env);
2153 size_t n;
2154 uint i;
2156 while (*flag == ',')
2157 flag++;
2159 inptr = flag;
2161 while (*flag) {
2162 while (*inptr && *inptr != ',')
2163 inptr++;
2165 n = (inptr - flag);
2166 for (i = 0; options[i].name != NULL; i++) {
2167 if (all) {
2168 flags |= options[i].flag;
2169 continue;
2172 if (n != strlen (options[i].name))
2173 continue;
2175 if (!strncmp (options[i].name, flag, n)) {
2176 if (!options[i].set)
2177 flags &= ~options[i].flag;
2178 else
2179 flags |= options[i].flag;
2183 while (*inptr == ',')
2184 inptr++;
2186 flag = inptr;
2189 return flags;
2192 void
2193 runtime_init (const char *platform_dir, guint32 flags)
2195 if (inited)
2196 return;
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);
2212 #if DEBUG
2213 debug_flags_ex = get_flags (0, "MOONLIGHT_DEBUG", debug_extras);
2214 debug_flags = get_flags (0, "MOONLIGHT_DEBUG", debugs);
2215 #endif
2217 inited = true;
2219 if (!g_type_inited) {
2220 g_type_inited = true;
2221 g_type_init ();
2224 moonlight_flags = flags;
2226 Deployment::Initialize (platform_dir, (flags & RUNTIME_INIT_CREATE_ROOT_DOMAIN) != 0);
2228 xaml_init ();
2229 downloader_init ();
2230 Media::Initialize ();
2233 void
2234 runtime_shutdown (void)
2236 if (!inited)
2237 return;
2239 Media::Shutdown ();
2241 inited = false;