My name is Michael Hutchinson and I'm a software engineer working for Xamarin on the MonoDevelop IDE and Mono for mobile platforms. Here you can find my journal, projects and photos.

Journal

Integrating a GTK# Application with the Mac

In this follow-up to my post on turning a GTK# app into a Mac app bundle, I describe how to integrate your application with Mac-specific features such as the main menu, the dock, and file/URL events. This is based on the work I did to integrate MonoDevelop and MonoDoc with the Mac, and largely involves cherry-picking code snippets from these projects. Although it would be nice to isolate this code into a library, I don't have the time at the moment to maintain such a library myself.

MonoDevelop with Mac main menu support

GTK# is a nice toolkit, and in my opinion the best cross-platform toolkit for Mono/.NET, but there are some things that just don't have direct cross-platform analogues, such as the Mac main menu. To integrate with such features, you need to implement platform-specific code paths. MonoDevelop does this in some cases using runtime checks:

if (PropertyService.IsMac) {
    //some mac-specific behaviour
} else if (PropertyService.IsWindows)
    //some windows-specific behaviour
} else {
    //some default behaviour
}

This is great when the platform-specific code doesn't introduce any dependencies, because you have one binary that works across all platforms. However, you can't rely on checking System.PlatformID.MacOSX so we have to use some other techniques for platform detection. In MonoDevelop's PropertyService we can find code based on Mono's Managed.Windows.Forms/XplatUI that detects whether the program is running on Mac or Windows. Here are the pertinent parts, copied into a new class:

public static class PlatformDetection
{
	public readonly static bool IsWindows;
	public readonly static bool IsMac;
 
	static PlatformDetection ()
	{
		IsWindows = Path.DirectorySeparatorChar == '\\';
		IsMac = !IsWindows && IsRunningOnMac();
	}
 
	//From Managed.Windows.Forms/XplatUI
	static bool IsRunningOnMac ()
	{
		IntPtr buf = IntPtr.Zero;
		try {
			buf = System.Runtime.InteropServices.Marshal.AllocHGlobal (8192);
			// This is a hacktastic way of getting sysname from uname ()
			if (uname (buf) == 0) {
				string os = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (buf);
				if (os == "Darwin")
					return true;
			}
		} catch {
		} finally {
			if (buf != IntPtr.Zero)
				System.Runtime.InteropServices.Marshal.FreeHGlobal (buf);
		}
		return false;
	}
 
	[System.Runtime.InteropServices.DllImport ("libc")]
	static extern int uname (IntPtr buf);
}

For certain other cases in MonoDevelop, particularly ones that introduce dependencies that only exist on some platforms, we have a "platform service" extension point using Mono.Addins, where a platform-specific addins can provide a platform-specific implementation of our PlatformService abstract class, and if none is found we fall back to a default implementation. This means that just one of our binaries is platform-specific. It's also nice because a lot of the platform-specific code is in one place. But there are still many places where certain platforms just need a small behavioural tweak and the overhead of pulling something out into this abstraction layer isn't worthwhile. In such cases we use a quick runtime check instead.

A third option is do build a different version of your binary for Mac, using ifdefs. This is what I did for MonoDoc, because it was easier for a quick hack, but I'll probably go back later and change it to use runtime checks.

#if MACOS
    //some mac-specific behaviour that only gets included when you pass /define:MACOS to the C# compiler
#endif

The first platform-specific codepath we'll add is the main menu, because that's the one your users are going to notice. There is a GTK library called ige-mac-integration that provides some platform integration features for the main menu and dock, and is included in the Mono framework for Mac. If you exported the DYLD_FALLBACK_LIBRARY_PATH like I described in my post about building the app bundle, you should be able to P/Invoke it. Unfortunately it's quite limited in what it can do, so we don't use it at all in MonoDevelop, but its menu integration is effective for simpler apps like MonoDoc. In the MonoDoc source you can find a small IGE wrapper in a single file that you can copy into your app. Since P/Invokes are only resolved at runtime if they're used, you can include the code on all platforms provided you only call it on Mac .

