tag:blogger.com,1999:blog-81120832024-03-07T18:05:42.864-05:00inferno programmer's notebooka lab notebook describing experiments conducted in the inferno programming environment.caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.comBlogger133125tag:blogger.com,1999:blog-8112083.post-33020576353634814912022-12-30T12:26:00.000-05:002022-12-30T12:26:32.831-05:00The Cartesian Theater of AI<div id="post">
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh12MKy-g8AFA4AgRQlBIH541aLt1wOaAfRkDYFTslh3Z8eWOEnq6KBjqJLYhrmixwzHYSroJKyeAF3a85VLaRfZYxKw1yqFFanKmAaojOq_ubuR4ciMz5_4GqtCkDT4NiMfsYpOvFVhRRlWNhWpwCfmK4TgbBrJI8329j8KDBilBVBXSNtvN0/s600/cartesian-theater.jpeg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="320" data-original-height="514" data-original-width="600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh12MKy-g8AFA4AgRQlBIH541aLt1wOaAfRkDYFTslh3Z8eWOEnq6KBjqJLYhrmixwzHYSroJKyeAF3a85VLaRfZYxKw1yqFFanKmAaojOq_ubuR4ciMz5_4GqtCkDT4NiMfsYpOvFVhRRlWNhWpwCfmK4TgbBrJI8329j8KDBilBVBXSNtvN0/s320/cartesian-theater.jpeg"/></a></div>
The Cartesian Theater was an intuition pump proposed by philosopher Daniel Dennett to describe a false theory of consciousness. Inside the mind is a stage watched by a humunculus that observes what passes on the stage, hears the sounds, and controls the rest of the mind by responding to the action of what the smaller version of the self sees. Of course, what makes the smaller self conscious? It is an infinite regression. Our understanding of mind is that there is no such theater. There is no one place in the mind that is the locality of consciousness.
<p>
I want to offer another thought experiment based on the concept of a Cartesian Theater.
<p>
Imagine a theater containing a screen with a camera pointing at it, and a speaker with a microphone directed toward the speaker. The camera is pointing at the screen. The mic is pointing at the speaker.
<p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGToDlrzgQKzzwtZI4XnBBzrBTu5N_dd2LSQmowcJEptZUHr7MzmyC65mGABxS64A4KlnR0uv_1ibUPT3No7X7CngE1mlRc2HOP3xa6usHHLO5W7RpSV03re59pM6NGKMT_w5F55Shrt9SJmXeutJ0qmIlsqbeXdWqJfmjat8imVuG6ZZmzfE/s1200/Infinite-Loop.jpg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="320" data-original-height="630" data-original-width="1200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGToDlrzgQKzzwtZI4XnBBzrBTu5N_dd2LSQmowcJEptZUHr7MzmyC65mGABxS64A4KlnR0uv_1ibUPT3No7X7CngE1mlRc2HOP3xa6usHHLO5W7RpSV03re59pM6NGKMT_w5F55Shrt9SJmXeutJ0qmIlsqbeXdWqJfmjat8imVuG6ZZmzfE/s320/Infinite-Loop.jpg"/></a></div>
<p>
However, instead of there being an infinite loop due to a direct connection between the camera and screen, insert an AI in the loop.
<p>
The images captured from the camera are sent to a neural network that generates speech, which is sent to the speaker. The mic sends the audio to a neural network that generates images on the screen.
<p>
You, as a kind of homunculus, can sit in the theater of this large “brain” and observe the realism of the images and sounds, like observing another’s consciousness.
<p>
If the homunculus is removed, are the images and sounds still real? Are they still interacting with each other inside the “brain”?
<p>
If you abstract away the theater and only information is passed between the neural networks, is the realism of the images and sounds still present?
<p>
How much realism of physical qualities like color or sound is carried within channels of information flow?
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-44213042907561289942022-02-05T13:40:00.000-05:002022-02-13T11:50:06.929-05:00Training AI with culture<div id="post">
<div style="text-align: left;"><h3 style="text-align: left;">How is culture relevant to the training of artificial intelligence?</h3><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgVCIlZg-kG2IiUvaShF_Uog1ffJPKeKxjvBtcAMmA6FReAXw8cpVPNlJHLI50YtL2q47AZDh9jMvtrzNYhC293ofcHPLFeqSE0K-RtH1c43-gbQ47aaC7II3mivs6-6S1V5HQHtgvAreb-s4utDN0k4FYwNL7jSdCAZmSSefAsIh3vtGj0HQA=s640" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="419" data-original-width="640" height="323" src="https://blogger.googleusercontent.com/img/a/AVvXsEgVCIlZg-kG2IiUvaShF_Uog1ffJPKeKxjvBtcAMmA6FReAXw8cpVPNlJHLI50YtL2q47AZDh9jMvtrzNYhC293ofcHPLFeqSE0K-RtH1c43-gbQ47aaC7II3mivs6-6S1V5HQHtgvAreb-s4utDN0k4FYwNL7jSdCAZmSSefAsIh3vtGj0HQA=w492-h323" width="492" /></a></div><br /><div><br /></div><div>A guess about the origin of human level intelligence is that it was mutually arising with complex society and culture, that is, with the origin and evolution of memes (Dawkins). Culture is composed of memes which are replicated by the two step process of being (1) recreated within a mind and then (2) enacted as some observable trait; creativity being necessary to reverse engineer the latent content of a meme, its meaning, in or order to accurately output the observable trait (Deutsch).</div><div>A human using creativity serves memes as a kind of “meme machine” by making high fidelity copies of cultural artifacts or traits such as manners, rituals, totems, and tools (Blackmore).</div><div><br /></div><div>The complexity of cultural artifacts has grown exponentially from stone tools to space telescopes. Culture is a deep library of memes carrying objective knowledge. That knowledge contains within it truth about the world, while also being riddled with errors (Popper). Knowledge is information with causal power so that what truth it contains causes itself to survive in its environment.</div><div><br /></div><div>Humans good at copying cultural artifacts must have risen in stature in their society. This would have put further evolutionary pressure on creativity even though it was hardly ever used, until within the last 300 years, to actually innovate. For most of human history there was no observable improvement within a human’s lifetime even though they were intelligent, creative, had language and culture. Humans copied existing artifacts, as faithfully as they could, to earn the prestige of society (Deutsch). These incentives drove both the acquisition of intelligence but also the disabling of it for anything but replication of the cultural meme pool. Only until the evolution of rational memes, which among others included the idea to actually innovate, did a jump in meme complexity occur, and objective knowledge grew exponentially.</div><div><br /></div><div>Given the above, intelligence is the efficiency of recreating the latent content of previously unknown memes and enacting their cultural traits. This loosely follows Chollet’s definition:</div><div><br /></div><div>Intelligence is the efficiency with which a learning system turns experience and priors into skill at previously unknown tasks. (Chollet)</div><div><br /></div><div>Another way to put this is that an intelligent agent can identify and select a previously unknown cultural artifact, guess the latent content that was used to create it, and output a functional replica of that artifact.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjZG5LBS00mILqIjaz7paSSzkTlvcugBAtOjBSRZIXV713YKfvEJcOj8QH_A-jVlU5GVMjX04AP-nIq5QNIRxTTUQyzxffWPZANBW0rzVh3AEyZVCatcyryZre9edeVxn7PVUmDz17u9CYegjKUN_LmCRe_4-wjhBEwUAian4mfqlv_EbyFEWs=s640" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="427" data-original-width="640" height="281" src="https://blogger.googleusercontent.com/img/a/AVvXsEjZG5LBS00mILqIjaz7paSSzkTlvcugBAtOjBSRZIXV713YKfvEJcOj8QH_A-jVlU5GVMjX04AP-nIq5QNIRxTTUQyzxffWPZANBW0rzVh3AEyZVCatcyryZre9edeVxn7PVUmDz17u9CYegjKUN_LmCRe_4-wjhBEwUAian4mfqlv_EbyFEWs=w420-h281" width="420" /></a></div><br /><div style="text-align: center;">What is the latent content of this cultural artifact?</div><div><br /></div><div>Culture serves up a deep library of novel artifacts as a training set, and the agent, observing an artifact, guesses its meaning, and in the appropriate context outputs a suitable replica. In that way the agent builds a library of skills, from which it can tackle tougher, more complex artifacts.</div><div><br /></div><div>A key difficulty with programming intelligence has been that there is little we can specify about what it is that must be created. We can’t specify the knowledge that is to be created without giving away the answer (Deutsch, 2012). Once we have the answer, then applying it turns into skill. Intelligence is fundamentally about closing a knowledge gap, whereas skill is about closing a performance gap.</div><div><br /></div><div>High skill is not the same as high intelligence. Yet intelligence builds on skill to perceive and manipulate the world.</div><div><br /></div><div>In order to explain and program intelligence, we must be explicit about where the knowledge exists and the role it has. Intelligence helps complete a task where the agent doesn’t yet have some necessary knowledge when it begins. It is presented an artifact that is has not seen before and which contains some latent knowledge it does not yet have.</div><div><br /></div><div>A key property of the artifact, if we are to observe intelligence at work, is that there must exist knowledge critical to forming or using the artifact that cannot be read from the artifact itself.</div><div><br /></div><div>Most cultural artifacts or traits contain objective knowledge. The artifact can be thought of as the output of some program. The logical depth of an artifact can be measured by the shortest program that outputs the artifact given random inputs (Bennett). More challenging artifacts for an intelligent agent will tend to have greater logical depth.</div><div><br /></div><div>An agent given an artifact needs to guess how to replicate it. That is, it needs to guess a suitable program that would output the artifact. The program is not readable from the artifact itself. It needs to be created from whatever prior knowledge the agent has and from its observations of the artifact. The agent can safely assume the knowledge must exist to output the artifact since the artifact is an existence proof of the program that output it.</div><div><br /></div><div>The path to training an AI with fluid intelligence must include culture, a deep library of artifacts that contain latent knowledge, that the agent must learn to replicate. Or to turn it around, we might look at the training set for intelligent agents as the emerging and evolving culture for an AI.</div><div><br /></div><div>That culture would need to be vast, with varying levels of difficulty to support the needs of a low skill learner starting with basics and progressing to advanced material. Because of the difficulty in creating such an edifice, it would be our culture that the AI learns to be intelligent by. Because our culture includes ideas such as innovation and a complex of other rational memes, our culture would be sufficient for an AI to learn to be truly creative and original.</div><div><br /></div><h3 style="text-align: left;">REFERENCES</h3><div>Bennett, Charles, Logical Depth and Physical Complexity</div><div><br /></div><div>Blackmore, Susan, Meme Machine</div><div><br /></div><div>Chollet, Francois 2019, On the Measure of Intelligence</div><div><br /></div><div>Dawkins, Richard, The Selfish Gene</div><div><br /></div><div>Deutsch, David, The Beginning of Infinity</div><div><br /></div><div>Deutsch, David, 2012, How close are to we creating artificial intelligence, https://aeon.co/essays/how-close-are-we-to-creating-artificial-intelligence</div><div><br /></div><div>Legg, Hutter 2007, Universal Intelligence: A Definition of Machine Intelligence</div><div><br /></div><div>Popper, Karl, Objective Knowledge</div></div><p>
</p><p></p><p></p></div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-16535665767010416082012-12-27T13:51:00.000-05:002012-12-27T15:01:03.374-05:00lab 111 - wavloop<div id="post"><p><a href="http://www.sonicspot.com/guide/wavefiles.html">WAV file format</a> contains audio sample data and optionally meta-data that describe the offsets of sample loops and cue points. The loop offsets are used by sampler software to generate a continuous sound, and the cue points mark the point in the sample data where the sound fades away after the note has been released.<br />
<p>A WAV file "smpl" chunk will identify the <strike>byte</strike> sample offset of the start and end of the loop in the sound data. Using wavplay.b as a starting point I tried to loop a sampled sound. <br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZF9P1yCQj4Isj3geDFn91cYHys7nEbJb5vpsHMsXmypQn5jTIeHIR2DANNZL-bxUgoIj0El8sc6Obm2NY721ULu9aY-BMtOh2irktqym9eQczc9unESxyHu5lG8Zv8qjh9Y5UtA/s1600/sinwave.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="320" width="317" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZF9P1yCQj4Isj3geDFn91cYHys7nEbJb5vpsHMsXmypQn5jTIeHIR2DANNZL-bxUgoIj0El8sc6Obm2NY721ULu9aY-BMtOh2irktqym9eQczc9unESxyHu5lG8Zv8qjh9Y5UtA/s320/sinwave.png" /></a></div><p>My sample data comes from Virtual Organ software <a href="http://sourceforge.net/projects/ourorgan/">GrandOrgue </a>and the <a href="http://sourceforge.net/p/ourorgan/samplesets/Sample%20Sets/">sample sets</a> created for it. In this case I'm using the<a href="http://sourceforge.net/p/ourorgan/samplesets/Burea_funeral/"> Burea Funeral Chapel </a> sample set.<br />
<p>My first test was simply to treat the sample as-is and loop the sound using the given offsets. This did not give good results with a notable noise as the data from the end of the sample joined with the beginning. I realized nearing the end of writing this post that the mistake I made was treating the offsets as counts of bytes instead of samples. The documentation I was using said they were bytes. But the early mistake caused me to develop the following, which turned out useful. You can try out my first experiment yourself with this lab's code and the included sample.<br />
<pre>% wavloop -n -s 045-A.wav
</pre><p>The -s flag treats sample offsets as byte counts. The -n flag turns off finding the nearest zero.<br />
<p>Audacity has a feature where you can move the selection boundary to the nearest zero in the waveform. In the picture above that would be the beginning, middle, or end of the waveform. Using this feature creates a more seamless transition between the end and start of the loop. However, the sound samples can be any point along the curve and there are very few actual zeroes in the sample data. Instead I search for the crossing point of the y axis from negative to positive. The start and end samples must cross the zero-line going in the same direction. In the picture, the end of the graph is crossing the boundary in the upward direction and would join perfectly with the start or the curve forming a perfect loop. Play the sample without the -n flag to hear the difference.<br />
<pre>% wavloop -s 045-A.wav
</pre><p>This works in most cases, but because the sample is in stereo the left and right channel might not be heading in the same direction at the same time.<br />
<p>For the release cuepoint I follow a similar technique. I search for the zero crossing point where the data is rising. That will be where I jump to. But where I jump from could be anywhere in the sample data. So when the release event happens I also need to look for the nearest crossing point from where I am. This gives us the state transitions in the code for generating the sound data: START, LOOPING, RELEASING, RELEASE, and DONE.<br />
<p>For the final experiment I tried to use the begin and end markers in the wav file as sample counts instead of bytes. In a 16bit, 2 channel WAV file there are 4 bytes for each stereo sample. This ended up making the best sounding loops.<br />
<pre>% bind -a '#A' /dev
% wavloop -n 045-A.wav
</pre><p>This is a lot of explanation for a little bit of code. I was hoping writing it out would help me figure out how to fix the looping to sound seamless in all cases, which it did, since I eventually tried the experiment of treating offsets as sample counts. But finding the nearest zero is still useful for jumping to the release cue point. The other way of making that join is by using a cross-fade, which I didn't try.<br />
<p>The next step will be to load in a complete sample set, at least for one windchest, and implement midiplay's Instrument interface to plugin to midiplay.<br />
<h2>FILES</h2><a href="http://code.google.com/p/inferno-lab/source/browse/#hg%2F111">lab 111 </a><br />
<h2>Footnotes</h2><p>The graph is generated in inferno-os by,<br />
<pre>calc -s 'for (i := 0; i < 2*Pi; i += 0.1) print i " " sin i "\n"' |math/graph0 /fd/0
</pre>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-13791530268683953472012-12-23T19:18:00.000-05:002012-12-23T19:18:10.461-05:00lab 110 - inferno archive edition<div dir="ltr" style="text-align: left;" trbidi="on"><div id="post">I've been occupied recently with archiving my digital media. I've been copying home videos on DV tapes to hard-disk, ripping audio CD's to WAV files, gathering photo collections, and trying to copy documents from Iomega disks, floppies, and my dusty old Acorn RiscPC.<br />
<p>The plan is to have a copy of this data to give to each of my children. My Dad recently scanned and sent me all his photographs of me and my siblings growing up; he also included pictures of himself and my Mother when they met in Africa. With technology today each generation can build a digital library of family history to hand on to the next generation. In the past a family album may have been passed on to only one person. The accumulation of digital data still presents problems. It requires discipline to store files that are open and not locked into devices or proprietary formats.<br />
<p>With digital preservation in mind I've tried to use file formats <a href="http://www.archives.gov/preservation/products/definitions/filetypes.html">recommended </a>for long term archiving. WAV files for audio, DV for video, JPG and PNG for pictures, PDF for documents, and plain text.<br />
<p>Today the storage media is a 1TB external hard-disk. In my current computing environment I'm plugging that disk via USB into computers running Windows 7, Ubuntu, MacOSX, RiscOS, and Raspian. Ideally I'd be able to launch an application from the hard-disk that'd be able to playback the archived media on any of these host systems. This is where Inferno enters the picture.<br />
<p>Based on the criteria for selecting the file types, (non-proprietary, well documented, wide support) all the host systems should support the files natively, or a download can be easily found that will. However, the challenge is to get Inferno to do it nearly as well and work with a single set of tools everywhere. The tools are then preserved with the media on the disk.<br />
<p>So here's the plan. I've created a clone of inferno-os (<a href="http://code.google.com/r/caerwynj-inferno-ae/">caerwynj-inferno-ae</a>) and I will try to support the media files that are stored in a digital archive. The target functionality is the following:<br />
<p><ul><li>Display pictures (JPG, PNG, GIF)</li>
<li>Playback audio (WAV)</li>
<li>Playback video (AVI-DV, MPEG-2)</li>
<li>View ebooks (ePub3.0)</li>
<li>View documents (Plain text, HTML4.0, PDF, DjVu?)</li>
<li>Playback MIDI (built-in synethsizer)</li>
<li>Mount disk images, archives, and file systems</li>
<li>Compress or decompress files (gzip, bzip2)</li>
</ul><p>Another important aspect of this is to have a collection of EMU's that run on all the target platforms. I started this project in the <a href="http://code.google.com/p/inferno-bin/">inferno-bin</a> repository a while ago. But it needs updating and the discipline to keep updating it. Ultimately the system interface of EMU needs to be locked down to give the <a href="http://ipn.caerwyn.com/2008/05/lab-88-degrees-of-freedom.html">freedom to run</a> dis well into the future without the need to recompile.<br />
<p>Here's where we are with support inside Inferno:<br />
<table><col width="40%">
<col widht="60%"><tbody>
<tr><th>File type</th><th>Inferno support</th></tr>
<tr><td>JPG, PNG</td><td>Supported by wm/view</td></tr>
<tr><td>WAV</td><td>Supported by wavplay</td></tr>
<tr><td>MIDI</td><td>Limited support by midiplay</td></tr>
<tr><td>ePub</td><td>Support for older OEB versions in ebook/ebook. I've started updating to support ePub standard.</td></tr>
<tr><td>Plain text</td><td>Supported</td></tr>
<tr><td>HTML4.0</td><td>Limited support in Charon</td></tr>
<tr><td>PDF</td><td>Unsupported, but MJL's <a href="https://bitbucket.org/mjl/pdfread">PDF library</a> are the beginnings of limited support</td></tr>
<tr><td>AVI-DV</td><td>No support</td></tr>
<tr><td>MPEG-2</td><td>Some support for an MPEG device. Could try to use Raspberry-Pi device support for MPEG-2</td></tr>
<tr><td>tar, gzip</td><td>Supported</td></tr>
<tr><td>bzip2</td><td>Unsupported</td></tr>
</tbody></table><p></div></div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com4tag:blogger.com,1999:blog-8112083.post-72529535878814581592012-12-23T12:38:00.000-05:002012-12-27T16:28:34.518-05:00lab 109 - wm/view true color<div id="post"><p>There has been a three and a half year gap in my posts to this blog. In that time I hadn't done any Limbo programming. I've used Acme as my editor everyday, but I was drifting towards using Notepad++ more often. In the past couple of months I've had the time to contemplate doing some hacking projects. I wanted to explore what I could do with Inferno for multimedia file types. This lab was the first thing I tackled in using Inferno again. I had to open up the Limbo paper to remember even some basic syntax.<br />
<p>It bothered me that wm/view only displayed images using the Inferno 256 color map. Charon didn't have this limitation and I thought it had something to do with their respective image libraries. They don't use the same code. I extracted Charon's img.b code out into another view tool only to realize once I'd finished that the difference was not in the handling of JPEGs or PNGs but in the remap of the raw image to an Inferno image after the image was loaded. <br />
<p>I changed wm/view to remap the graphic to 24bit color. Here are the results.<br />
<p><a href="http://www.flickr.com/photos/caerwyn/8300047071/" title="view-true by caerwyn, on Flickr"><img src="http://farm9.staticflickr.com/8501/8300047071_0455e3db7e.jpg" width="500" height="313" alt="view-true"></a><br />
<p>There are a few other files in this lab. <br />
<dl><dt>view.b</dt>
<dd>The modified wm/view to display JPEG and PNG in true color.
<dt>img.b</dt>
<dd>The img library extracted from charon</dd>
<dt>pics.b</dt>
<dd>A view tool similar to the Plan 9 view command. It uses the charon img.b library. It also used the rioclient, riolib, and menuhit files included in this lab to give the appearance of a Rio window. You can see it in the upper left of the screenshot. Using this you can zoom in, pan, and fit to window. The zoom now works much better with true color than with the colormap.
<dt>riolib.b</dt>
<dd>An alternative to wmlib.b for non-TK window applications</dd>
<dt>rioclient.b</dt>
<dd>Use with riolib.b for non-TK window applications</dd>
<dt>menuhit.b</dt>
<dd>Menus for non-TK windows</dd>
<dt>clock.b</dt>
<dd>A non-TK clock demonstration from Plan 9</dd>
<dt>picsar.b</dt>
<dd>Similar to pics.b but doesn't use the charon img library. Also doesn't do the remap to 24bit color.</dd>
<dt>matador.jpg</dt>
<dd>Test JPEG</dd>
<dt>test.png</dt>
<dd>Test PNG</dd> </dl><h2>FILES</h2><p><a href="http://code.google.com/p/inferno-lab/source/browse/#hg%2F109">lab 109</a><br />
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com2tag:blogger.com,1999:blog-8112083.post-34104782136291801362012-12-23T11:24:00.000-05:002012-12-27T16:28:12.816-05:00lab 108 - wavplay<div id="post"><p>wavplay plays a <a href="http://en.wikipedia.org/wiki/Wav">WAV</a> file. It is merely a combination of the wav2iaf and the auplay commands already in Inferno. I have no audio in IAF format, but I am putting together 100s of GBs of wav files as I'm ripping my CD collection. <br />
<pre>% bind '#A' /dev
% wavplay track.wav
</pre><h2>FILES</h2><p><a href="http://code.google.com/p/inferno-lab/source/browse/#hg%2F108">wavplay.b</a><br />
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-41127227672189439732012-12-20T18:45:00.000-05:002012-12-20T18:45:48.735-05:00lab 107 - midiplay<div id="post"><h2>NAME</h2><p>lab 107 - midiplay<br />
<h2>NOTES</h2><p>Midiplay plays back MIDI files. It uses the synthesizer I described in lab 62 and the MIDI module from lab 73. The command takes only one argument, the path to the midi file. I've included one in the lab to demonstrate. Bind the audio device before using it.<br />
<pre>% bind -a '#A' /dev
% midiplay invent15.mid
</pre><p>The synthesizer has 16 note polyphony. It uses three oscillators, one at the pitch, one at half pitch, one at double pitch. There is also a filter, two delays and a vibrato. <br />
<p>The sample rate is 8000hz and there is one mono channel (MIDI channel events are ignored). It performs well enough to work on my laptop without JIT turned on. All the synthesizer parameters can only be tweaked from inside the code at the moment.<br />
<p> <h2>FILES</h2><p><a href="http://code.google.com/p/inferno-lab/source/browse/107/?r=b89af0e0d6d4e59a347221eab803298b690dc991">lab - 107</a><br />
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-11889474808842076212010-12-06T16:22:00.007-05:002011-01-25T07:43:08.773-05:00lab 106 - UNIX RUDP Support<div id="post">
<h2>NAME</h2>
<p>lab 106 - UNIX RUDP Support
<h2>NOTES</h2>
<p>
A simple port of the Inferno native RUDP support to a standalone UNIX environment (with a client and server example). This particular example is single threaded and specifically intended for use with synchronous RPC mechanisms (in this particular instance a synchronous 9P client and server).
<p>
It could probably be greatly improved in several dimensions, but this example should provide a minimal implementation which more complicated incarnations can be built off of. Enjoy.
<h2>FILES</h2><p>
106/Makefile<br/>
106/client.c<br/>
106/rudp.c<br/>
106/rudp.h<br/>
106/server.c
</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8112083.post-50585767605706566262009-11-04T20:11:00.005-05:002009-11-04T20:32:34.488-05:00lab 105 - automount<div id="post">
<h2>NAME</h2><p>
lab 105 - automount
<h2>NOTES</h2><p>
A small modification to mntgen yields an automounter which automatically mounts a server based on path name. This is a fairly crude proof of concept with hardwired port numbers and top-level mount path -- but it would be fairly easy to make it a bit more robust.
Essentially, I just added a mount command to the code which dynamically adds the directory node to the mntgen tree on reference. Then I made the file system handling call its own thread so that the mount could reference the synthetic without locking up the original single threaded synthetic file server. One problem is that unknown servers take some time to respond with file note found, which is less than desirable. Fixing this and other annoyances is left as an exercise for the reader.
<h2>EXAMPLE</h2>
<pre>
% ls /amnt
% mount {automnt} /amnt
% ls /amnt
% ls /amnt/localhost/usr/inferno
/amnt/localhost/usr/inferno/9cpu
/amnt/localhost/usr/inferno/README
/amnt/localhost/usr/inferno/charon
/amnt/localhost/usr/inferno/lib
/amnt/localhost/usr/inferno/namespace
/amnt/localhost/usr/inferno/tmp
%
</pre>
<h2>FILES</h2><p>
105/automnt.b
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-50999307652340744272009-08-06T21:08:00.009-04:002009-11-16T21:37:34.914-05:00lab 104 - ducts: bi-directional mount channels<div id="post">
<h2>NAME</h2><p>lab 104 - ducts: bi-directional mount channels
<h2>NOTES</h2><p>Hi there, I want to talk to you about ducts. Are your ducts old fashion, out of date? </p><p>In this lab, I'll walk through a little facility I built to allow me to have bi-directional mounts over a single file descriptor channel. The nature of the way I access my Blue Gene environment requires me to do an awful lot over a single communications channel. I wanted the ability to both access the target node's file system while simultaneously exporting mine, while only using a single file descriptor.</p><p>To facilitate this, I wrote a little 9P-knowledgeable multiplexor which directs T-msgs to an export thread and R-msgs to a mount thread while multiplexing the responses from those threads back onto the same file descriptor.</p><p>The initial implementation of the base mechanism was relatively simple until I had to start considering how to clean things up when I started unmounting directories on one side or both. The liberal use of sys->pipe throughout the code meant it was somewhat difficult to detect when things were finished. I ended up adding a signaling packet to the protocol to notify the remote Duct when I had closed my mount, and then implemented a channel based monitor infrastructure so I could track what was still running and appropriately shut down the various pieces as things unmounted (it is possible to unmount one side without unmounting the other).</p><p>The interface is fairly simple, if using the module interface you spawn an uplink, specifying an input file descriptor and an output file descriptor for the connection (which may be the same FD), the path you wish to export and the mount point for where you want to place the imported file hierarchy.</p><p>If you are running it from the command line, it uses stdin and stdout for the input and output FDs and you only need specify the export path and mount point.</p><h2>EXAMPLES</h2><p></p><blockquote></blockquote><p></p><blockquote><p>host1% listen -A tcp!*!1127 duct /usr/ericvh /n/foo</p><p>host2% dial -A tcp!host1!1127 duct /usr/inferno /n/bar</p></blockquote><p></p><h2>FILES</h2><p></p><ul><li>104/duct.m </li><li>104/duct.b</li></ul><p></p><p>
</p>
</div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-8112083.post-2753192898622972002009-06-28T12:08:00.006-04:002011-01-25T07:45:43.135-05:00lab 103 - python content assist<div id="post">
<h2>NAME</h2><p>
lab 103 - python content assist
<h2>NOTES</h2><p>
Building on lab 102 I've started to write an acme client
for python content-assist in python.
I read the code for python's IDLE editor and pulled some pieces out
of that to help with my implementation. The AutoComplete.py
and CallTip.py modules are copied out of idlelib and reduced to just
the functions I needed.
<p>
Just as in lab 102 the acme client is implemented using pyxp
to connect to the acme file system.
<p>
In an acme editor window execute PyAssist. Start
typing some python. After typing a dot the client
will display available attributes for the module.
Continue typing and it will reduce the list
to those names with a matching prefix. Press the
Tab key to complete a name or prefix depending on
whether there is one or more names.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3667829637/" title="109528114135-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3624/3667829637_52ec958398_o.png" width="272" height="252" alt="109528114135-Acme-SAC" /></a>
<p>
After typing an open parenthesis
the client will show the call tip for the function.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3667832967/" title="109528114325-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3546/3667832967_94b7a4aa67_o.png" width="272" height="180" alt="109528114325-Acme-SAC" /></a>
<p>
The client implements an <b>Import</b> command so that more python modules
can be added to the namespace. PyAssist doesn't attempt to parse the edited
script. Idlelib includes code that could help to do this. But for now
you need to pass a module name as argument to the <b>Import</b> command.
<p>
A next step for python integration to acme is to implement a debugger.
Python comes with libraries for this too, so a debugger similar
to the acme debugger adeb should be a future lab. I might then
run edited scripts in a sub shell and inspect that process' namespace
similar to the IDLE implementation, so that content-assist adapts
to the script being edited.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/103/">inferno-lab/103</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com3tag:blogger.com,1999:blog-8112083.post-8849426589888928402009-06-23T20:58:00.005-04:002011-01-25T07:45:53.209-05:00lab 102 - python acme client<div id="post">
<h2>NAME</h2><p>
lab 102 - python acme client
<h2>NOTES</h2><p>
A recent post to 9phackers announced
<a href="http://groups.google.com/group/9p-hackers/msg/1335fb4c85a89e18">Pyxp</a>,
another implementation of Styx in Python.
<p>
I immediately downloaded Pyxp and tried it out. I had no trouble using
it so I started thinking about python clients I could write.
Python is still new to me so writing a styx client was an excuse to
get more practice.
<p>
I started with an acme client.
I translated the acmewin limbo module I use for most acme-sac clients to python.
Below is a simple example opening a new acme window
and doing some operations on it.
<pre>
from acmewin import Acmewin
win = Acmewin()
win.writebody("hello, world!\n\n\n")
win.tagwrite("Hello")
win.writebody("goodbye")
win.replace("/goodbye/", "GOODBYE")
win.select(",")
win.show()
</pre>
<p>
Remember to export the namespace before trying it out.
<pre>
% styxlisten -A 'tcp!*!localhost' export /
% textclient.py
</pre>
<p>
I recently saw on Hacker News a repost of Peter Norvig's
<a href="http://norvig.com/spell-correct.html">spelling corrector</a>.
I thought this would make an easy first trial of my python acmewin code.
I implemented a client to offer spelling suggestions to an editor window.
It works somewhat like my earlier acme content assist code.
This client opens the event file of another window it is assisting
and writes text out to its own window.
In this case it offers a suggested spelling for the word currently being
typed.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3655796216/" title="109523203021-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3548/3655796216_b4fa060d72_o.png" width="191" height="119" alt="109523203021-Acme-SAC" /></a>
<p>
Here's the implementation. Note that this is single threaded and it is not reading the event file of the second window. I haven't gotten that far in the Python book.
<pre>
#!/dis/python26
import sys
from acmewin import Acmewin
import spell
win = Acmewin(int(sys.argv[1]))
outwin = Acmewin()
while True:
(c1, c2, q0, q2, flag, nr, r) = win.getevent()
if c2 in "xX":
if flag & 2:
win.getevent()
if flag & 8:
win.getevent()
win.getevent()
win.writeevent(c1, c2, q0, q2)
if c2 == "x" and r == "Del":
outwin.delete()
break
if c1 == "K" and c2 == "I":
ch = r[0]
if ch in " \t\r\n":
outwin.replace(",", "")
continue
while q0 >= 0 and not (ch in " \t\r\n"):
sss = win.read(q0, q0+1)
if not sss:
# print("empty sss %d" % q0)
sss = " "
ch = sss[0]
q0 -= 1
if q0 < 0 and not(ch in " \t\r\n"):
q0 = 0
else:
q0 += 2
ss = win.read(q0,q2)
lastcorrect = spell.correct(ss)
outwin.replace(",", lastcorrect)
</pre>
<p>
To run this we need to know the id
of the window we are assisting, so we need a wrapper
to send the <b>$acmewin</b> environment variable as an
arg to the script. For that I have a script called SpellAssist.
<pre>
#!/dis/sh
$home/python/assist.py $acmewin
</pre>
<p>
Now that I have a simple assist-like client working I'd like to
develop it further. I'd like to try having content assist for python
inside acme. It should be possible to adapt the python
code that implements the IDLE editor to this purpose.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/102/">inferno-lab/102</a>
<br>
<a href="http://norvig.com/big.txt">big.txt</a> the large text file used to train the spelling corrector. Note that the path is hardcoded in spell.py and should be changed locally.
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com1tag:blogger.com,1999:blog-8112083.post-15270199048309174802009-06-15T21:19:00.003-04:002011-01-25T20:34:33.823-05:00lab 101 - limbo B+ tree<div id="post">
<h2>NAME</h2><p>
lab 101 - limbo B+ tree
<h2>NOTES</h2><p>
This lab is an implementation of a B+ tree. It is code
from a much older lab that was overly complicated
and buggy. I pulled the B+ tree code out, tried to fix
the bugs and clean it up. The interface is roughly the
same as dbm(2). Here is a synopsis.
<pre>
include "btree.m";
btreem := load Btreem Btreem->PATH;
Datum, Btree: import Btreem;
Btree: adt {
create: fn(file: string, perm: int): ref Btree;
open: fn(file: string, flags: int): ref Btree;
fetch: fn(b: self ref Btree, key: Datum): Datum;
delete: fn(b: self ref Btree, key: Datum): int;
store: fn(b: self ref Btree, key: Datum, val: Datum):int;
firstkey: fn(b: self ref Btree): Datum;
nextkey: fn(b: self ref Btree, key: Datum): Datum;
flush: fn(b: self ref Btree);
close: fn(b: self ref Btree);
};
init: fn();
</pre>
<p>
Like Dbm the keys and values are stored as arrays of
bytes, and being a B+tree the values are stored only
in the leaf nodes.
The maximum key and val size is 255 each.
The block size is 8192, so the maximum leaf node size is 515 making
the minimum branching factor 15.
The maximum number of records in an internal nodes
assuming an int as key is 630.
<p>
The delete is not fully implemented; it doesn't
merge nodes.
<p>
Here is some example code listing the full contents of
a btree.
<pre>
sys := load Sys Sys->PATH;
btreem := load Btreem Btreem->PATH;
Datum, Btree: import btreem;
btreem->init();
bt := Btree.open("index.bt", Sys->ORDWR);
for(key := bt.firstkey(); key != nil; key = bt.nextkey(key)){
v := bt.fetch(key);
sys->print("%s %s\n", string key, string v);
}
</pre>
<p>
Here is a rough comparison of btree vs. dbm.
This simple test is more of a sanity check that the
btree doesn't do anything horribly wrong in its
implementation (which it did originally, by making a syscall
to get the daytime for every block it tried to get).
<pre>
% awk '{print $1, NR}' < /lib/words | tr -d 'cr' > t1
% >index.bt
% >dbm.pag
% >dbm.dir
% time sh -c 'btree/store < t1'
0l 4.172r 4.172t
% time sh -c 'dbm/store dbm < t1'
0l 35.25r 35.25t
% ls -l dbm.* index.bt
--rw-rw---- M 6 xcs0998 XCS0998 8192 Jun 04 12:38 dbm.dir
--rw-rw---- M 6 xcs0998 XCS0998 1048576 Jun 04 12:38 dbm.pag
--rw-rw---- M 6 xcs0998 XCS0998 770048 Jun 04 12:36 index.bt
% time sh -c 'btree/list > /dev/null'
0l 5.187r 5.187t
% time sh -c 'dbm/list dbm > /dev/null'
0l 9.438r 9.438t
</pre>
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/101/">inferno-lab/101</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-83451690334518605622009-06-07T14:46:00.007-04:002011-01-25T07:44:33.162-05:00lab 100 - limbo tags<div id="post">
<h2>NAME</h2><p>
lab 100 - limbo tags
<h2>NOTES</h2><p>
I've been playing more with
<a href="http://ctags.sourceforge.net/">exuberant ctags</a>.
It's possible to make <b>ctags</b>
recognize limbo source code using regular expressions to identify
symbols we want tagged.
<p>
Here's a command I defined called <b>ltags</b>. It will search for
functions, assuming the function name is not preceded by spaces,
and, more reliably, find adt and module definitions:
<pre>
#!/dis/sh
os -t ( /n/D/ctags57/ctags.exe -n
'--tag-relative=yes'
'--langdef=limbo'
'--langmap=limbo:.b.m'
'--regex-limbo=/^([a-zA-Z][a-zA-Z0-9]+)\(.*\)/\1/m/'
'--regex-limbo=/([a-zA-Z][a-zA-Z0-9]+) *: *adt */\1/t/'
'--regex-limbo=/([a-zA-Z][a-zA-Z0-9]+) *: *module */\1/c/'
$* )
</pre>
<p>
Try running it under /, and use relative path names:
<pre>
% cd /
% ltags -R appl module
</pre>
<p>
And open the acme client
<pre>
Ctag /tags
</pre>
<p>
<a href="http://www.flickr.com/photos/caerwyn/3603741089/" title="10957142117-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3612/3603741089_c7e7ecc9ff_o.png" width="268" height="292" alt="10957142117-Acme-SAC" /></a>
<p>
I added a command "Tag" to the Ctag program that will
find and print all matching symbols to the name given as argument.
Mouse-chording makes this nice to use. Right-clicking on symbol
names will open the source file at that location. Browsing the
whole limbo source hierarchy is then made quite easy.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/100/">inferno-lab/100</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-35349610019099280372009-05-30T11:35:00.004-04:002009-06-07T20:46:05.059-04:00lab 99 - catching up with inferno-os<div id="post">
<h2>NAME</h2><p>
lab 99 - catching up with inferno-os
<h2>NOTES</h2><p>
<p>
I had fallen way behind in keeping acme-sac code
up to date with inferno-os. I tried to correct that this
week by merging changes and adding files from inferno-os.
<p>
I wrote a script,
<a href="http://inferno-lab.googlecode.com/svn/trunk/99/">part of this labs files</a>, to check the differences
between inferno-os and acme-sac.
I'm hoping by using this script I'll stay up to date
with inferno-os as changes are made.
The script compares sub-trees, such as the limbo
source code hierarchy, the C source code, the manual pages,
and the /lib directory. It prints out commands to
diff the files or copy the file over.
<pre>
% inf
inferno synchronization:
appl show changed limbo files
sys show changed C source files
man show changed man pages
lib show changed /lib files
cmd show changed sh scripts below /dis
update refresh file tree info for local and remote
adiff adiff selected file with inferno-os equivalent
diff diff selected file with inferno-os equivalent
missing show files missing from local tree
added show files added to local tree
% inf update
% inf sys
diff -u /sys/emu/port/win-x11a.c /n/D/inferno-os/emu/port/win-x11a.c
cp /n/D/inferno-os/emu/port/win-x11a.c /sys/emu/port/win-x11a.c
%
</pre>
<p>
There is a short list of directories I did not synchronize
because I know every file has changed; I consider these a fork,
though the changes to charon are not dramatic,
<pre>
/appl/acme/ /appl/charon/ /acme/
</pre>
<p>
There is a list of directories acme-sac has removed
from the inferno-os hierarchy:
<pre>
/FreeBSD/ /libbio/ /utils/
/Hp/ /libdynld/ /appl/alphabet/
/Inferno/ /libkern/ /appl/collab/
/Irix/ /liblogfs/ /appl/demo/
/NetBSD/ /libnandfs/ /appl/ebook/
/OpenBSD/ /libprefab/ /appl/spree/
/Plan9/ /libtk/ /appl/tiny/
/asm/ /tools/ /appl/wm/
</pre>
<p>
And finally there is a sub-tree we need to actually synchronize:
<pre>
Linux appl/grid lib libkeyring libmp module
Nt appl/lib lib9 libmath libsec
appl appl/math libdraw libmemdraw include
appl/cmd emu libinterp libmemlayer man
</pre>
<p>
After comparing these sub-trees I built a list of files
that were intentionally
<a href="http://inferno-lab.googlecode.com/svn/trunk/99/sync/removed">removed</a> and files locally
<a href="http://inferno-lab.googlecode.com/svn/trunk/99/sync/changed">changed</a>.
Any file found "missing", where inferno-os added
new functionality, was copied over. Files that were
not marked as locally changed but nevertheless contained changes
were merged, usually overwritten with the latest from inferno-os.
There is also a list of files
<a href="http://inferno-lab.googlecode.com/svn/trunk/99/sync/added">added</a> to acme-sac apart from anything
below /acme.
<p>
I found it useful to use the side-by-side
diff functionality from
<a href="http://www.caerwyn.com/ipn/2009/05/lab-95-acme-side-by-side-diff.html">lab 95</a>.
I added some simple
interactive merge functionality that worked if you start from the
end of the file and stepped backwards through the diffs
using a PrevDiff command on the adiff output and calling
the command Merge to apply the currently highlighted change.
The implementation of this Merge command was pretty simple
because it involved just copying the selected text from the
source file over to the target. Given acme's filesystem interface
this is literally a file copy,
<pre>
cp /mnt/acme/$b/rdsel /mnt/acme/$a/wrsel
</pre>
<p>
The PrevDiff commands does the work of setting up
the selected texts correctly, properly handling the case
of added, changed, or deleted text.
<p>
The list of files in the changed list should be reduced. Some of these are changes that could be pushed to inferno-os. E.g., diff adds the -u flag, os has changes from rog implementing host file name translation.
Some files will need to be continually merged, such as the man page indices.
<p>
The list of files and directories removed are mostly just functionality either specific to running inferno as a native os, or the Tk functionality. There shouldn't be anything there people would really miss in an acme-sac environment. Though if there is let me know.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/99">inferno-lab/99</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-18949867889461192992009-05-18T22:25:00.003-04:002011-01-25T07:46:02.468-05:00lab 98 - acme Ctag<div id="post">
<h2>NAME</h2><p>
lab 98 - acme Ctag
<h2>NOTES</h2><p>
Ctag is a new client for acme that reads the
tags file generated by ctags. In particular
I worked with
<a href="http://ctags.sourceforge.net/">Exuberant Ctags</a>
on windows. While working on acme content-assist in
<a href="http://www.caerwyn.com/ipn/2009/04/lab-94-acme-content-assist.html">lab 94</a> I thought a next possible step was using ctags
for code completion.
So in writing this program I wanted to explore using exuberant ctags by doing something easier, that is by providing assistance with
code navigation.
<p>
Exuberant ctags understands many languages,
but for the current effort I tried it on Java and Python.
<p>
For example, I generated tags for the python standard
libraries,
<pre>
cd:/Python30
ctags -R --excmd=number --exclude=test
</pre>
<p>
Then run the client on the tags file,
<pre>
Ctag /n/D/Python30/tags
</pre>
<p>
What you start with is a blank window. In the tagline
is a <b>File</b> command. Give it an argument of a filename
which it will grep for in the ctags file.
<pre>
File mailbox
</pre>
<p>
<a href="http://www.flickr.com/photos/caerwyn/3543896033/" title="109418215737-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3353/3543896033_65125900da.jpg" width="500" height="350" alt="109418215737-Acme-SAC" /></a>
<p>
It will then show all classes and methods within matching
files. You can right-click on a method name to open the
file at the line where the method is defined.
<p>
Ctag gives a top down view of a project. It is like the
Outline view of some IDEs.
A next step would be to implement the reverse direction,
but probably as part of the content assist.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/98/">inferno-lab/98</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-69489454621128182382009-05-18T21:20:00.003-04:002011-01-25T07:43:27.576-05:00lab 97 - acme Navigator<div id="post">
<h2>NAME</h2><p>
lab 97 - acme Navigator
<h2>NOTES</h2><p>
I created a simple directory browser for acme.
Called Navigator, it opens directories in the same
window. This makes descending
deep but unfamiliar directory hierarchies
easier because it avoids the proliferation of windows
that clutter the desktop.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3543167689/" title="109418171639-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3414/3543167689_a75099a1b8_o.png" width="316" height="227" alt="109418171639-Acme-SAC" /></a>
<p>
The client understands the command
<b>Pin</b> for creating a standard acme directory window
for the current directory (to pin the current directory to the desktop).
This is an example for a rather simple client for acme.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/97/">inferno-lab/97</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com1tag:blogger.com,1999:blog-8112083.post-6031845367637273432009-05-17T19:59:00.005-04:002011-01-25T07:43:37.100-05:00lab 96 - acme color schemes<div id="post">
<h2>NAME</h2><p>
lab 96 - acme color schemes
<h2>NOTES</h2><p>
The inferno version of acme always contained some code to configure
the acme color scheme, but I'm not sure if it always, if ever worked.
<p>
I modified the code a little in acme-sac to make the color configuration
work. And I came up with a few new schemes.
<p>
The schemes are checked into acme-sac under <b>/acme/color</b>.
To use a different scheme add a line like this to
your <b>$home/lib/profile</b>
<pre>
run /acme/color/evening
</pre>
<p>
Running the "evening" scheme will make acme look like this.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3538989033/" title="10941714634-Acme-SAC by caerwyn, on Flickr"><img src="http://farm3.static.flickr.com/2115/3538989033_3034f56d93_o.png" width="521" height="382" alt="10941714634-Acme-SAC" /></a>
<p>
The configuration files sets a few environment variables of the following form:<pre>
acme-fg-text-0='#000000'
</pre>
<p>
This sets the foreground text color in the body text to black.
It's possible to mix two colors, as for the standard acme background.
<pre>
acme-bg-text-0='#FFFFAA/#FFFFFF'
</pre>
<p>
Another simple scheme is "bw" for black and white.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3539046079/" title="109417142536-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3585/3539046079_b9f6feed38_o.png" width="512" height="375" alt="109417142536-Acme-SAC" /></a>
<p>
There is also a file /acme/color/standard for the default acme color scheme which can be copied tweaked.
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com2tag:blogger.com,1999:blog-8112083.post-27685127376951532702009-05-16T22:25:00.005-04:002011-01-25T07:46:14.443-05:00lab 95 - acme side-by-side diff<div id="post">
<h2>NAME</h2><p>
lab 95 - acme side-by-side diff
<h2>NOTES</h2><p>
Here's a script I wrote to help do side-by-side diffs in Acme.
Run adiff as normal inside acme. And then
in the adiff output window type the command NextDiff.
Executing this command will step through each diff in
the adiff output and highlight the changed region in each file.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3537787474/" title="109416221033-Acme-SAC by caerwyn, on Flickr"><img src="http://farm3.static.flickr.com/2402/3537787474_be85b0f488.jpg" width="500" height="350" alt="109416221033-Acme-SAC" /></a>
<p>
The script will highlight each line, avoiding the
cursor jump to highlighted regions as happens by plumbing
or right-clicking on a file pattern. Though the cursor jump
will occur the first time the diffed files are opened, for subsequent
execution of NextDiff for diffs within the same files the cursor
will remain over the NextDiff command and the highlighted regions
will change with file scrolling to show at least part of the
changed regions.
<p>
When the files first open you'll still need to manually arrange
the files side by side. There is no acme API for window placement.
However, the command will save some amount of scrolling, clicking,
and mouse movement within the adiff output.
<p>
Here's the script for acme-sac. Some slight changes might need to be made
for inferno, and something very similar should work in p9p acme.
<p>
<pre>
#!/dis/sh
fn findwin {
file = $1
< /mnt/acme/index getlines {
(w x y z zz f rest) := ${split ' ' $line }
if {~ $f $file} {echo $w}
}
}
fn highlight {
file = $1
lineno = $2
a := `{findwin $file}
if {! ~ $#a 0} {
cd /mnt/acme/$a
<> addr {
echo -n $lineno > addr
echo 'dot=addr' > ctl
echo show > ctl
}
}{
plumb $file:$lineno
}
}
cwd = `pwd
cd /mnt/acme/$acmewin
{
echo -n 'addr=dot' >ctl
(a b) := `cat
echo -n '/^[^\-<>].*\n/' > addr
echo -n 'dot=addr' > ctl
echo -n show > ctl
(f1 cmd f2) := `{getline data}
(f1 l1) = ${split ':' $f1}
(f2 l2) = ${split ':' $f2}
f1 = `{cleanname -d $cwd $f1}
f2 = `{cleanname -d $cwd $f2}
highlight $f1 $l1
highlight $f2 $l2
} <> addr
</pre>
<p>
The findwin and highlight functions I have used before in the acme debugger interface, adeb, in /appl/acme/acme/debug/adeb.b.
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-33805520471775385022009-04-08T20:21:00.011-04:002011-01-25T07:44:02.192-05:00lab 94 - acme content assist<div id="post">
<h2>NAME</h2><p>
lab 94 - acme content assist
<h2>NOTES</h2><p>
This lab explores a way of implementing content assist for acme.
I've focused on the user interface and how that might work inside
acme and not specific language support.
<p>
The command is called Assist. Launch it within an editor window and
it will provide assistance for that window only. A new window will open
and while you type in the edit window Assist
will attempt to match the currently typed word against the
content of /lib/words using the command look(1). The results are
displayed in the +Assist window.
<p>
With results in the +Assist window and with focus still in the editor
window type Ctrl-l to step down through the results. Each result
will be selected in turn and the selection will wrap around to the top.
To choose a selection and replace the currently edited word with the selected
text type Ctrl-k.
<p>
The Assist command also supports file completions. Start typing a path in
the edit window and type Ctrl-y to show file completions in the +Assist window.
Use Ctrl-l and Ctrl-k to navigate and select from the results.
<p>
Assist assumes /lib/words exists and is the default for finding word
completions. You can download a copy of
<a href="http://hg.pdos.csail.mit.edu/hg/plan9/raw-file/91a798d14960/lib/words">/lib/words</a> from plan9port.
You can also run Assist with a filename for your own list of words. It should
follow the format expected by look(1).
<p>
<a href="http://www.flickr.com/photos/caerwyn/3439120268/" title="10931314100-Acme-SAC by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3399/3439120268_dbd168ee51_o.png" width="378" height="231" alt="10931314100-Acme-SAC" /></a>
<p>
Some interesting things about the implementation. Acme isn't
aware of the control keys. It adds the control character to the
document and the Assist plugin reads it and removes it from the document.
In this way plugins can define their own special keys as long
as acme does not already process them in some special way (see table below).
<p>
The Assist plugin does cause the editor window to change
its behaviour slightly. Assist has to resend all events so
that normal editor commands like Put and Undo are understood.
However, because we've opened the event file the normal refresh
of the tag line with Undo and Put when the file is dirtied does
not happen.
<p>
The +Assist window is off to the side, or it could be placed just below
the edit window. This is pretty good. But your eye does need to travel to
see completion suggestions. Maybe not as ideal as the info
popup appearing just below the cursor position. However, this method fits
in far better to the acme model.
<p>
<h2>Special Keys</h2><p>
While working on this I thought I'd map out the control keys already understood by acme. The ASCII control characters are mostly passed through to the document. However, a number are interpreted by acme in a special way. In the table below NA, non-assigned, means they have their standard ASCII assignment.
<p>
<pre>
<table>
<tr><th>Ctrl Key</th><th>Description</th></tr>
<tr><td>a</td><td>move cursor to beginning of line</td></tr>
<tr><td>b </td><td>NA</td></tr>
<tr><td>c </td><td>copy</td></tr>
<tr><td>d </td><td>NA (eof)</td></tr>
<tr><td>e </td><td>move cursor to end of line</td></tr>
<tr><td>f </td><td>file completion</td></tr>
<tr><td>g </td><td>NA</td></tr>
<tr><td>h </td><td>delete</td></tr>
<tr><td>i </td><td>tab</td></tr>
<tr><td>j </td><td>NA (carriage return)</td></tr>
<tr><td>k </td><td>NA </td></tr>
<tr><td>l </td><td>NA </td></tr>
<tr><td>m </td><td>newline</td></tr>
<tr><td>n </td><td>NA</td></tr>
<tr><td>o </td><td>NA</td></tr>
<tr><td>p </td><td>move cursor to beginning of document</td></tr>
<tr><td>q </td><td>move cursor to end of document</td></tr>
<tr><td>r </td><td>NA</td></tr>
<tr><td>s </td><td>NA </td></tr>
<tr><td>t </td><td>NA</td></tr>
<tr><td>u </td><td>delete to start of line</td></tr>
<tr><td>v </td><td>paste</td></tr>
<tr><td>w </td><td>delete to start of word</td></tr>
<tr><td>x </td><td>cut</td></tr>
<tr><td>y </td><td>NA</td></tr>
<tr><td>z </td><td>undo</td></tr>
<tr><td>[ </td><td>escape</td></tr>
<tr><td>\ </td><td>NA</td></tr>
<tr><td>] </td><td>NA</td></tr>
<tr><td>^ </td><td>NA</td></tr>
<tr><td>_ </td><td>NA</td></tr>
<tr><td>? </td><td>?</td></tr>
</table>
</pre>
<p>
There are also mappings for the Home keys. These mappings are for the acme editor window in the most recent commits to acme-sac (the up/down arrow have been changed compared to other acmes).
<p>
<pre>
<table>
<tr><th>Key</th><th>Description</th><tr>
<tr><td>Home</td><td>scroll to beginning of document</td></tr>
<tr><td>End</td><td>scroll to end of document</td></tr>
<tr><td>PgUp</td><td>scroll page up</td></tr>
<tr><td>PgDown</td><td>scroll page down</td></tr>
<tr><td>Left Arrow</td><td>move cursor left one char</td></tr>
<tr><td>Right Arrow</td><td>move cursor right one arrow</td></tr>
<tr><td>Up Arrow</td><td>move cursor up one line</td></tr>
<tr><td>Down Arrow</td><td>move cursor down one line</td></tr>
</table>
</pre>
<p>
None of the function keys are interpreted or even passed to the console on windows inferno. The escape key has some special meaning, it selects the last entered text, or cuts the selected text if text is selected. The Alt key is also used for entering unicode keys.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/94/">inferno-lab/94</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-64249955119441099592009-03-29T14:46:00.003-04:002009-03-29T15:00:27.891-04:00Google Summer of Code 2009<div id="post">
<h2>NAME</h2><p>
Google Summer of Code 2009
<h2>NOTES</h2><p>
Plan 9 has been accepted this year as a mentoring organization for <a href="http://code.google.com/soc/">Google Summer of Code</a>. The <a href="http://gsoc.cat-v.org/">Plan 9 group</a> accepts projects to do with the operating system Plan 9 from Bell Labs and from related technologies such as Inferno, Plan 9 from User Space, v9fs, 9vx, and Glendix.
<p>
If you're a student and you like any of these technologies you should apply immediately to the GSoC with your a project proposal. You can take any of the ideas you've seen explored in this blog and build on them, or develop your own ideas and take Plan 9/Inferno into new territory. I would love to see projects that extend Acme-SAC such as with content-assist or syntax highlighting. Or new Inferno applications in particular for the Nintendo DS. Inferno is an "integrating environment" so try integrating Inferno with Eclipse, Python, or Ruby.
<p>
The deadline for student project proposals is April 3rd, so you need to be quick. There is so much cool stuff you could be working on this summer; Don't let this opportunity slip away.
<p>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-3078079975619809182009-01-03T22:14:00.012-05:002011-01-25T20:50:25.364-05:00lab 92 - vxinferno<div id="post">
<h2>NAME</h2><p>
lab 92 - vxinferno
<h2>NOTES</h2><p>
In this lab I create a new Inferno builtin module that calls the <a href="http://pdos.csail.mit.edu/~baford/vm/">vx32</a>
library and get a minimal system working
that runs native x86 code, with system calls redirected to inferno's system calls
and therefore making the inferno namespace visible to the sandboxed code.
<p>
Vx32 is a new user-level sandboxing library by Bryan Ford and Russ Cox. From the <a href="http://pdos.csail.mit.edu/papers/vx32:usenix08/">vx32 paper</a>,
<p>
<i>
<blockquote>
"Vx32 is a multipurpose user-level sandbox that enables any application to
load and safely execute one or more guest plug-ins, confining each guest
to a system call API controlled by the host application and to a restricted
memory region within the host’s address space."
</blockquote>
</i>
<p>
Inferno, being a virtual operating system, provides its own system call API to limbo applications. The same system calls are available as a C API for use by native libraries that appear as builtin modules or devices within the inferno environment. This C API is a natural fit for building a Vx32 sandbox allowing native code of all kinds to run within inferno, which has considerable flexibility over the namespace made available to the native code. This would allow inferno to be extended in new ways beyond limbo, builtins, or external processes that export styx protocol. It also extends the reach of native code that when run in this hosted environment its use of files take on even greater significance.
<p>
Please read the vx32 paper, download the code and play with it. I haven't
included the vx32 code in the lab. Instead this lab is more tutorial
in creating a new builtin module for inferno. This labs code, linked
to in the steps below, is all the code necessary to make vx32 appear
as a builtin. I've done enough to show some simple examples working,
but I haven't defined the full system call interface.
<p>
So here are the steps in creating a new builtin module linkage.
<h2>module interface</h2>
<p>
Create the limbo module interface, e.g.
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/module/vxrun.m">/module/vxrun.m</a>.
I created the interface to closely resemble the vxrun
application in the vx32 distribution. The module contains
one function to load and run a native ELF executable.
<p>
Edit
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/module/runt.m">/module/runt.m</a>
to include new include the new module
interface. This file includes all builtin modules and is used
later to generate a runtime C struct.
<h2>incorporate library code</h2>
<p>
Copy library and header files into inferno-os tree.
I copied vx32.h to
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/include/vx32.h">/include/vx32.h</a>.
I created a new
libvx32 folder at the root of the tree and created a
dummy mkfile. I didn't copy all the source into the tree,
I cheated and just copied libvx32.a to /Linux/386/lib.
But the emu build will expect the folder and mkfile to
exist. So this is a placeholder for now.
<h2>add builtin to libinterp</h2>
<p>
Implement the builtin linkage
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/libinterp/vxrun.c">/libinterp/vxrun.c</a>
This is the bulk of the work, where we call the vx32 API
and map the system calls defined in the codelet C library
that comes with the vx32 distribution, libvxc, to inferno's
API defined in /include/kernel.h.
<p>
The template can be generated by /dis/limbo from the vxrun.m interface
<p>
<pre>
% limbo -T Vxrun /module/vxrun.m
#include <lib9.h>
#include <isa.h>
#include <interp.h>
#include "Vxrunmod.h"
void
Vxrunmodinit(void)
{
builtinmod("$Vxrun", Vxrunmodtab);
}
void
Vxrun_run(void *fp)
{
F_Vxrun_run *f = fp;
}
</pre>
<p>
Which is actually wrong, it should be,
<p>
<pre>
Vxrunmodinit(void)
{
builtinmod("$Vxrun", Vxrunmodtab, Vxrunmodlen);
}
</pre>
<p>
The hard work is the filling in the Vxrun_run() function.
A lot of this code was taken from vx32/src/vxrun/vxrun.c.
<p>
The main work of running the native code and redirecting syscalls is in the following loop,
<p>
<pre>
for (;;) {
int rc = vxproc_run(p);
if (rc < 0){
acquire();
*f->ret = -4;
return;
}
if (rc == VXTRAP_SYSCALL) {
if(dosyscall(p, f->ret))
continue;
else
break;
}
}
</pre>
<p>
In dosyscall we switch on the syscall number and call the Inferno method, e.g.,
<p>
<pre>
static int
dosyscall(vxproc *proc, int* fret)
{
...
switch (NUM) {
case VXSYSREAD:
fd = ARG1;
addr = ARG2;
len = ARG3;
if (!vxmem_checkperm(proc->mem, addr, len, VXPERM_WRITE, NULL))
print("bad arguments to read");
ret = kread(fd, (char*)m->base + addr, len);
break;
... other syscalls
}
RET = ret;
return 1;
}
</pre>
<p>
To get this to build we need to
edit the
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/libinterp/mkfile">/libinterp/mkfile</a>. Add vxrun.$O to the
list of OFILES, add vxrun.m to the list of MODULES, and add the following rules to ensure the module header,
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/libinterp/vxrunmod.h">vxrunmod.h</a>, is generated.
<pre>
vxrunmod.h:D: $MODULES
rm -f $target && limbo -t Vxrun -I../module ../module/runt.m > $target
vxrun.$O: vxrunmod.h
</pre>
<p>
We can now compile libinterp.
<h2>edit emu config</h2>
<p>
The final step is to edit
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/emu/Linux/emu">/emu/Linux/emu</a>
configuration file
and add the dependencies on the vxrun module and
the vx32 library.
We can now build a new emu that has the vx32 vxrun as
a builtin module.
<h2>test</h2>
<p>
We need a limbo command to call the module.
I included
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/vxinferno.b">vxinferno.b</a> in the lab code.
But it does nothing more than load the module and
call it passing in any command line arguments.
<p>
<pre>
init(nil:ref Draw->Context, args:list of string)
{
vxrun := load Vxrun Vxrun->PATH;
vxrun->run(tl args);
}
</pre><p>
I used the vx32-gcc to compile the native code. I included one example,
cat.c, that would test the system calls, open, read, write, from the inferno
namespace. Note that the name of the executable to call from inside
Inferno is the host pathname, because vx32 itself is not using the Inferno
system calls. This could be fixed by either changing the elf loader, or
by using the library call to load the ELF from memory.
<p>
<pre>
$ cd ~/vx32/src/vxrun
$ vxrungcc cat.c
$ emu -s -r ~/inferno-os
; vxinferno /home/caerwyn/vx32/src/vxrun/_a.out /dev/drivers
#/ root
#c cons
#e env
#M mnt
...
</pre>
<p>
<h2>conclusion</h2>
<p>
This lab confirmed vx32 as a builtin to inferno would work.
Now it needs to be implemented in full. I'd most like
to see vx32 ported to windows.
There is an effort to port
<a href="http://www.midnight-labs.org/vxwin32/">vx32 to windows</a>,
but it seems to have stalled.
<p>
I think this is really cool!
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/92/">inferno-lab/92</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com6tag:blogger.com,1999:blog-8112083.post-20599170488460451162008-12-23T21:55:00.006-05:002011-01-25T20:35:04.063-05:00lab 91 - using freetype<div id="post">
<h2>NAME</h2><p>
lab 91 - using freetype
<h2>NOTES</h2><p>
While fiddling with Charon's fonts and wondering what work would be involved to replace the whole set I decided to take a quick look at the freetype module. This lab documents some of my progress.
<p>
A recent post to the acme-sac mail-list pointed me to the <a href="http://sourceforge.net/projects/dejavu/">DejaVu fonts</a>.
They are derived from Bitstream Vera Fonts but with more characters.
It includes various styles: Sans, Serif, Italic, Oblique, Bold, and Mono,
making it a good choice for Charon. At first I considered converting
the whole set over to Inferno format.
<p>
There is a program to convert TrueType fonts to the inferno format.
But the program is designed to run on Plan 9 and I don't have a
ready Plan 9 environment anymore.
So the effort of setting up an environment,
compiling and fixing problems I know exist in the conversion tool,
creating font files for all the styles, and in a variety of sizes,
and I was ready to look for an easier solution.
<p>
The Freetype library is compiled into the inferno-os emulator and exports
a builtin limbo interface.
There are no programs in inferno-os that use Freetype.
And there is no documentation describing the limbo interface.
There have, however, been a few posts to the inferno-list describing its use.
Also, the Freetype
documentation from its source website is good. The <a href="http://freetype.sourceforge.net/freetype2/docs/tutorial/step1.html">tutorial </a> basically describes
what needs to be done within inferno to use the freetype
module.
<p>
The first example from inferno-list, <a href="http://inferno-lab.googlecode.com/svn/trunk/91/testfreetype.b">testfreetype.b</a>, shows the use of the library
for rotation and scaling.
<pre>
% cd lab 91
% limbo testfreetype.b
% testfreetype fonts/DejaVuSans.ttf 'Hello World'
</pre>
<p>In this screenshot, the Inferno logo image is the background, and the word 'freetype' is scaled and rotated above it, with some transparency.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3130584885/" title="108112312208-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3126/3130584885_ec5bffc6b2_o.png" width="300" height="231" alt="108112312208-Inferno" /></a>
<p>
The example <a href="http://inferno-lab.googlecode.com/svn/trunk/91/dbft2.b">dbft2.b</a> is a simplification of the above that demonstrates
writing a string to a window.
I took the dbft2.b code an tried to adapt it to the frame module,
a port a <a href="http://plan9.bell-labs.com/magic/man2html/2/frame">libframe</a> from Plan 9, and used by Acme in inferno-os,
<p>
First of my own demos is an application called <a href="http://inferno-lab.googlecode.com/svn/trunk/91/term.b">term</a> that accepts keyboard input
and uses frame to display the entered text
inside a window. This uses inferno fonts.
Using frame requires some setup code,
<pre>
framem = load Framem Framem->PATH;
# setup a window client
...
win := wmclient->window(ctxt, "Term", Wmclient->Appl);
...
font = Font.open(display, "/fonts/lucidasans/unicode.8.font");
textcols = array[NCOL] of ref Draw->Image;
textcols[BACK] = display.black;
textcols[HIGH] = display.color(Draw->Darkyellow);
textcols[BORD] = display.color(Draw->Yellowgreen);
textcols[TEXT] = display.color(Draw->Medgreen);
textcols[HTEXT] = display.black;
framem->init(ctxt);
frame = framem->newframe();
win.image.draw(win.image.r, textcols[BACK], nil, ZP);
framem->frclear(frame, 0);
framem->frinit(frame, win.image.r, font, win.image, textcols);
</pre><p>
Its not documented in Inferno, but see the <a href="http://plan9.bell-labs.com/magic/man2html/2/frame">Plan 9 manual page</a>.
<p>
On input from the keyboard we append it to a buffer and
pass the buffer to frame:
<pre>
c := <-w.ctxt.kbd =>
buf[len buf] = c;
framem->frinsert(frame, buf[len buf - 1:], 1, frame.p0);
</pre>
<p>
Once I got that baseline working, the next program is <a href="http://inferno-lab.googlecode.com/svn/trunk/91/frame2.b">frame2.b</a> and <a href="http://inferno-lab.googlecode.com/svn/trunk/91/term2.b">term2.b</a> that uses freetype.
In the init function I load the freetype module and load a new face.
<pre>
freetype = load Freetype Freetype->PATH;
face = freetype->newface("./fonts/DejaVuSerif-BoldItalic.ttf", 0);
face.setcharsize(20<<6, 72, 72);
glyphsimg = ctxt.display.newimage(Rect((0,0), (20,20)), Draw->GREY8, 0, Draw->Black);
</pre>
<p>
I rewrote the three functions used by frame to display strings, stringx,
charwidth, and strwidth. Stringx does the work of loading the glyph
and drawing it.
<pre>
stringx(d : ref Image, p : Point, f : ref Font, s : string, c : ref Image)
{
origin := Point(p.x<<6, (p.y+face.ascent)<<6);
for (i := 0; i < len s; i++)
{
g := face.loadglyph(s[i]);
if (g == nil){
sys->print("No glyph for char [%c]\n", s[i]);
continue;
}
drawpt := Point((origin.x>>6)+g.left, (origin.y>>6)-g.top);
r := Rect((0,0), (g.width, g.height));
r = r.addpt(drawpt);
glyphsimg.writepixels(Rect((0,0), (g.width, g.height)), g.bitmap);
d.draw(r, c, glyphsimg, (0,0));
origin.x += g.advance.x;
}
}
</pre>
<p>In this screenshot, the term application is running inside inferno-os, with some typed text. (Note, there is a error in my render of lowercase 'f', the top of the 'f' has been chopped off.)
<p>
<a href="http://www.flickr.com/photos/caerwyn/3130795231/" title="108112314543-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3107/3130795231_53b29fcf77_o.png" width="259" height="219" alt="108112314543-Inferno" /></a>
<p>
Acme uses frame so we can quickly experiment with using
a TTF file for the Acme font. The above three functions
are in another module in the acme source called graph.b.
I've included a <a href="http://inferno-lab.googlecode.com/svn/trunk/91/graph2.b">graph2.b</a> file in this lab that can be
bound over the existing graph.dis and will load a hardcoded
TTF file for the font. Below is a screenshot of acme running
the DejaVuSans.ttf font.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3131052552/" title="108112392028-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3248/3131052552_6254663a69_o.png" width="646" height="505" alt="108112392028-Inferno" /></a>
<p>
Now back to my original aim: changing the charon fonts.
I created a module that defined a <a href="http://inferno-lab.googlecode.com/svn/trunk/91/ftfont.m">new Font ADT</a> to replace
the one defined in the draw module. I changed the interface
slightly, to include the freetype face and added the stringx
function.
<p>
All the code for drawing text in charon is in layout.b.
In this labs code I've included a <a href="http://inferno-lab.googlecode.com/svn/trunk/91/layout.b">replacement </a>that uses
the freetype Font adt. I changed all calls to Image.text()
to call a local function that itself calls Font.stringx().
Layout.b locally defines a Fontinfo that specifies all the
font files and sizes. I modified it to point to all the DejuVu
fonts with sizes as appropriate.
To run this yourself inside inferno-os you'll need to extract
the DejuVu ttf files directly under /fonts, then,
<pre>
% cd /appl/charon
% bind -bc '/n/local/inferno-lab/91' .
% limbo layout.b
% limbo ftfont.b
% bind layout.dis /dis/charon/layout.dis
% charon&
</pre>
<p>
And here is an screenshot of the results.
<p>
<a href="http://www.flickr.com/photos/caerwyn/3132353638/" title="1081123203852-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3101/3132353638_8e66746e8d_o.png" width="466" height="453" alt="1081123203852-Inferno" /></a>
<p>
I haven't looked at converting Tk.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/91/">lab/91</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com1tag:blogger.com,1999:blog-8112083.post-6544379016393963162008-12-04T21:48:00.004-05:002011-01-25T20:51:31.676-05:00lab 89 - electroquongton<div id="post">
<h2>NAME</h2><p>
lab 89 - electroquongton
<h2>NOTES</h2><p>
The code for this lab was something I was playing with to display on a Nintendo DS. It uses the mux window manager and the prefab module builtin. Because of the dependency on the builtin, which isn't usually part of the inferno-os standard emu build, I've included a muxemu.exe for Windows in this labs code. To launch the code run the following,
<pre>
% muxemu -r . -g256x384 /dis/mux/mux.dis
</pre>
<p>
and on the screen you should see this,
<p>
<a href="http://www.flickr.com/photos/caerwyn/2797968741/" title="10872522390-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3254/2797968741_5021c759bc.jpg" width="262" height="409" alt="10872522390-Inferno" /></a>
<p>
Move up and down using keys 'i' and 'm'. Enter a selection using the 'Enter' key and return to the main menu by pressing the spacebar.
<p>
I'm not experienced with the draw(2) API so I started with screens for board games.
<p>
<a href="http://www.flickr.com/photos/caerwyn/2661878066/" title="10861214276-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3174/2661878066_05e733b39f_o.png" width="262" height="217" alt="10861214276-Inferno" /></a>
<p>
I was experimenting using transparency effects. The look I was going for was Electroplankton for Nintendo DS.
<p>
<img src="http://dsmedia.ign.com/ds/image/article/602/602925/electroplankton-20050408034949221.jpg"/>
<p>
I tried to get shapes with edges that blended out into a graded background
and some simple animations of shapes pulsing.
<p>
<a href="http://www.flickr.com/photos/caerwyn/2798075737/" title="108725231623-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3285/2798075737_acc9529806_o.png" width="262" height="409" alt="108725231623-Inferno" /></a>
<p>
The one serious application I was trying to write was a <a href="http://www.strout.net/info/ideas/hexinput.html">QUONG keyboard</a> for convenient touch input.
<p>
<a href="http://www.flickr.com/photos/caerwyn/2793920589/" title="10872420332-Inferno by caerwyn, on Flickr"><img src="http://farm4.static.flickr.com/3067/2793920589_33afecf8cd_o.png" width="262" height="409" alt="10872420332-Inferno" /></a>
<p>
This application responds to the mouse to enter letters. The top half of the windows is an implementation of Plan 9's libframe. It was part of Inferno's acme implementation and I extracted it from acme's dependencies. If you take anything away from this lab it would probably be this one library, maybe to build an inferno based 9term.
<p>
A big disappointment is that these didn't actually work on the inferno-ds due to what I guess is a bug in the handling of graphics with masks. They did seem to work on the DS emulators, but slowly. Another difficulty is writing them so that they fit in the DS memory, the techniques of which I'm completely ignorant because I'm used to the great spaciousness of modern desktops.
<h2>FILES</h2><p>
<a href="http://inferno-lab.googlecode.com/svn/trunk/89/">inferno-lab/89</a>
</div>caerwynhttp://www.blogger.com/profile/12819704983814002831noreply@blogger.com0tag:blogger.com,1999:blog-8112083.post-81948971358265362752008-11-10T11:32:00.014-05:002008-11-12T15:19:37.324-05:00lab 90 - Multicast DNS and Zeroconf<div id="post"><blockquote></blockquote><blockquote></blockquote><blockquote></blockquote><h2>NAME</h2><p>lab 90 - Multicast DNS and Zeroconf (client portion)
</p><h2>NOTES</h2><p>While using synthetic file systems to publish services works great, you still need to know where your server is. This information can be provided from a shared ndb/local or even DHCP, but there are plenty of scenarios I have run into where I don't control the DHCP server and distributing ndb/local is tedious. In working with Blue Gene the problem becomes a bit worse in that we don't know apriori which portion of the machine (and therefore which IP address) we will get. Further complicating this is the fact that our front-end node (where we run Inferno) is established by a load balancer, and there are potentially 5 of us running our own file servers. So not only do we need to know how to get to the front end node, but how to get to the right front-end node. </p><p>While some form of simple broadcast service discovery may have been sufficient, I decided to take the time to see what it would take to add multicast DNS and service-discovery to the Plan 9 and Inferno DNS services. Multicast DNS and Service Discovery (aka Bonjour, aka Rendezvous, aka Zeroconf) is documented in plenty of places, here's a few good starting points for more information:
</p><p></p><ul><li><a href="http://en.wikipedia.org/wiki/Zeroconf">Wikipedia</a></li><li><a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">The Service Discovery RFC</a></li><li><a href="http://www.amazon.com/exec/obidos/redirect?path=ASIN%2F0596101007&link_code=as2&camp=1789&tag=zeroconfigurn-20&creative=9325">Zeroconf: The Definitive Guide</a></li></ul><p></p><p>Multicast DNS resolution is fairly straightforward, it involves just sending the DNS request to a multicast address (224.0.0.251) and using port 5353 instead of port 53. The first thing I did was modify /appl/cmd/ndb/dns.b to use this address and port when looking for any domain ending in .local per the zeroconf convention.</p><p>Service discovery is a bit more problematic. It involves sending a slightly different type of request. Typical DNS requests use an type A style request which retrieves an IP address for a hostname. Service discovery uses a PTR style request which returns three types of records -- the PTR record contains the instance of the service which will include a more specific name of the service location, a SRV record which will contain the port number of the service, a TXT record which contains some protocol specific information and an A response which contains the IP address.</p><p>So, for example, if I send a PTR request for _presence._tcp.local, I get four records in response:</p><p></p><ol><li>ptr record -> crazyjim@arl137._presence._tcp.local
</li><li>srv record -> port: 5298</li><li>txt record -> last=Peterson 1st=James msg=Away status=dnd</li><li>A record -> 9.3.61.137</li></ol><p></p><p>I added a flag to ndb/dnsrequest (-z) which forces sending a PTR request to DNS. Using the Plan 9 DNS service as a model, I modified the Inferno dns to be able to parse SRV and TXT records. The one things I changed was that the TXT response can contain several key value pairs. The Plan 9 DNS service just strings these together (with no seperator mind you). So I print the number of key value pairs, and then have the key=value pairs one per line prefixed with dual tabs to make it look nice.</p><p>The final remaining problem is that the current DNS daemon only returns a single response for each request, but multicast DNS may have several responses for a single request. This involves a much larger set of changes to dns.b. DNS will now accumulate responses and return them in one big set.</p><p>I probably need to do some work to get cs.b to play nice with such information. The whole dnsquery, and even the cs and ndb front-ends to it seem decidedly anti-Plan 9 in their layout -- particularly for something with lots of rich attribute information like zeroconf. As such I'm likely going to create a synthetic file server with which to browse zeroconf data. You register which types of zeroconf entities you are interested in by creating directories in a two level hierarchy -- the file server will then use my modified DNS to query the local net and will create nodes under those directories for responses with attribute information broken out into individual files. The other major thing that needs to be done is adding multicast server support to dns.b. But I think I'll write these up in a different lab entries as this one is getting long in the tooth already.</p><p>While this is sufficient to solve my initial problems, there are several additional aspects of zeroconf which might be nice to integrate for Inferno including support for link-local IP addresses (which I guess would only be important on native), proper uniqueness handling for claiming your local names, NAT-PMP support, DNS-LLQ support, and dynamic DNS updates.</p><h2>EXAMPLE</h2><pre>% ./dnsquery -z _presence._tcp.local
_presence._tcp.local ptr ericvh@ericvh-desktop._presence._tcp.local
ericvh@ericvh-desktop._presence._tcp.local txt 10
txtvers=1
1st=Eric
last=Van Hensbergen
port.p2pj=5298
status=away
node=libpurple
ver=2.5.2
vc=!
email=bergevan@us.ibm.com
phsh=943420112a8b192466a802bedfe547041a62ea90
ericvh@ericvh-desktop._presence._tcp.local srv 0 0 5298 ericvh-desktop.local
ericvh-desktop.local ipv6
ericvh-desktop.local ip 9.3.61.77
npe@macintosh-16._presence._tcp.local srv 0 0 5298 macintosh-16.local
npe@macintosh-16._presence._tcp.local txt 13
ext=
phsh=f308675309a23fa653c269c95f57eb7eb84efc44
last=
AIM=
nick=
1st=Noah
port.p2pj=5298
txtvers=1
version=1
node=
jid=
email=
status=avail
macintosh-16.local ipv6
macintosh-16.local ip 9.3.61.73
_presence._tcp.local ptr npe@macintosh-16._presence._tcp.local</pre><h2>DISCUSSION</h2><p>dns.b seems entirely too big, and I just made it bigger. It would seem better served if it were split up into a bunch of component modules. It seems like the marshalling and unmarshalling of DNS messages is a legitimate module, the cache is a module, local database/config access is another, and then a proper module interface for performing DNS queries and/or servicing DNS requests. The file and network servers could then be provided relatively cleanly. All in all it would clean the code up signifigantly and make the whole thing a lot more readable/extensible. Things like the registry or even cs could easily be implemented as plug-in modules versus discreet file servers (although also allowing them to use file services is desirable in certain scenarios so this should definitely be parameterized).
</p><p>Its funny, looking back at Virgild, it was essentially a broadcast form of multicast name resolution, just with its own more simple protocol instead of DNS.</p><h2>FILES</h2><p></p><blockquote></blockquote><ul><li>lab90/appl/cmd/ndb/dns.b
</li><li>lab90/appl/cmd/ndb/dnsquery.b
</li></ul><p></p><p>
</p></div>Unknownnoreply@blogger.com2