<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>micolous.id.au &#187; Coding</title>
	<atom:link href="http://micolous.id.au/archives/category/coding/feed/" rel="self" type="application/rss+xml" />
	<link>http://micolous.id.au</link>
	<description>the result of a blogging accident</description>
	<lastBuildDate>Wed, 21 Jul 2010 11:08:20 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Xbox 360 Big Button: Round 2</title>
		<link>http://micolous.id.au/archives/2010/07/18/xbox-360-big-button-round-2/</link>
		<comments>http://micolous.id.au/archives/2010/07/18/xbox-360-big-button-round-2/#comments</comments>
		<pubDate>Sun, 18 Jul 2010 08:59:08 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Toys]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=309</guid>
		<description><![CDATA[So, time for round 2 with the Big Button controllers.  I covered this stuff a bit yesturday.
I&#8217;ve since updated the driver so that it treats the directional buttons on the big button controllers as the X and Y axes.  This means that joydev.c will now detect the xbox360bb as a joystick driver, and [...]]]></description>
			<content:encoded><![CDATA[<p>So, time for round 2 with the Big Button controllers.  <a href="/archives/2010/07/18/xbox-360-big-button-ir-receiver/">I covered this stuff a bit yesturday</a>.</p>
<p>I&#8217;ve since <a href="/static/projects/bigbutton/xbox360bb-20100718.tar.bz2">updated the driver</a> so that it treats the directional buttons on the big button controllers as the X and Y axes.  This means that joydev.c will now detect the xbox360bb as a joystick driver, and so ordinary programs that use Linux&#8217;s joystick API can receive events from the controllers (and not just those that use evdev).</p>
<p>I also <a href="/static/projects/bigbutton/simon-20100718.tar.bz2">wrote a simple pygame version</a> of <em><a href="http://en.wikipedia.org/wiki/Simon_(game)">Simon</a></em>.  This works whenever you have four joystick devices attached to your computer.  You play by pressing any button on the controller.  The colours match up to how the Big Button controllers are presented in xbox360bb.  As soon as one person makes a mistake, the game ends.  The loser is reported on the console.</p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/07/18/xbox-360-big-button-round-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>portal2 2.8.1 API documentation</title>
		<link>http://micolous.id.au/archives/2010/05/01/portal2-2-8-1-api-documentation/</link>
		<comments>http://micolous.id.au/archives/2010/05/01/portal2-2-8-1-api-documentation/#comments</comments>
		<pubDate>Fri, 30 Apr 2010 20:27:13 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Lanning]]></category>
		<category><![CDATA[Projects]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=278</guid>
		<description><![CDATA[There have been some requests that I make portal2&#8217;s API documentation available online and not just available at LANs.  So I have published a copy of the portal2 API v2.8.1.  This covers the (at the time of writing) current version of portal2&#8217;s HTTP GET and XMLRPC APIs.
Unfortunately, there&#8217;s no online &#8220;simulator&#8221; available, but [...]]]></description>
			<content:encoded><![CDATA[<p>There have been some requests that I make <a href="/projects/portal2/">portal2</a>&#8217;s API documentation available online and not just available at LANs.  So I have published a copy of the <a href="/projects/portal2/portal2_api/">portal2 API v2.8.1</a>.  This covers the (at the time of writing) current version of portal2&#8217;s HTTP GET and XMLRPC APIs.</p>
<p>Unfortunately, there&#8217;s no online &#8220;simulator&#8221; available, but that should be trivial for someone to write if they&#8217;re testing their program.</p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/05/01/portal2-2-8-1-api-documentation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TrolledFS: A filesystem with fake file contents</title>
		<link>http://micolous.id.au/archives/2010/04/22/trolledfs-a-filesystem-with-fake-file-contents/</link>
		<comments>http://micolous.id.au/archives/2010/04/22/trolledfs-a-filesystem-with-fake-file-contents/#comments</comments>
		<pubDate>Wed, 21 Apr 2010 21:00:59 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=250</guid>
		<description><![CDATA[My friend [gm] had this idea for a filesystem.  It would take a real filesystem, and then whenever a user requested to open a file, it would instead give them a contents of another file from another folder, determined by it&#8217;s file extension.  For example, it would map all AVI files to a [...]]]></description>
			<content:encoded><![CDATA[<p>My friend <a href="http://gm.stackunderflow.com/blog/">[gm]</a> had this idea for a filesystem.  It would take a real filesystem, and then whenever a user requested to open a file, it would instead give them a contents of another file from another folder, determined by it&#8217;s file extension.  For example, it would map all AVI files to a single AVI file in another folder.</p>
<p>Because he didn&#8217;t implement it, and I got bored, I ended up <a href="http://micolous.id.au/static/projects/junkcode/trolledfs.py.bz2">writing &#8220;TrolledFS&#8221; as a FUSE filesystem module in Python</a>.</p>
<p>The module requests two options, <code>root</code>, which is the directory that provides the structure of the filesystem (ie: it will appear to be a copy of this folder), and <code>fakes</code>, a folder containing replacement files.  So if you requested <code>example.avi</code>, it would map it to the first file with the AVI extension in the fakes folder.</p>
<p>It is based on the Xmp.py (example) filesystem module from FUSE&#8217;s Python bindings, with all of the write calls filtered out.  Instead, filesystem will say it is a read-only filesystem.  If a file has no match in the fakes folder, the filesystem will say there was an I/O error.</p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/04/22/trolledfs-a-filesystem-with-fake-file-contents/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Smooth scrolling marquees in GTK#</title>
		<link>http://micolous.id.au/archives/2010/04/20/smooth-scrolling-marquees-in-gtksharp/</link>
		<comments>http://micolous.id.au/archives/2010/04/20/smooth-scrolling-marquees-in-gtksharp/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 20:06:28 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=244</guid>
		<description><![CDATA[Marquees aren&#8217;t normally supported in GTK#.  You have to write an implementation yourself, using the ViewPort and Label controls.  You can do that with this bit of code.  It&#8217;s a bit of a hack, you need to add a bunch of whitespace to your text to display so that the text properly [...]]]></description>
			<content:encoded><![CDATA[<p>Marquees aren&#8217;t normally supported in GTK#.  You have to write an implementation yourself, using the ViewPort and Label controls.  You can do that with this bit of code.  It&#8217;s a bit of a hack, you need to add a bunch of whitespace to your text to display so that the text properly &#8220;scrolls in&#8221; at the start, and scrolls out properly as well without graphical glitches.  This code runs nice and smooth by moving the text to the left by 3px every 33ms (so it works out to about 30fps).</p>
<p>Video demo (<a href="http://www.youtube.com/watch?v=HIO4fS-I-f4">or you can watch it on YouTube</a>):</p>
<p><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/HIO4fS-I-f4&#038;hl=en&#038;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/HIO4fS-I-f4&#038;hl=en&#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object></p>
<p>You need to put this code inside your Gtk.Window.</p>
<pre class="prettyprint">public partial class MainWindow : Gtk.Window
{
	// viewport which we use to contain the label and handle the scrolling of the marquee.
	private Viewport vpMarquee = new Viewport();

	// marquee text should have some padding before and after the text, to avoid rendering errors, and
	// to allow the text to "flow in" when it is reset after reaching the end.
	private Label lblMarquee = new Label("                                                                                                 [lblMarquee] Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec metus quam, ullamcorper eu suscipit quis, rutrum sit amet massa.                                                                                                 ");//" Nunc dapibus accumsan metus, commodo placerat urna blandit non. Nullam turpis justo, dictum quis dignissim non, vehicula vel lorem. Fusce congue purus odio, lobortis pulvinar neque. Integer dui odio, venenatis sed tincidunt in, fringilla id urna. Maecenas faucibus massa eu orci tincidunt ac vulputate nibh aliquam. Aliquam ullamcorper erat nunc. Vestibulum hendrerit adipiscing neque quis interdum.");

	public MainWindow () : base(Gtk.WindowType.Toplevel)
	{
		// normally you'd be using Stetic, so you need to call it's build-constructor.
		Build ();

		// add the control to the window.  in this example, i'm adding it to a VBox control as the last element.
		vpMarquee.BorderWidth = 0;
		vpMarquee.Add (lblMarquee);
		this.vbox1.PackEnd (vpMarquee, false, false, 0);

		// update marquee every 33ms (~30fps)
		GLib.Timeout.Add (33, new GLib.TimeoutHandler (MarqueeUpdate));

		// add other code here for other things...
	}

	bool MarqueeUpdate ()
	{
		Application.Invoke (delegate {
			// calculate the total amount of space the marquee requires.  this only works because I've manually set the size of the window,
			// otherwise you need another way to get the width of the window instead of this.
			double n = vpMarquee.Hadjustment.Upper - this.WidthRequest;
			if (n < 1)
				// safety incase we don't have an actual width calculated width
				n = 1;

			//Console.WriteLine("{0} > {1} ?", vpMarquee.Hadjustment.Value, n);
			if (vpMarquee.Hadjustment.Value >= n) {
				// we've reached the end.  reset the marquee to the 0-position.
				vpMarquee.Hadjustment.Value = 0;
			} else {
				// scroll the marquee to the left by 3px
				vpMarquee.Hadjustment.Value += 3;
			}

			// tell gtk# we want it to take into account the updated value.
			vpMarquee.Hadjustment.ChangeValue ();
			vpMarquee.Hadjustment.Change ();

			// redraw the control and it's children (the label).
			vpMarquee.ShowAll ();
		});

		// tell Timeout we want to be called again.
		return true;
	}

	// ... add more stuff here for other functionality.
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/04/20/smooth-scrolling-marquees-in-gtksharp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SDL VNC Server module (v2)</title>
		<link>http://micolous.id.au/archives/2010/02/17/sdl-vnc-server-module-v2/</link>
		<comments>http://micolous.id.au/archives/2010/02/17/sdl-vnc-server-module-v2/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 16:31:47 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=225</guid>
		<description><![CDATA[Time for a version two!  There&#8217;s a new version of the patch to SDL 1.2.14.  You need a clean source tree with this, so remove v1 of the patch before applying this one.
Changes:

Mouse cursors now work correctly.
You can now disable the &#8220;always shared&#8221; function of the VNC server and allow a client to [...]]]></description>
			<content:encoded><![CDATA[<p>Time for a version two!  There&#8217;s <a href="/static/projects/sdlvnc/SDL-video-vnc-v2.diff.bz2">a new version of the patch to SDL 1.2.14</a>.  You need a clean source tree with this, so remove v1 of the patch before applying this one.</p>
<p>Changes:</p>
<ul>
<li>Mouse cursors now work correctly.</li>
<li>You can now disable the &#8220;always shared&#8221; function of the VNC server and allow a client to take exclusive control with <code>SDL_VNC_ALWAYS_SHARED="0"</code>.</li>
<li>You can change the display number manually with <code>SDL_VNC_DISPLAY</code>.</li>
<li>8 bpp displays are now supported.</li>
<li>pygame applications work at 16, 24 and 32 bpp display depths. 8 doesn&#8217;t work properly.</li>
<li>This module now lies by default about the supported display depths reported by <code>SDL_ListModes</code>.  It defaults to 16 bpp only, in order to make pygame work properly.  You can change what it says this with <code>SDL_VNC_DEPTH</code>, or setting it to 0 to say all depths are supported.  This doesn&#8217;t limit calls to <code>SDL_SetVideoMode</code>.</li>
<li>You can tell the VNC server to ignore all client events by setting <code>SDL_VNC_VIEW_ONLY="1"</code>.</li>
</ul>
<p>There&#8217;s also a <code>README.VNC</code> included, which documents the functionality of the library and use of it&#8217;s environment variables.</p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/02/17/sdl-vnc-server-module-v2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SDL VNC Server module</title>
		<link>http://micolous.id.au/archives/2010/02/16/sdl-vnc-server-module/</link>
		<comments>http://micolous.id.au/archives/2010/02/16/sdl-vnc-server-module/#comments</comments>
		<pubDate>Mon, 15 Feb 2010 19:45:32 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=218</guid>
		<description><![CDATA[I keep writing things related to VNC, and coding in C.  It&#8217;s seriously starting to worry me.
I&#8217;ve spent the last couple of days writing a new SDL video output module, which acts as a VNC server using libvncserver.  Here are the patches against SDL 1.2.14.  To use VNC support, you need to [...]]]></description>
			<content:encoded><![CDATA[<p>I keep writing things related to VNC, and coding in C.  It&#8217;s seriously starting to worry me.</p>
<p>I&#8217;ve spent the last couple of days writing a new SDL video output module, which acts as a VNC server using <a href="http://libvncserver.sourceforge.net/">libvncserver</a>.  <a href="/static/projects/sdlvnc/SDL-video-vnc-v1.diff.bz2">Here are the patches against SDL 1.2.14</a>.  To use VNC support, you need to run <code>./configure</code> with <code>--enable-video-vnc</code>, and once built and installed, run the SDL application with the environment variable <code>SDL_VIDEODRIVER="vnc"</code>.</p>
<p>What works:</p>
<ul>
<li>16, 24 and 32-bit true-colour displays.</li>
<li>Mouse events.</li>
<li>Keyboard events.</li>
</ul>
<p>What&#8217;s left to do:</p>
<ul>
<li>Fix mouse cursors so they display correctly.</li>
<li>8-bit (paletted) displays.</li>
<li>Handle surface locking properly.</li>
<li>Reduce/eliminate tearing on frequent screen updates.</li>
<li>Run the libvncserver event loop inside SDL&#8217;s event loop system.</li>
<li>Implement a password on the VNC server and some sort of simple access control.</li>
<li>Implement a view-only mode.</li>
<li>Allow setting the display number manually.</li>
<li>pygame applications.</li>
<li>Fix screen resolution change colour issues.</li>
</ul>
<p>What will never work:</p>
<ul>
<li>OpenGL surfaces.</li>
<li>CDROMs, Joysticks and Sound over VNC.</li>
<li>YUV video output.</li>
</ul>
<p>I&#8217;ve had a lot of success with using <a href="http://www.sand-labs.org/owb/wiki">the Origyn web browser</a> (a WebKit/SDL-based web browser)  and <a href="http://www.paradroid.net/cgterm/">cgterm</a> (a SDL C64/C128 telnet client) with this output method.  Any SDL application that doesn&#8217;t use OpenGL surfaces <strong>should</strong> be able to run with this output module.</p>
<p>Some pictures:</p>
<p><a href="/static/projects/sdlvnc/sdl-video-vnc-v1-owb.png"><img src="/static/projects/sdlvnc/sdl-video-vnc-v1-owb_small.png" alt="[origyn web browser in tightvnc]" title="Origyn Web Browser (WebKit/SDL) in TightVNC"/></a> <a href="/static/projects/sdlvnc/sdl-video-vnc-v1-cgterm.png"><img src="/static/projects/sdlvnc/sdl-video-vnc-v1-cgterm_small.png" alt="[cgterm in tightvnc]" title="cgterm in TightVNC"/></a></p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/02/16/sdl-vnc-server-module/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VNC Splitter</title>
		<link>http://micolous.id.au/archives/2010/02/05/vnc-splitter/</link>
		<comments>http://micolous.id.au/archives/2010/02/05/vnc-splitter/#comments</comments>
		<pubDate>Thu, 04 Feb 2010 15:30:19 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=199</guid>
		<description><![CDATA[More VNC clients!
This time, this one will split up a single VNC server input into smaller pieces to form a 3&#215;3 grid of smaller VNC server images.  It&#8217;ll automatically resize it to 640&#215;480.  That stuff is configurable in the defines at the top of the file.  It works best when the ratio [...]]]></description>
			<content:encoded><![CDATA[<p>More VNC clients!</p>
<p>This time, this one will split up a single VNC server input into smaller pieces to form a 3&#215;3 grid of smaller VNC server images.  It&#8217;ll automatically resize it to 640&#215;480.  That stuff is configurable in the defines at the top of the file.  It works best when the ratio between the source image and the destination image is a whole number, (ie: 4&#215;4 of 640&#215;480 images and an input of 1280&#215;960 has a 2:1 ratio), because it uses a simple &#8220;nearest neighbour&#8221; scaling algorithm.</p>
<p>The idea of this client is that you take some input into this, run a whole bunch of thin clients or low end machines connecting to each of the pieces of the screen, and align some cheap CRT monitors in a grid, creating a much bigger screen.  While there are pretty huge bezels with CRT monitors, it is a very low budget way to create a 107cm (42&#8243;) display from a 3&#215;3 array of cheap 36cm (14&#8243;) monitors, for example.</p>
<p>Be aware that some VNC servers implement JPEG compression, which looks really horrible when you scale it up (for example, Vine).  This program also requires that your VNC server has no password.</p>
<pre class="prettyprint">// vnc-splitter
// Copyright 2010 Michael Farrell &lt;http://micolous.id.au&gt;
// build with: gcc -o vnc-splitter vnc-splitter.c -lvncserver -lvncclient
#include &lt;stdio.h&gt;
#include &lt;rfb/rfb.h&gt;
#include &lt;rfb/rfbclient.h&gt;

// the height and width of the screens
// this should match the aspect ratio of the input
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

// the amount of screens is this number squared
#define SCREEN_SQUARE_SIZE 3

// where to start displays counting from.
// the first screen number is this, and subsequent screens are other positions.
#define DISPLAY_START 30

// output buffers for child vnc server processes
rfbScreenInfoPtr output_screens[SCREEN_SQUARE_SIZE * SCREEN_SQUARE_SIZE];

static rfbBool resizeImage(rfbClient* client)
{
	int width=client-&gt;width;
	int height=client-&gt;height;
	int depth=client-&gt;format.bitsPerPixel;
	fprintf(stderr, "ResizeImage fired: %ix%i %ibpp\n", width, height, depth);

	// change the screen resolution
	// deallocate any existing framebuffer
	if (client-&gt;frameBuffer != NULL)
		free (client-&gt;frameBuffer);

	client->frameBuffer = malloc(width * height * (depth / 8));

	if (client->frameBuffer == NULL) {
		fprintf(stderr, "Cannot allocate memory for framebuffer!");
		exit(1);
	}

	// we also need to tell libvncclient how it should put pixel data in there
	client->format.redShift   = 8;
	client->format.greenShift = 16;
	client->format.blueShift  = 24;
	client->format.redMax     = 0xFF;
	client->format.greenMax   = 0xFF;
	client->format.blueMax    = 0xFF;

	// now we set the format and encoding method information back to libvncclient so it updates it's information.
	SetFormatAndEncodings(client);

	// report success to libvncclient
	return TRUE;
}

static void updateImage(rfbClient* client,int u_x,int u_y,int u_w,int u_h)
{
	unsigned int x, y, src_x, src_y;
	unsigned int *dest;
	unsigned int *src;

	unsigned int max_x = SCREEN_SQUARE_SIZE * SCREEN_WIDTH;
	unsigned int max_y = SCREEN_SQUARE_SIZE * SCREEN_HEIGHT;

	// precalculate the region we actually need to update.
	unsigned int u_min_x = ((double)(u_x) / (double)(client->width)) * max_x;
	unsigned int u_min_y = ((double)(u_y) / (double)(client->height)) * max_y;

	unsigned int u_max_x = ((double)(u_x + u_w) / (double)(client->width)) * max_x;
	unsigned int u_max_y = ((double)(u_y + u_h) / (double)(client->height)) * max_y;

	unsigned char updated[SCREEN_SQUARE_SIZE * SCREEN_SQUARE_SIZE];
	memset(updated, 0, SCREEN_SQUARE_SIZE * SCREEN_SQUARE_SIZE);

	// This scaling algorithm really sucks.  It's a "nearest neighbour" scaling format.
	// It's really easy to implement, really fast, but looks absolutely terrible.
	// Try and end up with the image being blown up so that 1x1 on the original maps to 2x2
	// on the destination.  If it maps to a fraction of pixels things look really bad.
	for (y=u_min_y; y&lt;u_max_y; y++) {
		for (x=u_min_x; x&lt;u_max_x; x++) {
			// now find out where that maps to on the original framebuffer.
			src_x = (unsigned int)(((double)x / (double)max_x) * (double)client->width);
			src_y = (unsigned int)(((double)y / (double)max_y) * (double)client->height);

			// figure out where this pixel is
			unsigned int d = ((y / SCREEN_HEIGHT) * SCREEN_SQUARE_SIZE) + (x / SCREEN_WIDTH);

			// now create a pointer to where that pixel is stored
			dest = (unsigned int*)&#038;output_screens[d]->frameBuffer[(((y % SCREEN_HEIGHT) * SCREEN_WIDTH) + (x % SCREEN_WIDTH)) * 4];

			// and fix a pointer to that
			src = (unsigned int*)&#038;client->frameBuffer[((src_y * client->width) + src_x) * 4];

			// and copy the pixel
			*dest = *src;

			// mark that screen as updated
			updated[d] = 1;
		}
	}

	for (x=0; x&lt;SCREEN_SQUARE_SIZE * SCREEN_SQUARE_SIZE; x++)
		if (updated[x])
			// mark the entire screen as updated for now.  this could really
			// use some proper optimisations.
			rfbMarkRectAsModified(output_screens[x], 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
}

int main (int argc, char *argv[])
{
	fprintf(stderr, "vnc-splitter\n");
	fprintf(stderr, "Copyright 2010 Michael Farrell &lt;http://micolous.id.au>\n");
	// create a 32-bpp "client".
	rfbClient* client = rfbGetClient(8,3,4);

	// tell the library we can handle being resized.
	client->canHandleNewFBSize = TRUE;
	client->MallocFrameBuffer = resizeImage;

	// handle framebuffer updates
	client->GotFrameBufferUpdate = updateImage;

	// we don't care about keyboard LEDs and tightvnc chat protocol.
	client->HandleKeyboardLedState = 0;
	client->HandleTextChat = 0;

	// make a blank cursor
	rfbCursorPtr cursor = rfbMakeXCursor(0, 0, "\0", "\0");

	// create the vnc servers
	unsigned int id;

	for (id=0; id&lt;SCREEN_SQUARE_SIZE * SCREEN_SQUARE_SIZE; id++) {
		output_screens[id] = rfbGetScreen(0, NULL, SCREEN_WIDTH, SCREEN_HEIGHT, 8, 3, 4);
		output_screens[id]->frameBuffer = malloc(SCREEN_WIDTH * SCREEN_HEIGHT * 4);

		output_screens[id]->serverFormat.redShift   = 8;
		output_screens[id]->serverFormat.greenShift = 16;
		output_screens[id]->serverFormat.blueShift  = 24;
		output_screens[id]->serverFormat.redMax     = 0xFF;
		output_screens[id]->serverFormat.greenMax   = 0xFF;
		output_screens[id]->serverFormat.blueMax    = 0xFF;

		output_screens[id]->autoPort = FALSE;
		output_screens[id]->port = SERVER_PORT_OFFSET + DISPLAY_START + id;
		output_screens[id]->alwaysShared = TRUE;
		rfbSetCursor(output_screens[id], cursor);

		// create the server in another thread
		rfbInitServer(output_screens[id]);
		rfbRunEventLoop(output_screens[id],-1,TRUE);
	}

	// connect to the server
	fprintf(stderr, "Connecting to server...\n");

	// rfbInitClient also allows us to use vncviewer-style commandline options.
	if(!rfbInitClient(client,&#038;argc,argv))
		return 1;

	// start pumping the loop.
	fprintf(stderr, "Pumping...\n");

	clock_t last_frame = clock();
	clock_t this_frame = last_frame;
	do {
		int i = WaitForMessage(client, 100);

		if (i &lt; 0)
			// there was an issue getting the message, probably our socket was closed.
			return 0;

		if (i)
			// handle message
			if (!HandleRFBServerMessage(client))
				// error handling message, die.
				return 0;
	} while (1);
	return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/02/05/vnc-splitter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A pipe-based VNC viewer</title>
		<link>http://micolous.id.au/archives/2010/02/02/a-pipe-based-vnc-viewer/</link>
		<comments>http://micolous.id.au/archives/2010/02/02/a-pipe-based-vnc-viewer/#comments</comments>
		<pubDate>Tue, 02 Feb 2010 13:34:55 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=171</guid>
		<description><![CDATA[I&#8217;ve written a simple pipe-based VNC viewer, that is, it outputs a 32 bits-per-pixel RAW ARGB image stream to `stdout` every tenth of a second.  The effect is you can chain this with ffmpeg to create a multicast UDP video stream:
./vnc-fifo vncserver:0 &#124; ffmpeg -f rawvideo -pix_fmt argb -s 1024x768 -i -   [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve written a simple pipe-based VNC viewer, that is, it outputs a 32 bits-per-pixel RAW ARGB image stream to `stdout` every tenth of a second.  The effect is you can chain this with ffmpeg to create a multicast UDP video stream:</p>
<p><code>./vnc-fifo vncserver:0 | ffmpeg -f rawvideo -pix_fmt argb -s 1024x768 -i -   -vsync 1 -s 512x384 -f mpegts -vcodec mpeg4 -b 1000k -threads 2 udp://239.255.2.3:1234</code></p>
<p>In this example, I assume your VNC server is running on `vncserver:0`, and it is outputting 1024&#215;768.  Other output resolutions will result in corruption, so you&#8217;ll need to change the parameter appropriately.  It&#8217;ll also resize the image to 512&#215;384.</p>
<pre lang="c" line="1" escaped="true">// vnc-fifo.c
#include &lt;stdio.h&gt;
#include &lt;rfb/rfbclient.h&gt;
#include &lt;time.h&gt;

static rfbBool resizeImage(rfbClient* client)
{
	int width=client->width;
	int height=client->height;
	int depth=client->format.bitsPerPixel;
	fprintf(stderr, "ResizeImage fired: %ix%i %ibpp\n", width, height, depth);

	// change the screen resolution
	// deallocate any existing framebuffer
	if (client->frameBuffer != NULL)
		free (client->frameBuffer);

	client->frameBuffer = malloc(width * height * (depth / 8));

	if (client->frameBuffer == NULL) {
		fprintf(stderr, "Cannot allocate memory for framebuffer!");
		exit(1);
	}

	// we also need to tell libvncclient how it should put pixel data in there
	// in a format that ffmpeg will be pleased with (32bpp ARGB)
	client->format.redShift   = 8;
	client->format.greenShift = 16;
	client->format.blueShift  = 24;
	client->format.redMax     = 0xFF;
	client->format.greenMax   = 0xFF;
	client->format.blueMax    = 0xFF;

	// now we set the format and encoding method information back to libvncclient so it updates it's information.
	SetFormatAndEncodings(client);

	// report success to libvncclient
	return TRUE;
}

int main (int argc, char *argv[])
{
	fprintf(stderr, "vnc-fifo\n");
	// create a 32-bpp "client".
	rfbClient* client = rfbGetClient(8,3,4);

	// tell the library we can handle being resized.
	client->canHandleNewFBSize = TRUE;
	client->MallocFrameBuffer = resizeImage;

	// we don't care about keyboard LEDs and tightvnc chat protocol.
	client->HandleKeyboardLedState = 0;
	client->HandleTextChat = 0;

	// connect to the server
	fprintf(stderr, "Connecting to server...\n");

	// rfbInitClient also allows us to use vncviewer-style commandline options.
	if(!rfbInitClient(client,&#038;argc,argv))
		return 1;

	// start pumping the loop.
	fprintf(stderr, "Pumping...\n");

	clock_t last_frame = clock();
	clock_t this_frame = last_frame;
	do {
		int i = WaitForMessage(client, 10);

		if (i &lt; 0)
			// there was an issue getting the message, probably our socket was closed.
			return 0;

		if (i)
			// handle message
			if (!HandleRFBServerMessage(client))
				// error handling message, die.
				return 0;

		// pump out a frame only if it has been 1/10 sec since the last one.
		this_frame = clock();
		if (this_frame - last_frame >= (CLOCKS_PER_SEC / 100)) {
			// pump out a frame
			last_frame = this_frame;
			fwrite(client->frameBuffer, 1, client->width * client->height * (client->format.bitsPerPixel/8), stdout);
			fflush(stdout);
		}

	} while (1);
	return 0;
}</pre>
<p>There is an issue with using ffmpeg in this fashion, because the framerate is variable, you essentially lie in your MPEG transport stream by saying you have a much higher framerate.  The effect in ffplay is that it&#8217;ll buffer 10-45 seconds of video, play it all back to the end very fast, empty it&#8217;s buffer, then play normally.  It&#8217;ll be &#8220;hanging on your every packet&#8221; for more video data.</p>
<p>Unfortunately, I couldn&#8217;t get VLC to act in the same fashion (as a reciever), even after telling it not to drop or skip frames, playback was jerky.</p>
<p>Oh well, an experiment to see if I could get out of writing a VLC or ffmpeg source module for VNC.  Looks like if I want to go further with this, there&#8217;ll need to be a lot of changes to the code.</p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/02/02/a-pipe-based-vnc-viewer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VNC Client as Screen Saver</title>
		<link>http://micolous.id.au/archives/2010/01/24/vnc-client-as-screen-saver/</link>
		<comments>http://micolous.id.au/archives/2010/01/24/vnc-client-as-screen-saver/#comments</comments>
		<pubDate>Sat, 23 Jan 2010 21:06:51 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=169</guid>
		<description><![CDATA[I&#8217;ve whipped up a VNC Screen Saver in .NET for Windows.  It is based on this screensaver example and VncSharp.  It&#8217;s a bit of a quick hack, but works.
To get it, download the binaries and extract it to your Windows system32 folder (typically `C:\Windows\System32\`).  Then open the screensaver settings, select `Vncscreensaver` and [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve whipped up a VNC Screen Saver in .NET for Windows.  It is based on <a href="http://www.codeproject.com/KB/cs/scrframework.aspx">this screensaver example</a> and <a href="http://cdot.senecac.on.ca/projects/vncsharp/">VncSharp</a>.  It&#8217;s a bit of a quick hack, but works.</p>
<p>To get it, <a href="/static/projects/vncscreensaver/vncscreensaver-1.0-bin.7z">download the binaries</a> and extract it to your Windows system32 folder (typically `C:\Windows\System32\`).  Then open the screensaver settings, select `Vncscreensaver` and then configure it with your VNC server details.  You can then use the screen saver!</p>
<p>Make sure the server you&#8217;re connecting to doesn&#8217;t require a password, otherwise this won&#8217;t work.  The client runs in &#8220;view only&#8221; mode.  It will quit when you either click the mouse or press a key, because hooking mouse movement seems to cause problems with the VNC client when it tries to reconnect.</p>
<p>If you&#8217;re interested, there&#8217;s also <a href="/static/projects/vncscreensaver/vncscreensaver-1.0-src.7z">source code available</a> for the program, licensed under the GPLv2 (the same license as VncSharp).</p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/01/24/vnc-client-as-screen-saver/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux iSCSI COW Images, and Windows integration.</title>
		<link>http://micolous.id.au/archives/2010/01/23/linux-iscsi-cow-images-and-windows-integration/</link>
		<comments>http://micolous.id.au/archives/2010/01/23/linux-iscsi-cow-images-and-windows-integration/#comments</comments>
		<pubDate>Sat, 23 Jan 2010 10:38:32 +0000</pubDate>
		<dc:creator>micolous</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Computers]]></category>
		<category><![CDATA[Lanning]]></category>

		<guid isPermaLink="false">http://micolous.id.au/?p=164</guid>
		<description><![CDATA[More stuff for RetroLAN PCs, yay!
I&#8217;ve now got all the RetroLAN PCs running out of a copy-on-write image.  This means I now have a single 4GB &#8220;base&#8221; image with an installation of Windows XP, instead of having 10 of them.  It also means it is very trivial to reset the machine&#8217;s disk images, [...]]]></description>
			<content:encoded><![CDATA[<p>More stuff for RetroLAN PCs, yay!</p>
<p>I&#8217;ve now got all the RetroLAN PCs running out of a copy-on-write image.  This means I now have a single 4GB &#8220;base&#8221; image with an installation of Windows XP, instead of having 10 of them.  It also means it is very trivial to reset the machine&#8217;s disk images, which I achieved with a bit of scripting. </p>
<p>First up, I needed to setup the copy-on-write images through `device-mapper`.  This program is designed to let you setup software RAID, but it also allows snapshotting disks to provide a backup of a device at a point in time.  Unfortunately, `dmsetup` doesn&#8217;t support accessing file images, only actual devices, so I need to create some loopback devices first.</p>
<pre lang="bash" line="1"># setup loop0 as the "master" image, and mark it as read-only so no changes ever get written back.
losetup -r /dev/loop0 /store/master.img
# we need to get the size of the image in sectors for use later on.
cow_size=`blockdev --getsize /dev/loop0`</pre>
<p>The next step is to create a blank image file and loopback device to commit changes to.  For my setup, I created 200MB images.  Be aware of things like disk-based caches and automatic defragmentation will use up the allocated space very quickly, and when you run out of space in this file further writes will not be allowed, and Windows will soon crash with a blue screen of death.  (More on dealing with this in a bit)</p>
<pre lang="bash" line="1"># remove any existing COW image
rm /store/pc-1.cow

# create a new 200MB image (it's actually comes out 5% more than stated, but it allows for overheads in the filesystem)
dd if=/dev/zero of=/store/pc-1.cow bs=1M count=200

# now hook it to a loopback device
losetup /dev/loop1 /store/pc-1.cow</pre>
<p>Now we have two devices, `/dev/loop0` which contains the master image, and `/dev/loop1` which will contain any changes made to the disk.  The next step is to use dmsetup to create a `device mapper` which will layer the COW image ontop of the original image.</p>
<pre lang="bash" line="1"># create the cow!
echo "0 ${cow_size} snapshot /dev/loop0 /dev/loop1 p 64" | dmsetup create pc1</pre>
<p>This will create a device called `/dev/mapper/pc1`.  You can use it in your iSCSI setup with something like this in your `/etc/ietd.conf`:</p>
<pre lang="txt" line="1">Target iqn.2010-01.lan.someplace.iscsihost:pc1
        Lun 0 Path=/dev/mapper/pc1,Type=fileio</pre>
<p>You can then start `ietd`.</p>
<p>Now the next part is writing a script to let you reset the COW images.  `ietd` does not like you changing things while it is running, so you&#8217;ll need to make sure your script suspends all disk activity before blanking out the COW image.  Something like this:</p>
<pre lang="bash" line="1"># disconnect the COW image #1
ietadm --op delete --tid 1 --lun 0

# suspend dm activity
dmsetup suspend pc1

# clear the cow cache
dd if=/dev/zero of=/dev/loop1

# reload the cow table
master="/dev/loop0"
cow_size=`blockdev --getsize ${master}`
echo "0 ${cow_size} snapshot ${master} /dev/loop1 p 64" | dmsetup reload pc1

# resume dm activity
dmsetup resume pc1

# reconnect the COW image #1
ietadm --op new --tid 1 --lun 0 --params Path=/dev/mapper/pc1</pre>
<p>You could change this script around so that it allows you to pass in a PC number as a command-line argument, and then hook in a CGI script that will call it to reset it remotely.  In my setup, I&#8217;m using gPXE to boot from iSCSI, but it can also be used to access a URL via HTTP.  So your CGI script reads in the request IP address, and if it is one of the resettable machines, it will reset it&#8217;s COW image.  Then it sends back a gPXE script instructing it to run a DOS .com file that will reboot the computer.  For this to work you need to have your CGI script execute the command as the superuser (root).</p>
<p>One of the things about the COWs is if you reset them or they become full while Windows is running, Windows will very soon crash, and refuse to boot up again.  To better inform both yourself and users about the usage levels, you can read the information from `dmsetup`:</p>
<pre lang="bash" line="1"># dmsetup status pc8
0 8385930 snapshot 55296/409600</pre>
<p>In this example, it is indicating that the full image is 8385930 sectors, and 55296 of 409600 sectors are used in the COW image.  So I wrote a Python CGI script that reports this in a parsable fashion to clients:</p>
<pre lang="python" line="1" escaped="true">#!/usr/bin/env python

print "Content-Type: text/plain\r\n\r"

from os import environ
from subprocess import Popen, PIPE

ip = environ['REMOTE_ADDR']
d = int(ip.split(".")[3])
if d &gt;= 41 and d &lt;= 54:
  machine = d - 40
  try:
    p = Popen(('/sbin/dmsetup', 'status', 'retrocow%d' % machine), stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate()
    use, total = stdout.split(' ')[3].split('/')
    use, total = long(use), long(total)
    print "OK %d %d" % (use, total)
  except:
    print "ERR Problem getting parsing information"
else:
  # return error
  print "ERR Not a RetroLAN Machine"
</pre>
<p>For me, the RetroLAN machines have IPs ending in 41 to 54.  That corresponds to their PC number (retro-01 to retro-14).  This script outputs the following text file when requested via CGI if successful:</p>
<pre>OK 55296 409600</pre>
<p>This isn&#8217;t very usable for the end user, so I wrote a .NET 2.0 app called `CowBell` that runs in the system tray showing the percentage of COW usage, and notifies them if they&#8217;re over 75% usage (which means they will crash soon).  I&#8217;ve made <a href="/static/projects/cowbell/cowbell-1.0-src.7z">source code available for download</a>, which you&#8217;ll need to modify `frmMain.cs` line 37 to include the path to where you put the CGI script, and uncomment the line otherwise it <strong>will not compile</strong>.  If you make modifications to this program I do ask that you share your improvements with me.</p>
<p>This is the message that constantly pops up when you&#8217;re over 75% usage:</p>
<p><img src="http://micolous.id.au/static/projects/cowbell/cowbell.png" alt="" /></p>
<p>The application refreshes it&#8217;s status every 10 seconds, and if you right-click the icon it shows the COW usage in sectors.</p>
]]></content:encoded>
			<wfw:commentRss>http://micolous.id.au/archives/2010/01/23/linux-iscsi-cow-images-and-windows-integration/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
