Friday, September 30, 2011

MonoGame 1.6 is almost out of the door


Since the project at work using Moonlight has stabalised nicely I have turned the time I use for open source projects towards MonoGame.

For those of you who dont keep uptodate with the game programming world or have been living under a large bolder for the last few years MonoGame is an open source implementation of  Microsofts XNA Framework. Put it simply the XNA framework is a Managed .net game development environment for which you can develop games for Windows,, XBox 360 , Zune and now Windows Mobile 7.


The MonoGame project is brining the XNA framework to other platforms , we currently support Mac OSX, Linux, iPhone, iPad, Windows and Android. What is really cool is that with only one (maybe 2) #ifdef statements the same code will run on all of those platforms :). There are already games using the framework available on the App Store for iPhone.

With the 1.6 release we will be supporting Android as well which is where my interest is. I already have it running nicely on my Galaxy S and plan to test it out on a eTouch as well to see how it works with lower spec devices. One of the really great things about this project is that not only do we have things like shaders working (although no 3D....yet ;) ) we also have networking working , across all of those platforms. So you can play a game on a iPhone against someone on an Android. We did some tests and had 5 different clients working over the internet. There is still allot of work to do on that and unfortunatly we cannot include the XBox 360 in the list of games we can connect due to the networking technology used.

If you have time and have any XNA based games that you want to try on android or iOS, or just want to write games check it out.

The Windows Phone 7 Platformer example running on Android

Thursday, May 19, 2011

Run WPF Applications on Linux

One of the recuring questions the mono team get is will they support WPF in mono. As Miguel de Icaza posted it his blog this would take years or work to implement the entire WPF framework over to mono. There is a quick alternative if you dont mind not having the full WPF framework and opting for Silverlight 3/4 compatability for your xaml and some of the framework. While you can produce Desktop applications using Silverlight and Moonlgiht with the new Out of Browser option , this kind of applicaiton still runs in a sandboxed environment which is one of the reasons people choose WPF over Silverlight in some cases because it gives you full access to the .net framework ( for example UDP Sockets, Hard disk access etc)

One of my more recent c# projects was producing a skinable dekstop user interface for a project, the obvious solution would be use WPF. The style support is built in and fairly easy to use so it was a no brainer, the only down side was that it needed to run on an embedded system running linux. Enter Moonlight... the Moonlight project does provide a way to run apps on the desktop using a program called mopen. This program creates a GTK window in which to host the moonlight runtime engine, the main drawback is that it only supports running the xaml files directly or loading a .xap file.

One of the other requirements was that the system needed to support plugin dlls for new screens, while allot of this could be accomplished using xap files, I decided to take a look a what it would take to write a true desktop moonlight app which would run from a mono application.

In order for this whole system to work a couple of changes were needed to the MoonlightHost.cs and the Deployment.cs classes in the moonlight source code. My previous blog post showed how to patch the files and provides a link to a ubuntu repository which you can use (for 10.04) to test this stuff out.

The whole applicaiton is based around a "host" application like mopen, but speficially designed to handle loading xaml based screens from the local directory rather than from an extracted xap file. Here is a class layout of the project





















Our app entry point is  the main static method in Program.cs. Within this method we will use the WindowManager to Create an IWindow instance. As you probably guessed under windows we will create a WPFWindow instance, but under moonlight we create a MoonlightWindow. Both of these window classes support the IWindow interface and as such have a Run method and a Content property.

The Run method does what you'd expect, it shows the window and does not return until the window is closed. The Content property is of type System.Windows.UIElement and this is where we set the control we want to display. For now this is hardcoded but we will add support for loading the content from a dll later.

Lets look at the contents of the WPFWindow class.

public class WPFWindow : IWindow
 {
     private System.Windows.Window window;
     private System.Windows.Application app = new System.Windows.Application();  
  
     public WPFWindow(string[] args)
     {      
        window = new System.Windows.Window();    
        window.SizeToContent = SizeToContent.WidthAndHeight;
        window.Background = new SolidColorBrush(Colors.Black);
        window.Closed += delegate 
        {          
           Dispatcher.CurrentDispatcher.InvokeShutdown();
        };                   
        window.UpdateLayout();
     }
               
     public void Exit()
     {
      window.Close();
     }
          
     public void Run()
     {
      app.Run(window);
     }
     
     public UIElement Content
     {
      get {
          return (UIElement)window.Content;
      }
      set {
          window.Content = value;
      }
     }
 }