if (PlatformDetection.IsMac) {
    //enable the global key handler for keyboard shortcuts
    IgeMainMenu.GlobalKeyHandlerEnabled = true;
 
    //Tell the IGE library to use your GTK menu as the Mac main menu
    IgeMainMenu.MenuBar = yourGtkMenuBar;
 
    //tell IGE which menu item should be used for the app menu's quit item
    IgeMainMenu.QuitMenuItem = yourQuitMenuItem;
 
    //add a new group to the app menu, and add some items to it
    var appGroup = IgeMainMenu.AddAppMenuGroup ();
    appGroup.AddMenuItem (yourAboutItem, "About FooApp...");
    appGroup.AddMenuItem (yourPrefsItem, "Preferences...");
 
    //hide the menu bar so it no longer displays within the window
    yourGtkMenuBar.Hide ();
}

The IGE library automatically converts your menu's shortcuts to use Command instead of Control, but if you explicitly check modifiers elsewhere in your code, for example in custom widgets, you may wish to make Mac-specific tweaks to behaviour. If you do need to access modifiers from key events directly, beware that the Mac GTK modifier mappings are very strange; see the MapRawKeys method on MonoDevelop's KeyBindingManager for details.

Next we'll handle the Apple Events that Mac applications in App Bundle form are expected to handle.

The Quit event is sent when the Quit command on your app's the Dock icon is used (among other things), and obviously should cause your app to quit. The Reopen event is sent when the dock is clicked, and should cause your app to show its windows if they're hidden, or if you follow the Mac document-per-window model and no document is open, optionally create a new empty document.

The strange events for developers from other OSes are the file open and URL open events that your app gets set if it's registered to handle file types or URL types. Because the Mac only expects to have a single instance of a bundle app running at once, each app handling multiple documents, Launch Services sends the files or URLs as events to the running instance of the app, or if the app is not running, starts it then sends the events. They are not ever passed to your app as command-line arguments.

MonoDevelop's MacPlatform addin has a wrapper for these events that exposes them as simple static .NET events on a static class called OSXIntegration.Framework.ApplicationEvents. Simply copy the entire contents of the MacPlatform/MacInterop directory and include it in your app, then connect handlers to the ApplicationEvents class's static events sometime before starting the GTK mainloop:

if (PlatformDetection.IsMac) {
	ApplicationEvents.Quit += delegate (object sender, ApplicationEventArgs e) {
		Application.Quit ();
		e.Handled = true;
	};
 
	ApplicationEvents.Reopen += delegate (object sender, ApplicationEventArgs e) {
		mainWindow.Deiconify ();
		mainWindow.Visible = true;
		e.Handled = true;
	};
 
	//optional, only need this if your Info.plist registers to handle urls
	ApplicationEvents.OpenUrls += delegate (object sender, ApplicationUrlEventArgs e) {
		if (e.Urls != null || e.Urls.Count > 0) {
			OpenUrls (e.Urls);
		}
		e.Handled = true;
	};
 
	//optional, only need this if your Info.plist registers to handle files
	ApplicationEvents.OpenFiles += delegate (object sender, ApplicationFileEventArgs e) {
		if (e.Files != null || e.Files.Count > 0) {
			OpenUrls (e.Files);
		}
		e.Handled = true;
	};
}

You might wonder how to register to handle files and URLs in your Info.plist. The Apple docs have the full details, but we can look at the MonoDoc Info.plist and the MonoDevelop Info.plist for some basics.

To register URLs, add the CFBundleURLTypes key to the Info.plist's main dictionary. Its value should be an array of URL types, where each one is a dictionary defining the name and the schemes (prefixes):

<key>CFBundleURLTypes</key>
<array>
	<dict>
		<key>CFBundleURLName</key>
		<string>MonoDoc</string>
		<key>CFBundleURLSchemes</key>
		<array>
			<string>monodoc</string>
		</array>
	</dict>
</array>

Notice that you're not just registering to handle the URL types, you're also defining them. Launch Services automatically scans app bundles on the system and in the user's directory to build system-wide and per-user databases of URL types and their handlers. So, as soon as your app is present, it could be used to handle the things it claims. You can even define the icon, as an icns file to be loaded from your app's Resources directory, just like the bundle's icon, and this will show up in Finder.

Registering file types is similar, but there are more keys. You can also register whether your bundle is the default editor, and whether it's an editor or viewer.

