In the last few weeks a large number of bugs have been
fixed in our Windows.Forms implementation which finally has
allowed us to get larger applications running with Mono.
As time goes by we are able to run more complicated
applications with Mono.
In October I imported a version of Paint.Net, a
paint application built with .NET into our repository. I
made some changes to it to get it working on Linux, this is a
screenshot as of this afternoon:
Paint.NET running on Linux
Paint.NET has been a fantastic test case, it is made up of
about 70,000 lines of C# code. Update: The port runs
in Mono, without any external dependencies (no Wine for instance).
Compiling Paint.NET on Mono: the first steps
To get the code building on Linux, I had to remove all the
references to a third party library that was used by the
Paint.NET back in October (DotNetWidgets). This is the last
release of Paint.NET that targeted .NET 1.x; Newer versions
of Paint.NET have replaced DotNetWidgets for the Strip
controls in Windows.Forms.
The port is not as clean as I would like to, as I switched
strategies in the middle of the port, let me explain.
First I used prj2make to turn the Visual Studio
solution into a Makefile; Then I had to rename some of the
files, as the actual filenames were spelled differently
(filename casing) than the files distributed. This was easy.
A rather large and obnoxious task was replacing all the
calls to DotNetWidgets (which provided Paint.NET with a spiced
up UI experience) with the more simple widgets that ship as
part of the standard API. This was done by removing the
reference to the library and then changing the code to use the
simpler widgets in a loop.
And this was my first mistake in the port. Instead of
planning the port, I kind of brute forced the port on my spare
time. About one third of the way in manually replacing the
code in my favorite editor, I realized that I should just have
implemented the DotNetWidgets interface and be done with it.
The result was a 129 line source file that in one hour gave me
everything I needed to get Paint.NET building without this
Exercise for the reader: describe the morale of the story.
Making it Run
This Paint application despite its young age is quite
sophisticated and calls into a number of Win32 libraries to
use features not exposed directly by the .NET libraries. For
example, it determines the number of CPUs to adjust its thread
pool, it uses low-level heap routines from the OS and other
things like that.
Luckily all this OS-specific code is neatly split into the
SystemLayer directory of Paint.Net and wrapped in a
handful of classes.
Since most of these routines are very simple to provide in
Unix I initially decided that it would be a good idea to
implement these functions in a way that other future
Windows.Forms applications would benefit, so I started
implementing a number of routines that are now part of our
Also, supportw is useful in showing how we can map
unmanaged calls back into managed calls, so it was a useful
example to have around for anyone which might need to "bounce"
unmanaged calls into the managed world.
At this point in November, Paint.NET started running into
missing functionality in Mono's System.Drawing and Mono's
Windows.Forms; I would take a break of a few months before
coming back to it a couple of weeks ago.
One of the early road blocks that I ran into with Paint.NET
was the incomplete support for Regions in our System.Drawing
implementation. We supported rectangular regions, but not
the GraphicsPath-based regions.
Implementing this was not an easy task. Around the same
time we started working with a third party control vendor that
uses System.Drawing extensively for his commercial product and
we also ran into ZedGraph
which is a popular library for generating plots which was
running into some gradient issues with our implementation.
was kind enough to take on the challenge of implementing these
ZedGraph in Mono, April 2006;
Sebastien blogged extensively about his work on GDI+, some screenshots:
Blogging about this attracted Bill Holmes, which helped
refine the finer points of the gradient brushes.
With GraphicsPath-based Regions implemented, gradients in
place it is now possible to get most of the basic operations
in Paint.NET going without a crash.
We are still missing support for "GraphicsPath.WidenPath"
(used by Paint.NET to paint ellipses and rectangles, but
really, who uses that these days? Aren't bezier paths all
Another missing feature are PathGradientBrushes. We do not
believe that this can be implemented with Cairo, but we would
love to be proved wrong. Cairo is the underlying engine that
we use to implement GDI+.
Some of the us secretly wish that the Xara
Xtreme library was already open sourced (they are planning
on releasing it) but to use it, we would need it to be
licensed under the LGPL or the MPL license.
Once the graphics issues were sorted out, Paint.NET turned
out to be a QA engineer dream come true. After a while, we
got to the point where we can actually draw with the program
and we have fixed the most critical performance problems
(initially it was not even possible to draw, due to a
trigger-happy redraw routine).
Midways through the port, I changed direction. Instead
of continuing to work on supporting the emulation of unmanaged
APIs by emulation or by bouncing the API to managed code, I
started to do a more regular "port". So I started to replace
calls to SystemLayer with proper Unix calls or other
Mono libraries. This has proved to be much simpler.
We might still implement a few emulation routines for some
P/Invokes, depending on the popularity of the technique, but
in general we will advocate that folks use OS-specific calls
depending on their needs.
Paint.NET has exposed a lot of bugs in our Windows.Forms
Sadly, in addition to the bugs reported, we need to fix
also the minor details, and the cosmetic aspects of
Windows.Forms before we can ship it.
At Novell, Jackson, Peter and Mike are now being joined by
Atsushi, Gonzalo and Chris (who were up to this point
working on ASP.NET 2.0) to assist in fixing bugs in
And of course lots of people in the community are helping
out, specially Alexander Olk, Jordi Mas and Jonathan Chambers.
Carlos is now working in collecting applications from the
CodeProject and SourceForge and repeat the process that I
described above for Paint.NET: we will be gathering
applications as test cases and Carlos will be routinely
testing them for potential regressions.
In addition, although we had a NUnit test case for
Windows.Forms we were not really using it, so many failures
went unnoticed for a while. So our immediate next step is to
make sure that our NUnit tests pass on Unix (Atsushi just
recently fixed them on Windows, as we had some incorrect tests
or tests that depended on a specific release of the
Atsushi has a great
call for contributors that includes some background
information on those which migh be interesting in helping:
So I have started to look around existing bug list and work on
it. At first I thought that it is highly difficult task: to
extract simple reproducible code from problematic sources, and
of course, to fix bugs. Turned out that it is not that
Actually, it is very easy to find MWF bugs. If you run your
MWF program, it will immediately show you some problems. What
I don't want you to do at this stage is just to report it as
is. We kinda know that your code does not work on Mono. It's
almost not a bug report. Please try to create good bug
report. Here I want to share my ideas on how you can try
creating good MWF bug reports and bugfixes.
Followed by some easy-to-follow steps on how to squash the
Other Mono Events
There are a number of exciting new developments in the Mono
Universe that I hope to write about next week in more detail
(news on our compacting GC, a new IR representation for Mono, a few
touch-ups for our inliner).
The summer of code has some great proposals, and hopefully
next week we will have the definite list of projects that we
will be mentoring, but the one am most excited about is a VB.NET
compiler (VB.NET 8, with Generics and all that) written in