Smalltalk Development on XO

From OLPC
Jump to: navigation, search

General Introduction

The Etoys activity installed on XO is written in the Squeak Smalltalk system. Etoys pretends to be an application for end-users, but it is just a kids' playground guarded by soft fences, so to speak. Once you make a hole on the fences and get outside, you will have access to a full-fledged, general purpose, multimedia ready, integrated development environment. One of unique aspects of Etoys is that all files that the core developers of the activity uses are shipped with XO; this means that even if the only computer you have is the XO, you can develop as much as any of the core developers do with Etoys, and if you prove that you are good, you can be a core developer of the default activity on XO. Sounds exciting?


On this page, I'll explain how to disable the fences, write code, save your changes and share it.

Set up Your Files

First, you copy two files from /usr/share/etoys to a location where the user "olpc" can write. Follow the steps described below:

Do either one of following two options (2.1 "Copy Files to /media" or 2.2 "Use USB memory"). If you don't have a USB memory handy or you would like to do it without one, do 2.1. Otherwise, do 2.2.

Copy Files to /media

Open the "Console" activity. and execute the following commands:

 % su
 # mkdir -p /media/foo/olpc-dev
 # chown olpc:olpc /media/foo/olpc-dev
 # chmod 777 /media/foo/olpc-dev
 # cd /usr/lib/squeak/3*
 # ln -s ../../../share/etoys/EtoysV3.sources
 # exit
 % cp /usr/share/etoys/etoys.{image,changes} /media/foo/olpc-dev
 % chmod 666 /media/foo/olpc-dev/etoys*