<key>CFBundleDocumentTypes</key>
<array>
	<dict>
		<key>CFBundleTypeIconFile</key>
		<string>monodevelop.sln.icns</string>
		<key>CFBundleTypeExtensions</key>
		<array>
			<string>sln</string>
		</array>
		<key>CFBundleTypeName</key>
		<string>MonoDevelop Solution</string>
		<key>CFBundleTypeRole</key>
		<string>Editor</string>
		<key>LSIsAppleDefaultForType</key>
		<true/>
	</dict>
</array>

With these examples, you should now be able to make your application fit in much better on Mac.

I'm still investigating other Mac integration points for MonoDevelop, such as native file dialogs, but that's likely to take some time, as binding the native toolkits and getting them to play nicely with the GTK mainloop is likely to be difficult, and there are other important things keeping me busy. When they're done I'll be sure to share that code too.

This is part of the Catchup 2010 series of posts.

Creating a Mac App Bundle for a GTK# Application

While making the MonoDevelop and MonoDoc packages for Mac I learned a few things about adapting GTK# apps for Mac, and I'd like to share them so that anyone else who's built a GTK# app on Windows or Linux can provide a nice self-contained Mac app bundle for their Mac users. This first part will cover building an app bundle, and a later post will cover building platform-specific code paths so that your app integrates with the main menu and dock.

If you're not a Mac developer, you might be wondering exactly what an app bundle is. Well, it's simply a directory with a ".app" extension that contains an application and everything it needs. The Mac GUI shell treats this folder as a self-contained application that can be run directly. It never has to be "installed" as such, but can simply kept wherever the user wants, typically in the system's Applications folder, and to "uninstall", it's simply deleted. To look inside an app bundle, use the context menu on the bundle in Finder.

The most important part of the app bundle is the Info.plist manifest, which lives in the Contents subdirectory of your app bundle, and describes the application. A plist ("property list") is an Apple structured data format that can be represented in binary or xml formats. We'll be using the xml format, which is fairly simple, if a little odd. Let's look at a simplified version of the MonoDoc app bundle's manifest:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>English</string>
	<key>CFBundleExecutable</key>
	<string>monodoc</string>
	<key>CFBundleIconFile</key>
	<string>monodoc.icns</string>
	<key>CFBundleIdentifier</key>
	<string>com.novell.monodoc</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>MonoDoc</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>2.2</string>
	<key>CFBundleSignature</key>
	<string>xmmd</string>
	<key>CFBundleVersion</key>
	<string>2.2</string>
	<key>NSAppleScriptEnabled</key>
	<string>NO</string>
</dict>
</plist>

As you can see, it simply contains key/value pairs to set properties of the app bundle. This is just a subset of the keys you can set, but they're the important ones for our purposes. If you need more, see the Apple docs.

The one you need to change are:

CFBundleExecutable
The name of the executable within the Contents/MacOS subdirectory of your bundle that Launch Services will run when opening your app. We'll be using a shell script to run your real executable using Mono.
CFBundleIconFile
The icon of your app bundle, shown in Finder and the Dock. It should be an icns file created using Icon Composer, and placed in the Contents/Resources subdirectory of your bundle.
CFBundleIdentifier
A namespaced unique ID for your app.
CFBundleName
The name for your app that will be shown by the dock and menu bar.
CFBundleShortVersionString
A short display string for your app bundle's version.
CFBundleVersion
Your app bundle's version.

Treat the other keys as boilerplate that must be included. Much of the app bundle layout and properties are designed for apps using the native toolkits; we are using just enough of them to fit in and work correctly.

If you read the descriptions of the keys, you'll see that your app bundle directory should have the following structure:

  • YourAppName.app
    • Contents
      • Info.plist
      • Resources
        • yourappname.icns
      • MacOS
        • yourappname
        • Your app's real executable, dll, and other files

After you've written your Info.plist, made an icon file using Icon Composer (from the Apple Developer Tools), and copied all your app's real file into the bundle's MacOS folder, all we're missing is the launch script. The Apple Launch services can run shell scripts, but doesn't know how to open Mono programs directly, so we use a shell script to start Mono. Let's look at the MonoDoc launch script:

#!/bin/sh
# Author: Michael Hutchinson (mhutchinson@novell.com)
 
DIR=$(cd "$(dirname "$0")"; pwd)
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
export DYLD_FALLBACK_LIBRARY_PATH="$DIR:$MONO_FRAMEWORK_PATH/lib:/lib:/usr/lib"
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
 