All fairly straight forward. We manually create a WPF Window and a System.Windows.Application (which is a WPF host).

So lets take a look at the Moonlight Window

public class MoonlightWindow : IWindow
 {
 
     private Gtk.Window window;
     private MoonlightHost host;
      
     public MoonlightWindow(string[] args)
     {
  string manifest = BuildAppManifest();
  System.IO.File.WriteAllText("appmanifest.xaml", manifest); 
AppDomain.CurrentDomain.AssemblyResolve += delegate (object sender, ResolveEventArgs rea) 
         {
    string mapped = MapVersion (rea.Name);
    Assembly result = mapped != null ? Assembly.Load (mapped) : null;
    if(result == null)
    {
       Debug.WriteLine("AssemblyResolveEvent ({0}, {1}) mapped to: '{2}' loaded assembly: '{3}'", sender, rea.Name, mapped, result != null ? result.FullName : "not found");
    }      
         return result;
         };
   
         Gtk.Application.Init();
                MoonlightRuntime.Init();

                window = new Gtk.Window("");
  window.DeleteEvent += delegate
                {                 
                  Gtk.Application.Quit();
                };              
            
  host = new MoonlightHost();   
                host.LoadDesklet();                 
                window.Add(host);       
    }
            
           public void Exit()
           {          
              Gtk.Application.Quit();
           }
  
    public void Run()
    {
  window.ShowAll();            
  Gtk.Application.Run();
      }
  
    public UIElement Content
    {
      get {
       return host.Application.RootVisual;
      }
      set {
       host.Application.RootVisual = value; 
FrameworkElement top = (FrameworkElement)value; 
host.SetSizeRequest((int)top.Width, (int)top.Height); 
window.Resize((int)top.Width, (int)top.Height); 
}
    }
  
  
  /// 
  /// Builds the appmanifest file for Moonlight. This is required so that the correct styles and classes are initialized 
  /// 
  /// 
  internal string BuildAppManifest()
  {
   
   StringWriter sw = new StringWriter();
sw.WriteLine("<Deployment xmlns='http://schemas.microsoft.com/client/2007/deployment' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
   sw.WriteLine(" RuntimeVersion='4.0.40624.0'>");
   sw.WriteLine("<Deployment.Parts>");
      
   string[] screenfiles = Directory.GetFiles(".", "*.dll");
   foreach(string file in screenfiles)
   {
    if (!file.StartsWith("Moonlight.") && !file.StartsWith("System."))
    {
       sw.WriteLine(String.Format("<AssemblyPart x:Name='{0}' Source='{1}'/>",Path.GetFileNameWithoutExtension(file), file));
    }
   } 
   sw.WriteLine("</Deployment.Parts>");
   sw.WriteLine("</Deployment>");     
   return sw.ToString();
   
  }
  
  static string MapVersion (string name)
  {
   string version = typeof (int).Assembly.GetName ().Version.ToString ();
   switch (name) {
    case "Microsoft.VisualBasic, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35":
     return "Microsoft.VisualBasic, Version=" + version + ", Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
    case "mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "mscorlib, Version=" + version + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";
    case "System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System.Core, Version=" + version + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";
    case "System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System, Version=" + version + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";
    case "System.Net, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System.Net, Version=" + version + ", Culture=neutral, PublicKeyToken=7cec85d7bea7798e";
    case "System.Runtime.Serialization, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System.Runtime.Serialization, Version=" + version + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";
    case "System.ServiceModel, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35":
     return "System.ServiceModel, Version=" + version + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";
    case "System.ServiceModel.Web, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System.ServiceModel.Web, Version=" + version + ", Culture=neutral, PublicKeyToken=31bf3856ad364e35";
    case "System.Xml, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System.Xml, Version=" + version + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";
 
    /* these are the only 3.0 assemblies we need to redirect to, all the others redirect to fx assemblies */
    case "System.Windows.Browser, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System.Windows.Browser, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756";
    case "System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e":
     return "System.Windows, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756";
   }
 
   return null;
  }
 
    }

This is a bit more complicated. There are some bit that have been borrowed from mopen. MapVersion makes sure that the Moonlight/Silverlight dll references are mapped to the correct Moonlight Versions. BuildAppManifest is a method that builds an appmanifest.xaml file when the application starts, this is required so that Moonlight correct loads the default themes for the components and also to ensure that any external dll's that contain xaml resources or components are registered with Moonlight.

The most important lines are the following

host = new MoonlightHost();   
host.LoadDesklet();    

This is where the new method on the MoonlightHost class , LoadDesklet, is called. This method initializes the MoonlightRuntime so that it will load assemblies from the local application directory and not from a temp directory where a xap file was extracted to.

So with all of those pieces in place lets take a look at the main method in Program.cs

public class Program
 {
  [STAThread()]
  public static void Main(string[] args)
  {
   IWindow window = WindowManager.CreateWindow(args);
   
   var c = new Canvas();
   c.Width = 640;
   c.Height = 480;
   c.Background = new SolidColorBrush(Colors.Blue);
   window.Content = c;
      
   window.Run();
  }
 }

The is really simple code. We create the IWindow using the WindowManager, then create our content and assign that to the window.Content property and finally run the app. This will produce the same result in Windows and Linux.

Building the application under windows, you can just open the solution with Visual Studio 2010 (Express) or greater or use the SharpDevlop 4.0 IDE which is a great tool.

Under linux this soltuion might build under monodevelop, but I havent tested it. I prefer to use xbuild from the command prompt. In order to build the project you must first have installed a patched version of mono and moonlight with the new methods. I have a debian package available which you can read about how to install here.

To use these package you will need to use the source command to update the search paths. You can download the appropriate script from here, then use
source /usr/local/bin/moon4-dev-env

once you are in a parallel enviroment (you will see a [moon4-dev] at the console) you can run the following commands to build and then run the app.

xbuild  WPFMoonlight.sln
cd ./WPFMoonlight/bin/Debug
mono WPFMoonlight.exe

This should run up the app. Currently the content is hard coded but this can be replaced with calls to Assembly.LoadFile or LoadFrom to dynamiacally load assemblies and then use Reflection to figure out which Content to show.

I'll try to post something up that shows how to do that via a nice plugin interface. In the meantime you can get the source code for this project here.

Tuesday, May 17, 2011

Using Moonlight for Desktop Applications

One of the things people seem to ask is if you can get WPF applications working under linux. While you cant take a WPF app and run it directly there are ways to utilize the Moonlight engine to resuse your business logic and the xaml resources to work in a desktop senario under linux.

Currently Moonlight has a tool called mopen which allows you to host xap files in a desktop widget which is a nice way to package your entire application up and get it to run on the desktop. There is a tool called mpack which you can use to build the desktop xap file to use with mopen. However if you want to just have an exe and load the dll's locally then mopen is not going to cut it..yet, there are comments in the code which suggest this might be added in the future.

In order to develop full desktop applications with moonlight you will need to patch the current moonlight trunk with the patch at the end of this entry. I wont go into the details on how to get moonlight building as there are notes on how to do this in the README files on git.

If you would rather not have to build the source yourself I have a public repository for ubuntu distros which contains both the runtime only environment (no compilers or debug support) and the development environment, these debians are based on the latest mono trunk (2.11) and the latest moonlight 4 trunk.

The runtime debian is about 30MB, the development one is 97MB. Note there are allot (and I mean allot) of dependencies for moonlight so it might take a while.

To use the repository you will need to add
deb http://repo.infinitespace-online.net /
to your sources.list (via the GUI or via the command line) then run
sudo apt-get update
sudo apt-get install moon4-desktop
for the runtime or
sudo apt-get update
sudo apt-get install moon4-desktop-dev
for the development environment.

Both debians install into the /opt/ folder rather than in /usr. This is so you dont mess up any existing mono installation. However you will need to use the "source" command to update your environment do use the new mono install. Both debians place a script in /usr/local/bin so you can run the following commands

source /usr/local/bin/moon4-env

for the runtime environment and

source /usr/loca/bin/moon4-dev-env

for the development environment. In both cases your terminal should change from your usual @ to a [moon4] or [moon4-dev].

Now if you want to build your own version of the desktop support then you will need to get the latest moon trunk from git and apply the following patch.

diff --git a/class/System.Windows/System.Windows/Deployment.cs b/class/System.Windows/System.Windows/Deployment.cs
index 9962983..ea3c94c 100644
--- a/class/System.Windows/System.Windows/Deployment.cs
+++ b/class/System.Windows/System.Windows/Deployment.cs
@@ -299,11 +299,28 @@ namespace System.Windows {
     throw new MoonException (2105, e.Message);
    }
   }
