Last updated at 10:22 pm UTC on 12 July 2007
Nebraska is a toolkit for building remote interactions with Morphic in Squeak. It is modelled somewhat after Kansas, although as yet it's a much simpler system. There is a shared world on some server on a network, and other people can connect to that world from afar. Whenever the shared world tries to draw to its screen, it sends drawing commands across the network. Whenever a user tries to perform a mouse or keyboard interaction, those interactions are forwarded to the server where a RemoteControlledHand acts on their behalf.
The status is that you can do useful things and even develop code remotely. The performance is reasonable over a local T1 connection (I haven't actually tested it otherwise....) However, some things don't work. Many things still draw to the server's world instead of the shared world. And I haven't even touched 3D.
Nebraska was originally written by Lex Spoon, and has since been greatly updated by BobArning. In particular, there is now much better support for bitmap-based graphics. If you downlaod Squeak 3.0 or newer, then you'll have Nebraska. Have fun!
The Nebraska UI
The user interface to Nebraska is fairly simple. First, decide which machine is going to be the "server". Server in this case means the machine whose world will be shared with the client(s).
- The server must have a known IP address and some firewalls may prevent it from receiving client connections.
- The connection should be reasonably fast since there can be large amounts of data transferred. For maximum performance, keep the server's world simple. Large bitmaps will take longer than simple text windows, e.g. This may not matter if you are connected via ethernet, but over a dial-up, it could be a big issue.
On the server machine, hit the "Share" button on the orange navigator strip. A small morph will appear in the upper left of the display. This has controls for ending the Nebraska session (the X button) plus some information about the connected clients.
On each client machine, you will need a "badge" representing the server. If you don't already have one available (you probably don't), then get an EToySenderMorph from the new morph/experimental menu. This pink thing is often referred to as a badge and is the central point for many of the collaborative facilities in Squeak. Click on the area of the badge containing the IP address. Put the IP address for the server in the resulting dialog and hit Accept. On the bottom of the badge is a row of small colored buttons. The "S" button on the right end of this row is the button to connect you to the world represented by the badge. Press it now. The first thing you will see is a rectangle with an orange border. The server's world will appear within this border as soon as the data is received. You can resize the orange frame using the yellow resize handle from the halo (cmd-click or alt-click on the frame). There is also a small yellow palette of buttons floating above the server display.
- The "1x1" button will immediately enlarge the frame to show the server's world at full size.
- The "Quit" button will disconnect you from the server
- The "B" button will change the style of connection to Nebraska. The normal connection is to send every drawing command on the server to the client. Sometimes (servers with very active displays and/or slow connections) this means that the client will fall further and further behind the server. If that is the case, pressing the "B" button will switch to a buffered connection. In this mode, the server sends an entire copy of its display, but only when previous data has been sent. This means that the cllient will always be current to within the time required to send a full screen.
If the server has a badge representing the client, then the client's name and/or picture will be associated with the client's cursor. If not, '???' will be used.
Trying it out
Boris Gaertner March 10, 2005 The correct way to create an instance of NebraskaServer is either
NebraskaServer serveWorld: World
NebraskaServer serveWorld: World onPort:
These initialize the instance, but a simple NebraskaServer new does not. To open a NebraskaServerMorph, it is seemingly sufficient to write:
NebraskaServerMorph serveWorld: World onPort: 9091.
This creates a server, registers it with the current world and opens the morph. The morph in turn obtains the server from the current world. A bit tricky, but it works.
Blake These all DNU in the exact same place:
world extent: newExtent.
BFAV2 Release 2.30 Nebraska should be in the image allready. Open the navigator flap and click on 'share' and the current project is shared over the network. Note that the SqueakLogo which is a FlashMorph will give a error if present. Get rid of the logo
or open a new project and share that.
Blake OK, so importing the Nebraska from the swiki page broke the installed Nebraska.I've often wondered about the "Share" thing. It works in a clean 3.7 install. And, if I comprehend correctly, it sets up a Squeak as a server
on 9091. The code for connecting programmatically seems to work. Cool.
NOTE: if you want to have a local view of the server, you shouldn't use the TCP connections. The problem is that the server will occasionally do a #flush, and it won't work due to single threading. The better solution is to use a LoopBackStringSocket instead of a regular StringSocket, but there is no handy method for that right now....
Playing with Nebraska (Lex Spoon)
Here are some things you can glue together, once you have the efficient remote interaction Nebraska gives you.
- You can give people flaps that live on the local machine, and let them drag and drop things into and out of the shared world.
- Conversely, you can swap modes and have the shared stuff be in a flap, for times when you are primarily working locally but want to have a collaboration window available.
- You can have a large shared world that is scrollable, like the original Kansas (in fact, the reason Kansas got its name). This way multiple people can really bump elbows. :)
- You could set it up to display only, and thus have a very efficient format for Squeak movies or Squeak streaming presentations.
There are also other nice things that could be done:
- NetworkTerminalMorph should have a border, so that you can interact with it on the client.
- The password-handling code I posted a while ago should be integrated into the main system.
Have a blast! Nebraska is just one of a load of components in Squeak
that you can combine in lots of interesting ways. A little bit of
programming can take you a long way, if you are programming in Squeak.
Accessing the Shared World Progammatically
You can access the shared world with Smalltalk code on the server. Just do "server sharedWorld". For example, you can use this to add some morph you want to edit to the world:
| server |server := NebraskaServer new.
server startListeningOnPort: 9091.
server sharedWorld addMorph: (some morph).
(NebraskaServerMorph new server: server) openInWorld.
Changing the Size of the Shared World
The proper way to change the size of the shared world is as follows:
server extent: 600@400 depth: 32The server will broadcast the change to all connected clients.
The basic system provided here isn't the only possible scenario this toolkit can be used for. For example, you may wish to make Nebraska the main UI for the system and allow project switches inside of the shared world. You may wish to make a client image which automatically connects to some server. There are lots of possibilities for the components herein.
StringSocket's are wrappers around regular sockets that allow sending arrays of strings. I have found this more convenient that dealing with raw binary streams. The encoders and decoders use StringSocket's instead of talking to Socket's directly.
CanvasEncoder accepts canvas commands, encodes them into string arrays and forwards them over an underlying string socket. CanvasDecoder does the opposite, decoding string arrays and drawing onto an actual canvas.
There are two commands other than drawing commands that CanvasEncoder/Decoders work with. First, they handle screen resizes. Second, they handle "force to screen" commands.
A RemoteCanvas wraps a CanvasEncoder, and has a clipping rectangle and a transformation associated with it. Whenever you draw to a RemoteCanvas, it will first establish the clipping rectangle and transformation, and only then will it forward the actual drawing commands. The purpose of RemoteCanvas, instead of just having CanvasEncoder, is to allow multiple clipping rectangles and transformations to be live at the same time. However, clip rectangles and transformations don't work perfectly at the moment, so this part of the system might change....
These classes encode and decode MorphicEvents, similar to CanvasEncoder/Decoder.
This is similar to RemoteHandMorph. It accepts events from a MorphicEventDecoder, and acts on those events.
A NetworkTerminalMorph is a simple morph which accepts drawing commands from a CanvasDecoder, and which forwards all events sent to it to a MorphicEventEncoder.
This interface is extremely simplistic right now, but it shows how the system can be used....
NebraskaPasteUpMorph is a variant of PasteUpMorph which has an additional set of remote canvases. The most notable difference from a regular PasteUpMorph is that #assuredCanvas builds a canvas out of these remote canvases, instead of building a FormCanvas on the Display.
(Nowadays, regular PasteUpMorphs are used. Lex hasn't worked out the exact mechanism that is used nowadays, but the result is that you can share a regular PasteUpMorph – including the current World !)
NebraskaServer and NebraskaServerMorph
These are the heart of the server setup described above. The server does basic things like accepting new connections and updating the underlying NebraskaPasteUpMorph whenever the set of clients changes. The ServerMorph at this time mostly just step's the server periodically.
Other Kinds of Canvases
There are several kinds of canvases made for this system along the way.
- MultiCanvas: forwards canvas commands to multiple underlying canvases
- CachingCanvas: maintains a cache of the screen in a local Form, so that contentsOfArea: can be quick even if the main canvas is a remote one.
- NullCanvas: does nothing. great for benchmarking.
- ClippingCanvas: wraps a canvas and clips the underlying canvas. This is just around so that I don't have to give a clipping rectangle to all of the above kinds of canvases.
- PluggableCanvas is an abstract superclass of several of the above. It's useful when all canvas commands should be modified in a uniform way. (eg, all canvas commands should be executed by doing a clip, and then running the original command)