exec mono "$DIR/browser.exe"

This is very straightforward. It simply gets a full path to the bundle's MacOS directory and puts it in the DIR variable so that can be used later in the script, and sets DYLD_FALLBACK_LIBRARY_PATH to include this directory and the Mono framework directory, so that you can P/Invoke native libraries in your MacOS directory and the Mono framework lib directory. It also sets the PATH environment variable to include the official Mono framework's bin directory, ensuring that the official Mono is used. Using the official Mono is important, because it means you avoid issues specific to users who have MacPorts or some other custom Mono in their PATH, which are likely to be difficult to reproduce or fix. It then executes the app using Mono.

This is a simple script, but it has some deficiencies. We can improve it by taking some code and tricks from the MonoDevelop Mac launch script.

First, we'll detect whether the Mono Framework is installed, and if it's not, show a message using AppleScript telling the user to download it. This prevents your app from dying silently if Mono isn't installed. Put this in your script before the exec call.

#mono version check
 
REQUIRED_MAJOR=2
REQUIRED_MINOR=4
APPNAME="MonoDevelop"
 
VERSION_TITLE="Cannot launch $APPNAME"
VERSION_MSG="$APPNAME requires the Mono Framework version $REQUIRED_MAJOR.$REQUIRED_MINOR or later."
DOWNLOAD_URL="http://www.go-mono.com/mono-downloads/download.html"
 
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' |  cut -f5 -d\ )"
MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
if [ -z "$MONO_VERSION" ] \
	|| [ $MONO_VERSION_MAJOR -lt $REQUIRED_MAJOR ] \
	|| [ $MONO_VERSION_MAJOR -eq $REQUIRED_MAJOR -a $MONO_VERSION_MINOR -lt $REQUIRED_MINOR ] 
then
	osascript \
	-e "set question to display dialog \"$VERSION_MSG\" with title \"$VERSION_TITLE\" buttons {\"Cancel\", \"Download...\"} default button 2" \
	-e "if button returned of question is equal to \"Download...\" then open location \"$DOWNLOAD_URL\""
	echo "$VERSION_TITLE"
	echo "$VERSION_MSG"
	exit 1
fi

We can also take some code to use the "-a" argument to exec to set the process name that will be see by the "ps" commandline tool. Note that this doesn't work on OS 10.4, so we check the OS version and define an exec command in a variable that we can use later.

# Work around a limitation in 'exec' in older versions of macosx
OSX_VERSION=$(uname -r | cut -f1 -d.)
if [ $OSX_VERSION -lt 9 ]; then  # If OSX version is 10.4
	MONO_EXEC="exec mono"
else
	MONO_EXEC="exec -a appname mono"
fi

Finally, change the exec call from the original version to use our new MONO_EXEC variable, write all console output to a log file in a subdirectory of ~/Library/Logs, and pass the value of the MONO_OPTIONS environment variable to Mono. The MONO_OPTIONS environment variable is useful to enable you to to pass diagnostic options to the Mono runtime, such as "--debug", without altering your script.

EXE_PATH="$DIR\appname.exe"
LOG_FILE="$HOME/Library/Logs/$APPNAME/$APPNAME.log"
mkdir -p "`dirname \"$LOG_FILE\"`"
$MONO_EXEC $MONO_OPTIONS "$EXE_PATH" $* 2>&1 1> "$LOG_FILE"

Let's tidy this all up into a single script, with all the values you need to change for your specific app in one place at the top.

#!/bin/sh
 
#get the bundle's MacOS directory full path
DIR=$(cd "$(dirname "$0")"; pwd)
 
#change these values to match your app
EXE_PATH="$DIR\appname.exe"
PROCESS_NAME=appname
APPNAME="AppName"
 
#set up environment
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
export DYLD_FALLBACK_LIBRARY_PATH="$DIR:$MONO_FRAMEWORK_PATH/lib:/lib:/usr/lib"
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
 
#mono version check
REQUIRED_MAJOR=2
REQUIRED_MINOR=4
 