+  
+  internal bool InitializeDeployment (string localPath) {
+   XapDir = localPath;   
+   TerminateAndSetCulture (null, null);
+
+   NativeMethods.deployment_set_initialization (native, true);
+   try {
+    EntryPointType = "System.Windows.Application";
+    EntryPointAssembly = typeof (Application).Assembly.GetName ().Name;
+    EntryAssembly = typeof (Application).Assembly;
+    ReadManifest ();
+    return LoadAssemblies ();
+   }
+   finally {
+    NativeMethods.deployment_set_initialization (native, false);
+   }
+  }
 
   internal bool InitializeDeployment (string culture, string uiCulture)
   {
    TerminateAndSetCulture (culture, uiCulture);
-
+            
    NativeMethods.deployment_set_initialization (native, true);
    try {
     EntryPointType = "System.Windows.Application";
diff --git a/gtk/Moonlight.Gtk/MoonlightHost.cs b/gtk/Moonlight.Gtk/MoonlightHost.cs
index b2a17e9..a085b13 100644
--- a/gtk/Moonlight.Gtk/MoonlightHost.cs
+++ b/gtk/Moonlight.Gtk/MoonlightHost.cs
@@ -70,10 +70,10 @@ namespace Moonlight.Gtk
    Mono.Xaml.XamlLoader.AllowMultipleSurfacesPerDomain = true;
 
    windowingSystem = NativeMethods.runtime_get_windowing_system ();
-   window = NativeMethods.moon_windowing_system_create_window (windowingSystem, MoonWindowType.Plugin, 0, 0, IntPtr.Zero, IntPtr.Zero);
+   window = NativeMethods.moon_windowing_system_create_window (windowingSystem, MoonWindowType.Plugin, 0, 0, IntPtr.Zero, IntPtr.Zero);        
    surface = NativeMethods.surface_new (window);
    Raw = NativeMethods.moon_window_gtk_get_native_widget (window);
-
+   
    SizeAllocated += OnSizeAllocated;
   }
 
@@ -122,6 +122,17 @@ namespace Moonlight.Gtk
   }
 
   /// 
