2009-11-18 Chris Toshok <toshok@ximian.com>
[moon.git] / NOTES
blob7ab30397628f81e237378a8ab9f89301ec4e09db
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.
10 * Use of C++
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
58         referenced.
60 * Items
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.
69         
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.
78 * Class Hierarchy
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.
87 * Lighter objects
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:
95                 Hashtable properties;
97         And properties become for example:
99                 static default_value_for_x;
101                 object_get_x ()
102                 {
103                         if (properties.Contains ("x"))
104                                 return properties ["x"];
105                         else
106                                 return default_value_for_x;
107                 }
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
121         method.
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.
132         OnPropertyChanged:
134                 To perform operations when a property defined on your class is
135                 changed, you must override this method.
138                 void
139                 MySubclass::OnPropertyChanged (DependencyProperty *prop)
140                 {
141                         if (prop->type != Type::MYSUBCLASSTYPE) {
142                                 ParentClass::OnPropertyChanged (prop);
143                                 return;
144                         }
146                         if (prop == MySubclass::FirstProperty) {
147                                 /* do stuff */
148                         }
149                         else if (prop == MySubclass::FirstProperty) {
150                                 /* do different stuff stuff */
151                         }
152                         else if ...
154                         .
155                         .
156                         .
157                 }
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:
174                 void
175                 MySubclass::OnSubPropertyChanged (DependencyProperty *prop, DependencyProperty *subprop)
176                 {
177                         if (prop == MySubclass::FirstProperty) {
178                                 /* do this whenever any part of the value of FirstProperty changes */
179                         }
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 */
185                         }
186                         .
187                         .
188                         .
189                         else
190                                 ParentClass::OnSubPropertyChanged (prop, subprop);
191                 }
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
197                 support for them
200                 It should look like this:
202                 bool
203                 MySubclass::OnChildPropertyChanged (DependencyProperty *prop, DependencyObject *child)
204                 {
205                         if (prop == MySubclass::FirstAttachedProperty) {
206                                 // do something when "child"'s value for FirstAttachedProperty changes
208                                 return true;
209                         }
210                         else if (prop == MySubclass::SecondAttachedProperty) {
211                                 // do something when "child"'s value for SecondAttachedProperty changes
213                                 return true;
214                         }
216                         return false;
217                 }
220         OnCollectionChanged:
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
224                 collection.
227                 It should look like this:
229                 void
230                 MySubclass::OnCollectionChanged (Collection *col, CollectionChangeType type,
231                                                  DependencyObject *obj, DependencyProperty *prop)
232                 {
233                         if (col == GetValue (MySubclass::MyCollectionProperty)->AsMyCollection()) {
234                                 /* do something when the collection MyCollectionProperty changes in any way */
235                         }
236                         else if (type == CollectionChangeItemRemoved &&
237                                  col == ...) {
238                                 /* do something only when an item is removed */
239                         }
240                         .
241                         .
242                         .
243                         else
244                                 ParentClass::OnCollectionChanged (col, type, obj, prop);
245                 }
247 * Video
249         The video stuff is *incredibly* early at this point and
250         requires an FFmpeg installation from SVN (the only available
251         one). 
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
261         to do two things: 
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. 
276 * Rendering
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 
285         support.
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
290 * Demo
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
305 * Mozilla plugin
307         To browser plugin installation, use:
309                 make
311                 make install-plugin
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));
327                 
328         This is OK and is perfectly harmless, type "cont" to
329         resume execution.
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
337         browser.
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 
343   are listed on:
345         http://www.mono-project.com/MoonlightQuirks
346         
347 ----------------------------------------------------------------------------------
348 Issues
349 ----------------------------------------------------------------------------------
351 * Reference Counting
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.
356         
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
361            to cache it.
363 * Value and Kinds
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
371 * XAML Parsing
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?
388         Missing features:
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?
413 * Ink
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).
426 * Optimization Ideas
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
433                 there is:
435                         ->value & ~1
437                 If the bit is not set, we got a regular pointer to a
438                 GSList.
441 * Adding a new class
443         There are several things you must do when adding a new
444         class for use in moon.
445         
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.