VERSION_TITLE="Cannot launch $APPNAME"
VERSION_MSG="$APPNAME requires the Mono Framework version $REQUIRED_MAJOR.$REQUIRED_MINOR or later."
DOWNLOAD_URL="http://www.go-mono.com/mono-downloads/download.html"
 
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' |  cut -f5 -d\ )"
MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
if [ -z "$MONO_VERSION" ] \
	|| [ $MONO_VERSION_MAJOR -lt $REQUIRED_MAJOR ] \
	|| [ $MONO_VERSION_MAJOR -eq $REQUIRED_MAJOR -a $MONO_VERSION_MINOR -lt $REQUIRED_MINOR ] 
then
	osascript \
	-e "set question to display dialog \"$VERSION_MSG\" with title \"$VERSION_TITLE\" buttons {\"Cancel\", \"Download...\"} default button 2" \
	-e "if button returned of question is equal to \"Download...\" then open location \"$DOWNLOAD_URL\""
	echo "$VERSION_TITLE"
	echo "$VERSION_MSG"
	exit 1
fi
 
#get an exec command that will work on the current OS version
OSX_VERSION=$(uname -r | cut -f1 -d.)
if [ $OSX_VERSION -lt 9 ]; then  # If OSX version is 10.4
	MONO_EXEC="exec mono"
else
	MONO_EXEC="exec -a \"$PROCESS_NAME\" mono"
fi
 
#create log file directory if it doesn't exist
LOG_FILE="$HOME/Library/Logs/$APPNAME/$APPNAME.log"
mkdir -p "`dirname \"$LOG_FILE\"`"
 
#run app using mono
$MONO_EXEC $MONO_OPTIONS "$EXE_PATH" $* 2>&1 1> "$LOG_FILE"

Now open a terminal, make the script executable, and execute it.

chmod +x AppName.app/Contents/MacOS/scriptname
./AppName.app/Contents/MacOS/scriptname

It's useful to run the script directly like this because you will immediately see the result of any errors in it. Assuming the script worked, your app bundle is complete, and you can double-click on it to run it.

In my follow-up post I cover writing platform-specific code paths to integrate your new bundle better with the platform, including using the Mac main menu, and handling Apple events, which will enable your app to handle files and URLs.

This is part of the Catchup 2010 series of posts.

Long Time No Blog

It's strange to think that's it's 2010 already and I haven't blogged since June. It certainly hasn't been for lack of things to blog about — if anything, I've been too busy working on things to blog about them, so whenever I've had something to share I've usually used Twitter because much easier to fire off a quick tweet than to write a blog post. However, I'm determined to start blogging again so that I can share detailed information to which I can refer people more easily.

So, over the next few weeks, I'm going to try to catch up a bit. I intend to blog about the following topics:

I don't have any particular order planned, so if there's something you'd particularly like to see, leave a comment and I'll try to take it into consideration. I'll link the items in the list through to the posts as I make them.

Unusual XNA Tools Licensing

In the Microsoft XNA EULA, I found the following restriction:

You may not use the software to develop programs that run on any platform other than the Windows, Zune or Xbox 360 platforms

I'm no lawyer, but this sounds like very poor wording. Does this mean that all source code developed with XNA Game Studio is restricted to use on those platforms, in perpetuity? That would mean that it couldn't even be used on a future, new Microsoft platform!

This seems to me to be unreasonable and probably unenforceable.

Moonlight Development on Linux with MonoDevelop

Yesterday I made a screencast demonstrating Moonlight Development on Mac using MonoDevelop. Today, I bring you the same thing on Linux:

Downloadable OGV version

Unfortunately, getting this running isn't quite as simple as the Mac installer. However, if you're on openSUSE/SLED, it's almost as easy, using my trunk MonoDevelop packages from the openSUSE build service (1-click install for openSUSE 11.1).

You just have to download and extract my Moonlight SDK, extract it somewhere, then tell MonoDevelop where it using the MOONLIGHT_2_SDK_PATH environment variable:

export MOONLIGHT_2_SDK_PATH=/full/path/to/MoonSDK
monodevelop

If you don't have openSUSE/SLED, you'll have to use the Mono 2.4 VMWare image or build MonoDevelop from trunk. If you build MonoDevelop from trunk, I suggest not installing it, and instead using "make run". If you don't even have Mono 2.4, you'll need to build that too, and please use a parallel environment if you don't want to risk messing your system up with hard-to-diagnose problems.

Moonlight Development on Mac using MonoDevelop

Yesterday I announced much-improved MonoDevelop preview builds for Mac, which include ASP.NET MVC support out-of-the-box. However, there was a little surprise in that build that I didn't mention — full support for building and running Silverlight projects using a bundled copy of the Moonlight SDK.