+  /// Initializes the Moonlight Plugin for a Desktop environment
+  /// This will allow moonlight data to be loaded from other dll's and not from within a xap file.
+  /// 
+  public void LoadDesklet()
+  {
+   string path = System.IO.Path.GetDirectoryName(".");
+      
+   Deployment.Current.InitializeDeployment(path);
+  }
+  
+  /// 
   ///    Initializes the Surface widget from the XAML contents in a string
   /// 
   /// The contents of the string.@@ -146,6 +157,7 @@ namespace Moonlight.Gtk
    Content = (FrameworkElement)toplevel;
   }
 
+
   /// 
   ///    Initializes the GtkSilver widget from the XAML contents in a file
   /// 
diff --git a/src/bitmapimage.cpp b/src/bitmapimage.cpp
index 0d1c901..f183d66 100644
--- a/src/bitmapimage.cpp
+++ b/src/bitmapimage.cpp
@@ -502,7 +502,7 @@ BitmapImage::CreateLoader (unsigned char *buffer)
    moon_error = new MoonError (MoonError::EXCEPTION, 4001, "unsupported image type");
   }
  } else {
-  loader = Runtime::GetWindowingSystem ()->CreatePixbufLoader (NULL);
+  loader = Runtime::GetWindowingSystem ()->CreatePixbufLoader ("png");
  }
 }
 
diff --git a/src/deployment.cpp b/src/deployment.cpp
index 14e55fa..3fe738f 100644
--- a/src/deployment.cpp
+++ b/src/deployment.cpp
@@ -205,6 +205,7 @@ Deployment::Initialize (const char *platform_dir, bool create_root_domain)
   Deployment::desktop_deployment->InitializeDesktop (root_domain);
   Deployment::SetCurrent (Deployment::desktop_deployment);
   Deployment::desktop_deployment->EnsureManagedPeer ();
+                Deployment::desktop_deployment->InitializeAppDomain();
 
   Application *desktop_app = MoonUnmanagedFactory::CreateApplication ();
   desktop_deployment->SetCurrentApplication (desktop_app);


then build and install.

