1 This contains a minimal runtime in C for trying out some ideas for the
2 Moonlight binding. It is not designed to be our final implementation.
4 The C++ file here was merely to test Agg, but the public API is pure C
5 and without agg (which is currently disabled due to laziness on my
6 part) it should build with a C compiler
8 This is not likely the final version of this code.
12 C++ is only used for the class hierarchy and API entry points
13 are not exposed as C++ member methods but instead they are exposed
14 as regular C-callable methods that have access to the C++
15 public fields or any other public inline declarations.
17 Object creation uses the gtk-like naming scheme for constructors:
19 ClassName *class_name_new ();
21 All classes that inherit from DependencyObject have to be constructable,
22 even though their C# counterparts are abstract, because their C#
23 counterparts still have a public constructor (there can be derived
24 classes with no corresponding unmanaged representation, and we have
25 to use the C class of the abstract C# class).
27 Destruction of these objects is also done in the C-like way:
29 class_name_destroy (ClassName *object)
31 This is done so that our public API can be easily P/Invoked
32 from the managed side.
34 Notice that the majority of property setters and getters for
35 DependencyProperties will be done through two calls:
37 dependency_object_set_value
38 dependency_object_get_value
40 So there is no need to bind much more than that for
41 getting/setting, unless those setting/getting are useful to
42 develop/debug the C++ code.
44 * Base and Reference Counting
46 Base objects use a system similar to Gtk's object reference
47 counting. Objets are born in "floating" mode, which when
48 first refed turn into "refcount = 1".
50 This allows code like this:
52 shape_set_brush (rect, new SolidBrush ());
54 Without having to manually decrement the reference count for
55 the SolidBrush immediately following it.
57 When an object is exposed to the managed world, it must be
62 Items for the base class for all the objects that are rendered,
63 items contain a bounding box (double x1, y1, x2, y2) which must
64 be updated when the virtual method ->getbounds is called.
66 In addition each item can contain an affine transformation,
67 the code tracks the user set values as well as an absolute
68 affine transformation.
70 The encoding of the affine transformation is done with a
71 "double *" which, if not-NULL should point to six doubles in
72 the format expected by cairo_matrix_t (and libart).
74 The double [6] can be casted into a cairo_matrix_t, which is why
75 this appers like that in the source code. This was done in
76 case we want to switch to AGG.
80 I tried to simplify the class hierarchy from the one found in:
82 www.mono-project.com/WPFNotes
84 We will likely have to introudce some of the same classes to mirror
85 the hierarchy as my over-simplification left a few things out.
89 Currently the code stores all the properties in the classes,
90 this needs to be changed to use a DependencyProperty-like system,
91 which basically makes objects lightweight.
93 All objects basically have a hashtable:
97 And properties become for example:
99 static default_value_for_x;
103 if (properties.Contains ("x"))
104 return properties ["x"];
106 return default_value_for_x;
109 This ensures that objects with a few hundred exposed
110 properties (that are barely changed) do not consume a lot of memory.
112 * Caching commonly used values in DepdendencyObjects
114 For commonly used fields that might live in a dependency property, it
115 might be best to keep the data on the object itself. The only
116 way of doing this, and still support the DependencyObject system is
117 to use the class instance fields as a cache.
119 To do this, you can cache the values in the instance variable of the
120 class and monitor changes to the property overriding the OnPropertyChange
123 The OnPropertyChange method is called after the actual value has changed
124 so you must update your internally cached version of it with the value
125 in the dependencyproperty.
127 * DependencyObject::On*Changed methods
129 The following are guidelines for the different methods used in
130 property change notifications.
134 To perform operations when a property defined on your class is
135 changed, you must override this method.
139 MySubclass::OnPropertyChanged (DependencyProperty *prop)
141 if (prop->type != Type::MYSUBCLASSTYPE) {
142 ParentClass::OnPropertyChanged (prop);
146 if (prop == MySubclass::FirstProperty) {
149 else if (prop == MySubclass::FirstProperty) {
150 /* do different stuff stuff */
159 you don't need to explicitly call NotifyAttacheesOfPropertyChange.
160 this is done for you.
164 OnSubPropertyChanged:
166 if your class has a property that is a DependencyObject (say a
167 property of type Brush or Transform), you should override this
168 method to catch changes to that object's properties.
171 It should look like this:
175 MySubclass::OnSubPropertyChanged (DependencyProperty *prop, DependencyProperty *subprop)
177 if (prop == MySubclass::FirstProperty) {
178 /* do this whenever any part of the value of FirstProperty changes */
180 else if (prop == MySubclass::SecondProperty &&
181 subprop == SecondClass::Property) {
183 /* do this whenever the property "SecondClass::Property" changes on
184 the object stored in our MySubclass::SecondProperty property */
190 ParentClass::OnSubPropertyChanged (prop, subprop);
193 OnChildPropertyChanged:
195 if your class defines "attached" properties, like the Canvas
196 class's Left and Top, you must override this method to add
200 It should look like this:
203 MySubclass::OnChildPropertyChanged (DependencyProperty *prop, DependencyObject *child)
205 if (prop == MySubclass::FirstAttachedProperty) {
206 // do something when "child"'s value for FirstAttachedProperty changes
210 else if (prop == MySubclass::SecondAttachedProperty) {
211 // do something when "child"'s value for SecondAttachedProperty changes
222 if your class has any collection properties, it must override
223 this method to be able to deal with changes to elements of the
227 It should look like this:
230 MySubclass::OnCollectionChanged (Collection *col, CollectionChangeType type,
231 DependencyObject *obj, DependencyProperty *prop)
233 if (col == GetValue (MySubclass::MyCollectionProperty)->AsMyCollection()) {
234 /* do something when the collection MyCollectionProperty changes in any way */
236 else if (type == CollectionChangeItemRemoved &&
238 /* do something only when an item is removed */
244 ParentClass::OnCollectionChanged (col, type, obj, prop);
249 The video stuff is *incredibly* early at this point and
250 requires an FFmpeg installation from SVN (the only available
253 Currently this is very basic, it merely does video frame decoding, and
254 makes no attempt to keep track of the clock (as it should) nor to do any
255 kind of audio output (yet).
257 The video is also busted, I do not know why, but the "decoded"
258 video is incorrect, it has the wrong colors.
260 The video thread currently sends "messages" to the main thread
263 * To inform the main thread that the video has been
264 initialized and that the video bounds can be computed.
266 * To request an invalidate (when a new frame is ready)
268 The invalidate request could *perhaps* be done by taking the
269 Gdk lock and issuing the invalidate directly.
271 The issue is that we might need to lock also on the
272 (Item *) structures in case the user-code is making changes to the
273 affine transform and hence the bounding box as we are trying
274 to access those from the Gdk lock.
278 Currently we are using Cairo and an xlib surface that points to
279 a pixmap to do the rendering, but this has proven for some users
280 to be slower than the software rendering that we were using.
282 The software rendering used to use gdk_draw_pixbuf, which had
283 one problem: gdk_draw_pixbuf expects the data to be in a slightly
284 different ARGB format than the ones that Cairo and libswscale
287 To support software rendering we should re-implement this code and
288 and add support for byte swapping to the format required by gdk_draw_pixbuf
292 The demo has a couple of hardcoded videos for now, you must
293 edit the filenames hardcoded inside of it.
295 Sometimes you might also have to define VIDEO_DEMO at the top,
296 as every once in a while I will disable them.
298 There is currently a race condition in the rendering engine,
299 I have not yet been able to find out what it is, so sometimes
300 the engine will not display anything, restart it in those cases.
302 You will notice some rectangles painting, thats where the
303 video should be, it is also not clear why those move
307 To browser plugin installation, use:
313 It will install all need stuff to your local plugin folder at
314 ~/.mozilla/plugins. In plugin/test folder theres a file
315 index.html that can be use to see plugin in action. Since plugin
316 is under development, if you have any problems with it try to
317 execute your browser from command line and check log messsages,
318 this information can be useful to help us fix the problem.
320 If you debug this, you will run into:
323 Program received signal SIGSEGV, Segmentation fault.
324 [Switching to Thread 1088688944 (LWP 9517)]
325 0x43827037 in GC_find_limit (p=0x89cb1ec "", up=0) at os_dep.c:813
326 813 GC_noop1((word)(*result));
328 This is OK and is perfectly harmless, type "cont" to
331 * Mozilla plugin installer (user plugin)
333 To build a mozilla plugin installer (XPI) configure with
334 --enable-user-plugin and build as above. This modifies the libraries
335 to open the libraries it needs from ~/.mozilla/plugins and builds an
336 unsigned .xpi file which you may use to install the plugin in your
339 To install the plugin open the plugin/novell-moonlight.xpi with your
340 mozilla-based browser and follow the prompts.
342 * Some undocumented Silverlight features and their Moonlight implementation details
345 http://www.mono-project.com/MoonlightQuirks
347 ----------------------------------------------------------------------------------
349 ----------------------------------------------------------------------------------
353 1. Value() holds a ref to its contained dependency
354 object. This means we don't have to do anything special for
355 anything stored in a DP.
357 2. Collections hold a ref to their constituents.
359 3. If you *must* cache an DO pointer in an instance field, you
360 must ref/unref it properly. But really, you shouldn't need
365 I wonder if we should move instead to use as keys not the enum
366 values that we have, but instead the void * to the managed "Type"
367 as the key for the various types.
369 That would further unify the managed and native code
373 We are going to need a way of flagging classes as abstract to
374 avoid instantiating those from XAML.
376 Maybe a flag in UIElement::flags
378 * Cairo Considerations
380 surface_clear: should we use something like this?
382 cairo_set_source_rgba (s->cairo, 1.0, 1.0, 1.0, 1.0);
383 cairo_set_operator (s->cairo, CAIRO_OPERATOR_SOURCE);
384 cairo_paint (s->cairo);
386 But with a rectangle to set the region to clear?
390 - cairo line's cap (cairo_[g|s]et_line_cap) are identical for
391 both start and end. SL supports different line cap style on
392 both (StrokeStartCap and StrokeEndCap)
394 - cairo dashes (cairo_set_dash) do not support line cap. SL
395 support this (StrokeDashCap).
397 - cairo gradient brushes, like radial, don't behave the same
398 if the center point is outside the circle
400 - cairo currently doesn't have an implementation of the
401 documented cairo_stroke_to_path which is needed to be able
402 to render the ink's strokes outline color.
404 * Rendering Considerations
406 Currently we draw in an off-screen cairo surface when the
407 widget is not realized, seems like a waste of power, but its
408 used for bounding box computations.
410 Should we have a if (!realized) return in those places and
411 merely have a single cairo context that we paint to?
415 It seems that Silverlight ignores pressure information when
416 rendering static ink.
418 We currently hack around the OutlineColor (draw Outline then
419 draw inner color on top of it) so we can use a big pen (line
420 width) to draw strokes (otherwise we would need
421 cairo_stroke_to_path).
423 We don't (yet) support different Height/Width but that could
424 be added using a transform (and adjusting the coordinates).
428 The GSList that we use to keep track of attached objects could
429 actually be abused to store the values there, we can use the
430 bottom bit to track this info:
432 If bottom bit is set, the pointer to the value stored
437 If the bit is not set, we got a regular pointer to a
443 There are several things you must do when adding a new
444 class for use in moon.
446 a) If the class inherits from DependencyObject, you have to:
447 1. Override GetObjectType() to return that type.
448 2. Execute typegen.sh.
450 b) If the class does NOT inherit from DependencyObject, you have to:
451 1. Add a Value::Kind entry for it (in value.h.in).
452 2. add line(s) to types_init_first in type.cpp.in registering the
453 type and its parent type (if there is one.)
454 3. If it's used in the C++ code, add an As$TYPE method in
455 value.h.in and an implementation of it to runtime.cpp
456 4. Execute typegen.sh.
458 * TimeManager::Instance::Start
460 Currently we call this in runtime_init(), it should be further delayed until
461 we determine that we have a storyboard to play, otherwise we are wasting cycles.