I created a screencast to show off how easy this makes it to develop Silverlight apps on the Mac:

Screencast

Note that, as I said before, this is an alpha preview release of MonoDevelop, but I feel that it's already very usable. After seeing the screencast, I'm sure you'll agree. Download it from the Mac Preview page on the MonoDevelop website.

The Moonlight support in MonoDevelop trunk is entirely functional on Linux too, but it's not so easy to get it running since you need to build and install the Moonlight stack from source, and I would strongly advise against doing this unless you're comfortable with setting up a Parallel Mono Environment. However, it's easy if you use a binary Moonlight SDK like the Mac package does, and tomorrow I'll blog about how to do this with my MonoDevelop trunk packages for openSUSE.

I wrote much of the Silverlight/Moonlight project support code in the lead-up to the 2008 PDC, but the Moonlight 2.0 toolchain and runtime wasn't complete enough to make it worthwhile for me to finish the build/run support, so I focussed on other things, and we quietly dropped the addin from the MonoDevelop 2.0 release. On Friday, with the first Moonlight 2.0 preview and Mac MonoDevelop preview looming, I decided to finish it off, and did so over that afternoon and the weekend. I spent Monday and Tuesday polishing it and making it possible to bundle the Moonlight SDK in the MonoDevelop app.

It still needs work — the XAML completion in particular is a quick hack I put together one evening during PDC — but I believe that it's already a great way to build your Silverlight apps quickly and easily!

MonoDevelop Mac Preview Builds

The past few weeks I've been working on improving the MonoDevelop experience on the Mac, making it integrate better with the Mac experience. Along with ASP.NET MVC support and other soon-to be-announced excitement, I think this makes MonoDevelop much more interesting for Mac users.

MonoDevelop with Mac main menu support

Among other things, I have:

  • Abstracted out the main menu and reimplemented it as a native Mac global menu.
  • Added handlers for Mac events, such as Quit, Dock Click, Open Files, etc.
  • Added Mac-specific command accelerators.
  • Made it possible to capture the Mac Command and Option keyboard modifiers for accelerators.
  • Made accelerators display everywhere using Mac-style glyphs.
  • Tweaked the text editor's caret and selection navigation to behave more like a Mac IDE, including Alt/Control word/subword splitting.

This has been made difficult by the native GTK+ Mac. Although the basics work very well, there are substantial problems with some of the more advanced things we do, such as key capturing for accelerators, and custom widgets. In the future we will be fixing issues upstream and shipping patched GTK+ builds with Mono, but for now I've been focussing on making everything work with the released Mono 2.4.

To do much of this, I had to build a large set of Carbon P/Invoke signatures and wrappers, and getting these right took some time. It seems to me that this could be the basis of a "Mac Bridge" in the style of the Vista Bridge. Having such managed wrappers would make it easier for developers to make their apps integrate more closely with the unique aspects of various platforms.

This work will be released in MonoDevelop 2.2. Right now it's not finished, and is very much an alpha. However, since it's already a substantial improvement for Mac users, I am making available a Mac preview build. This is a trunk build that has undergone no QA testing. I simply update it whenever I feel that trunk is usable and bugfixes or improvements have been made since the previous build. Use at your own risk. I have already listed a number of known issues.

Download it from the Mac Preview page on the MonoDevelop website.

If you do run into problems that aren't on the known issues list, please file bug reports. And, as ever, contributions are more than welcome.

MonoDevelop ASP.NET MVC on the Mac

After releasing a preview of the ASP.NET MVC MonoDevelop addin, I decided to try it on the Mac.

Although the MonoDevelop 2.0 release on the Mac is still an alpha, it's a lot better on the Mac than 1.0 was. Other than a couple of GTK+ redraw issues, the major remaining problems are mostly to do with lack of Mac integration, such as Ctrl-Click, top-level menu bar and Mac-like keyboard shortcuts. Making MonoDevelop completely supported on the Mac is one of our targets for MonoDevelop 2.2.

I downloaded the Mono 2.4 Mac installer and MonoDevelop 2.0 Mac installer (beware, alpha release) then followed exactly the same steps from my earlier Linux-based walkthrough, and apart from a couple of harmless warnings during installation of the addin, it worked flawlessly!