The Patch adds a new method (amongst other things) to the Moonlight.Gtk.MoonlightHost class called LoadDesklet which initializes the moonlight engine to allow local dll's to be loaded rather than expecting the binaries to be packed up in a xap file. This is more like the way WPF works under windows. Although you are still restricted to Silverlight 3/4 based Xaml support.

Next time I'll go through how to use this new stuff to develop desktop applications.

Monday, November 1, 2010

Moonlight WritableBitmap and System.Drawing.Bitmap

A recent project required that I write a Moonlight client for an existing WCF service. This WCF service returned image information as System.Drawing.Bitmap rather than a url which a web client could use.

Now I could have just saved the image to a MemoryStream and then set a BitmapImage.StreamSource to be that stream, but I wanted to know if I could use a WritableBitmap to do the work rather than creating a new BitmapImage each time I get a image from the WCF service.


public static bool Convert(System.Drawing.Image image, ref WriteableBitmap _writableBitmap)
       {
            try
               {
                   if (_writableBitmap == null)
                   {                  
                      _writableBitmap = new WriteableBitmap((int)image.Width, (int)image.Height);
                   }
                   System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)image;              
                   System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
                   System.Drawing.Imaging.BitmapData bitmapData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                   try
                   {                               
                    int cnt = bmp.Width * bmp.Height * 4 / 4;
                    System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, _writableBitmap.Pixels, 0, cnt);                    
                   }
                   finally
                   {
                    bmp.UnlockBits(bitmapData);
                   }
                   return true;
                 }
                 catch
                 {
                   return false;
                 }
       }
As it turns out the code to put the data from a 32bit bitmap into a Writeablebitmap is quiet simple :)

Thursday, December 10, 2009

Building Mono for Moonlight on Ubuntu 9.10

In this post we are going to build and install the version of mono that works with the Beta 9 of moonlight. To make things easy we are going to be building on Ubuntu 9.10 Karmic. There are a number of reasons for this, the package manager makes it easy to install the various dependencies we need but mainly it's the distro I am most familiar with.

First thing we need to do is to get the dependencies installed for building Mono 2.6. The current version of mono is 2.4 and Moonlight 2.0 will not work with that version so until 2.6 gets released and makes it's way into ubuntu we'll have to build it ourselves.
sudo apt-get install build-essential mono-mcs bison gettext libglib2.0-dev
This will get everything we need to build Mono, you will probably have some of those packages installed already. I did those on a minimal install machine so I had to install all of these packages.

now run the following commands, this will download the latest moonlight beta 9 version of the mono framework.
wget http://ftp.novell.com/pub/mono/sources/moon/1.99.9/mono-2.6.tar.bz2
wget http://ftp.novell.com/pub/mono/sources/moon/1.99.9/mono-basic-2.6.tar.bz2
tar -xjvf mono-2.6.tar.bz2
tar -xjvf mono-basic-2.6.tar.bz2
once this is done cd into the mono-2.6 directory and type
./configure --prefix=/usr  --with-moonlight=yes
This sets up the build environment. The --prefix part is something I do when installing on ubuntu. By default Mono will build to /usr/local however in more ubuntu systems mono is installed in /usr, now you can (and are advised to) setup a parallel build environment when using mono, but I prefer to use the latest stuff and just refresh my local build as and when new stuff comes out.

Now that configure has done it's stuff you can just type
make
the go a do something else for a while. Once it's finished you should be able to
sudo make install
and that should install the Mono 2.6 system. You can verify that by typing
mono --version
We now need to build mono-basic-2.6, just follow the same procedure
cd mono-basic-2.6
./configure --prefix=/usr
make
sudo make install
That should be all you need to do. You should now have a working Mono 2.6 installation.

Tuesday, December 8, 2009

Moonlight 2 preview is almost here


The title says it all... For those of you who don't know Moonlight is the open source implementation of Silverlight. It runs on linux machines and will allow developers to bring silverlight applications to linux not only in browsers but also on the Desktop.

It's this Desktop capability that has me interested. One of the cool things about developing using Silverlight (and WPF it's desktop version ) is the ability to create stylish user interfaces easily and quickly. Before Moonlight these user interfaces were for Windows and Mac OSX only, but not anymore.

I started looking at this stuff a couple of months ago, and while there was some information about Moonlight Desklets, there was very little information on how to get them working. So what we are going to do to start with in this blog is to get moonlight working from scratch, then develop our own application.