Use USB memory

  • Plug your USB memory into a slot, start the "Console" activity and execute the following:
 % cd /media/*
 % mkdir olpc-dev
 % cp /usr/share/etoys/etoys.{image,changes} olpc-dev
 % ln -s /usr/share/etoys/EtoysV3.sources olpc-dev
 % chmod 666 olpc-dev/etoys.*

(In the first option, you could make the symbolic link to /usr/share/etoys/EtoysV3.sources from the location where your etoys.image will reside. In a future version, we would include the symbolic link from /usr/lib/squeak/3.9-12 or such automatically.)

Note that your USB memory must be formatted with a filesystem that can support symbolic links, such as ext2 or ext3. Also, the mountpoint must be writable by user "olpc", or else the Journal activity will not recognize the external storage. (Journal will attempt to create a directory .olpc.store in the root of the storage device to host its metadata. Once this is created with the proper permissions, perhaps the mountpoint could revert to ownership by root.) If your USB memory is not formatted to support symbolic links and there is enough space on the USB memory device, you can just copy the EtoysV3.sources file instead of making a symbolic link.

As you might see, the purpose of this step is to make a copy of etoys.image and etoys.changes and put them under /media/vol-name/olpc-dev, make the directory writable, and put writable copies of etoys.image and etoys.changes in the directory.

If you can read a shell script, take a look at /usr/bin/etoys. The ALTIMG variable specifies where to look for alternate .image. You could edit the shell script and specify your own .image file anywhere in the file system.

Start Up Etoys and Change Preferences

Now, bring up the Sugar frame (either going to the donut view or press the frame button), and click on the shooting star to launch Etoys. Since the .image is an exact copy of the default one so far, you might not see whether your image is launched or not. First, you need to know how to break in the fences put around Etoys. What we would like to do is to turn off "eToyFriendly" preference and set up a few other things.

Show Source
Preferences Panel
The World Menu
  1. Press, Alt-, (i.e., hold the Alt key first, and while you are holding it, hit "," key and then release Alt key) or (if we fix a bug) do the "show source" gesture. You'll get a menu that looks like Figure "Show Source".
  2. From the menu, choose 'help...' and 'preferences...'. Depending on the language setting, these menu items may be translated. But you should be able to figure them out. You get a Preference Panel that looks like Figure "Preferences Panel".# In the Preference Panel, click on the button labeled "scripting", and uncheck "eToyFriendly" box. This may take 10 seconds or so; be patient.
  3. After it is done, now you can bring up the vital "World menu" by just clicking on the empty area of screen. The World menu looks like Figure "The World Menu".
  4. Now, try to choose "save" (fifth from the bottom). If the .image you are on is indeed the writable one, it will update the .image and .changes files on the disk. If you do not see a pink small dialog pops up but the cursor changes to a pen-shape momentarily, the files are successfully saved. If you see a pink dialog, go back to the previous section and make sure you are doing it right. (If you know how to get a Workspace, try to evaluate expressions like "Smalltalk imagePath".)
  5. For what we are going to do (write your own code), having a car running around in front of you may be disturbing (either a virtual car or real car). Get the World menu again, and choose "previous project" at the top. You'll be taken to an empty project, which is more suitable to do the following and more. Save your image again after that.

There are a few other preferences you might want to change. One is "swapControlAndAltKeys"; this is "on" by default so that basic code editing short cut keys use the ctrl key. You could set this to off to be compatible with other platforms, but there is a catch. Sugar intercepts some of the Alt-key combinations so this can be very dangerous. For example, when you press Alt-c to mean to copy text, Sugar interprets it as quit and kills your Squeak session. Another preference is "swapMouseButtons" preference. During development, you would rather use the right mouse button to bring up context menus rather than the halo. (If you turn it on, do Alt-click on a widget to get the halo.)

Assuming you can save your image file, this is a good time to "fetch updates" into your image. The core developers make the changes to the official image in a form of downloadable files (which is explained below) and if your XO is connected to the Internet, you can fetch them. To do so, get the World menu, click on "help...", and choose "update code from server". If yours is not connected to the Internet, that is okay, just move on.

Here, Let us do "save and quit" from the World menu to save the current Squeak session and have a cup of coffee.

Resuming Session

To resume a previously-saved session, click on the shooting star icon in the Sugar frame. The exact environment you saved will be resumed. (But there is a bug that takes you to the Launcher project. A later version of standard image doesn't do it if eToyFriendly is off.) Yes, Sugar's idea of continuous running activities you only suspend and resume have a root in this Smalltalk's idea. However, beware of a difference; Squeak does not save its image automatically upon exit. You even have an option to say "quit", to mean "quit without save".

You can also copy your .image and .changes pair to a non-XO environment (Windows, Mac, Linux or two dozen of other different kinds of platforms) and resume the session on it. The screen extent may be different on a different computer but otherwise the exact state, including all running processes comes back.

From the Console activity, you can launch a Squeak session by executing:

 % squeak your-favorite.image

(There are some command line options you might want to provide. see /usr/bin/etoys if you want to follow what it does.)

You could also write a one-liner shell script to add these options. (Yes, in fact, we should change /usr/bin/etoys so that when an argument .image is specified, it uses that image.)

Basic Programming Tools

Describing the full details of Smalltalk development techniques is not the scope of this page, but I will describe some basics.

From the 'open...' menu in the World menu, you can access the most important tools. Transcript ('transcript (t)') is always handy. Get one, resize it to a fairly small size (a height enough for 5 lines or such is usually ok). Equally handy is a Workspace. Get one from 'workspace (k)' menu item. In there, type "3 + 4", put the text insertion cursor on the line, get the context menu (by right-clicking on the pane, or if swapMouseButtons is not set, click on a square button above the vertical scroll bar), and choose "print it (p)". It executes the expression and prints the result. (See Figure "Workspace and Transcript".)

Workspace and Transcript

The main programming tool in Squeak is "System Browser", which is available from 'browser (b)' menu item in 'open...'. (You rarely need to instantiate a Browser from the menu. Because you have one or any other text pane opened, you can instantiate more from it with keyboard short cuts.) (See Figure "System Browser".)

System Browser

In the Browser, let us define a subclass of "RectangleMorph" called "MyMorph". (Squeak's default GUI framework is called "Morphic". And, graphical entities on screen are subinstances of Morph.) Get a context menu in the top-left pane. From the menu, choose "find class... (f)" and type, "Rectan" or such and hit the enter key. From the list, choose "RectangleMorph" (Figure "Find a class").

Find a class

You might want to resize the Browser. In the second from bottom pane, you see text that looks like:

 BorderedMorph subclass: #RectangleMorph
       instanceVariableNames: 
       classVariableNames: 
       poolDictionaries: 
       category: 'Morphic-Basic'

Replace it with

 RectangleMorph subclass: #MyMorph
       instanceVariableNames: 
       classVariableNames: 
       poolDictionaries: 
       category: 'Morphic-Basic'

Get the context menu in the pane and choose "accept (s)". (See Figure "Defining a class".)

Defining a class

Notice that the red borders of the pane go away, and an item "MyMorph" shows up in the second pane from the left. You successfully created a class in Squeak.

Let us define a method. Click on the "-- all--" line in the third pane. The bottom pane will show the method template (See Figure "Method Template").

   message selector and argument names
       "comment stating purpose of message"
       
       | temporary variable names |
       statements
Method Template

Here, you just replace it with something like:

   initialize
       super initialize.
       self color: Color green.

and choose "accept (s)" from the context menu. Now, you define the initialize method for a instance of the class. (See Figure "Defining a Method".)

Defining a Method

Go to the Workspace you might have already opened, and type:

   m := MyMorph new.
   m openInHand.

Select these two lines, get the context menu for the workspace and choose "do it (d)" or "print it (p)", you get a green rectangle attached to the mouse cursor. Drop it somewhere visible. (Figure "Evaluation in Workspace".)

Evaluation in Workspace

In the workspace, the instance on the screen is bound to a workspace variable "m". Evaluate lines like:

   m color: Color red.
   m width: 100.
   m height: 100.
   m borderWidth: 10.

etc. You can select and "do it" each line at a time, or select many lines and execute them like a method.

Similar things can be done from an "inspector". Alt-click on the instance of MyMorph on screen to get halo. With "eToyFriendly" preference off, you have an extra handle that looks like a wrench (See Figure "Debug Handle".)

Debug Handle

Click on it, and choose "inspect morph" from the menu you get. On the left, there is a list of instance variables including inherited ones. If you select one, the printed representation of the value bound to the instance variable is shown on the right. You can edit there, and accept it. A value created from the string is stored into the instance variable. The bottom pane can be used as a Workspace, except the fact that its "context" is set to the MyMorph instance. Namely, 'self' refers to the instance, and all instance variable names are usable to use the current values in them.

For doing a fun experiment, let's add an instance variable to MyMorph. Go back to Browser, and click on the "instance" button below the second pane. You'll go back to the "class definition" of MyMorph that looks like:

 RectangleMorph subclass: #MyMorph
       instanceVariableNames: 
       classVariableNames: 
       poolDictionaries: 
       category: 'Morphic-Basic'

Put the text selection cursor in between the single quotes after instanceVariableNames:, and type in "timeLimit", and accept. The red-borders goes away again, and the class definition looks like:

 RectangleMorph subclass: #MyMorph
       instanceVariableNames: 'timeLimit'
       classVariableNames: 
       poolDictionaries: 
       category: 'Morphic-Basic'

(Figure "Adding an instance variable".)

Adding an instance variable

Notice also that the instance variable list in the inspector is updated and now you have "timeLimit" variable in the list at the bottom. Choose it and set a value like 20 in the right pane, and accept it. (Figure "Set value to instance variable from inspector".)

Set value to instance variable from inspector

Go back to the Browser, click on "-- all --" again. Replace the method template with:

  step
     timeLimit := timeLimit - 1.
     self width: 100 / timeLimit.

"step" is an API to make the method execute at a regular interval. To start it, click on the instance of MyMorph, and drop it again. This triggers the World to recognize the dropped morph expects step to be called. Look at the value in the inspector, and see it decrease by one at every second. Also, the width of MyMorph instance changes.

20 seconds later, timeLimit becomes zero, and in the step method, you will get an error because you divide a number by zero. What happens? You will see a small pink window called notifier that shows the list of stack frames that ends up with the error. (Figure "Error Notifier".)

Error Notifier

Click on the second from top that reads "MyMorph>>step" and you get a bigger window that looks like Figure "Debugger".

Debugger

The part "/ timeLimit" is highlighted. That is the message currently executing in the frame.

The wonderful thing about Smalltalk is that the debugger and code is "live". An error happened because we are silly enough to divide a number by zero. Perhaps division is not the right thing. Edit the method shown "in" the debugger, so that it reads:

      step
         timeLimit := timeLimit - 1.
         self width: 100 - timeLimit.

and accept it right there. The system keep all of the stack frames up to this method invocation and restart the accepted method right from there. Click on the "proceed" button in the debugger, and pick up and drop the rectangle again. The system is fine with such on-the-fly editing. (Figure "Fixing code in the Debugger".)

Fixing code in the Debugger

If you want to look at what you did, go back to to the Browser and while the definition of step is shown, click on the "versions" button. You see the history of all of the methods, which is kept in the .changes file.

Save your .image whenever you think it makes sense.

Troubleshooting

A few life savers are good to know. If you happen to get into an infinite loop and the system seems to be locked up, hit Alt-. (i.e., hold down the Alt-key, and hit the period key). You can interrupt the process.

Inevitably, you may end up with breaking the system, by redefining or removing some crucial method or system object. In that case, the system doesn't respond anymore and you cannot even save the image. Is all your work since last save is lost? Yes and no. At least, all method changes, class definition changes, and do it are logged into the .changes file, and the tools under "changes..." menu, such as "recent logged changes..." can bring them back.

See also the Etoys_Tips_and_Tricks page for... well... tips and tricks.

Submit your changes

After writing a fun program, you probably would want to share what you wrote. In the "changes..." and "open..." menu, there is a tool called Change Sorter (Figure "Change Sorter".)

Change Sorter

Get the context menu for the top-left pane, and choose rename. Give a proper name to your changes (here, let us say it is "myChanges"). Then get the context menu again and click on "file out (o)". The textual representation of your work is written to disk. The file name will be "myChanges.cs". Email myChanges.cs to etoys-dev@squeakland.org, or attach them to a tracker ticket (http://tracker.squeakland.org/). The core developers are using the exact same mechanism. The "official" changes are uploaded to http://etoys.squeak.org/updates and all developers can fetch them via "update code from server" in "help..." menu. There are some styles in code, organization of methods and classes, and description (preamble and postscript) of changesets. If you are interested in, browse these files on the web and also fetch the real developers version of image from http://etoys.squeak.org/download

Final Words

I didn't mention the features of Browser that let you navigate through the interrelated methods in a way you navigate through hyperlinked text. I didn't mention about the profiler, nor all the protocols of the Morphic GUI framework, nor various ways of finding a method that would do what you want (try method finder in the "open..." menu.). Please explore and make great and fun applications, enhancements, and share them with us. Please send questions to the etoys mailing list.

External Resources

http://squeakbyexample.org/ Here you will find a great book about programming with Squeak.