Running an ASP.NET MVC app in MonoDevelop on the Mac

Since none on the MD team is familiar with Macs, contributions from Mac users would be most welcome, including testing and feedback about how to make it feel more Mac-like.

Dyson, a great game using Mono

Last week, Miguel, Joseph and I went to the GDC again to promote the use of Mono as a runtime for games. I may write about that at a later time.

While we were there, we went to the Independent Games Festival Awards, and there were many interesting-looking games. One in particular that stood out to me was Dyson, because of its elegant, abstract, procedurally generated art. It's an "ambient real-time strategy" game that involves exploring and conquering asteroid belts with fleets of self-replicating seedlings.

The next day, someone talking to us at our GDC booth mentioned that Dyson runs on Linux using Mono! It uses SDL via the Tao wrappers. It doesn't run on 64-bit Linux yet; it looks like Tao.Sdl isn't 64-bit safe. Fortunately, I have a 32-bit parallel Mono environment, which isn't too hard to set up. However, setting up SDL and the dependencies for libgdiplus was nontrivial, so I don't recommend this path.

Anyhow, I got it working. Here you can see an almost-conquered asteroid belt:

The Dyson game running on Linux using Mono

And a close-up of fending off an invasion:

The Dyson game running on Linux using Mono

It's a lot of fun, and I'm looking forward to the full game's release! It's already listed on Steam with a July 31st release date, and says there that Linux is a supported platform — does that mean we'll see Steam on Linux soon?

ASP.NET MVC MonoDevelop Addin Preview

The past month or two, in addition to bugfixing for our recent MonoDevelop 2.0 release, I've been quietly hacking on a MonoDevelop Addin to support writing ASP.NET MVC applications. Those of you following me on Twitter may have picked up a few hints...

Now that Microsoft has released ASP.NET MVC under the MS-Pl licence, I'm making a preview release of the ASP.NET MVC addin for people who'd like to start playing with this exciting new tech on Mono. It's relatively immature, so don't expect everything to work perfectly, but it handles creating and running a VS-compatible ASP.NET MVC project just fine.

You'll need MonoDevelop 2.0 and Mono 2.4 or later. Make sure you have the package that contains System.Web.Abstractions/Extensions/Routing; on SUSE this is currently mono-extras. The addin includes a copy of the System.Web.Mvc.dll, so you don;t need to do anything special for that.

The addin is in the default MonoDevelop addin repositories (though I may be releasing updated versions from my own repo at http://mjhutchinson.com/mdrepo), so open MonoDevelop, click on the Tools->Add-in Manager menu, and click the Install add-ins... button. Select the ASP.NET MVC addin, and complete the installation.

Installing the ASP.NET MVC Addin

Restart MonoDevelop, as there seems to be a minor issue with reference assemblies updating dynamically.

Now you can create new ASP.NET MVC projects.

Creating a new ASP.NET MVC project

When you create a new project, you'll see a project with all the basics set up, but minimal content, so that you don't have to worry about stripping out things you don't need. If even the "Hello World" view is too much, there's also an "Empty ASP.NET MVC Application" template.

There's an "Add View" command on the context menu for the Views folder, and an "Add Controller" command on the context menu for the Controllers folder.

The Add View Menu

Similarly to the Visual Studio ASP.NET MVC tooling, you can add custom T4 templates for the "Add View" command. As part of developing this feature, I implemented an open-source T4 engine from scratch, which Miguel already blogged about. I also added some syntax highlighting, code folding, and a document outline, and T4 compilation errors are reported in the error pad so you can jump to the error location.

The Add View dialog showing a custom TT template

I'm not happy with the restrictive nature of the ASP.NET MVC T4 host, but initially I went for compatibility with MS' version. In future I'd like a way for these templates to be able to output multiple files and expose additional properties to the GUI, and for the GUI to hide settings that the templates don't support. Note also that the "Strongly Typed" model class option is disabled, as the ASP.NET MVC T4 host exposes a type object for this, whereas MD doesn't load actual types. I'm trying to figure out a way around this.

And the proof that it works with Mono and MD? Hit F5 to build and run.

Running the default Hello World view

There are many more exciting features that I'd like to add, though I have many other things on my plate right now, such as improving MD's Mac support, so contributions are very much welcome.

Pages

Subscribe to Journal