Ancient porting notes
From HalyardWiki
These notes are for developers upgrading old programs to modern versions of Halyard. Note that after 0.5.0, we're actually committing to greater API stability than we have in the past. For newer porting notes, see Porting notes.
[edit] Updating from 0.4.7 to 0.5.0
We have now implemented most of the new syntax for templates and cards, and are now porting 5L/test and the IML libraries to the new system. There are many issues to keep track of when porting to the new system, so this document is intended as a reference or checklist that can be referred to in order to get everything ported smoothly.
[edit] Renames
There are several things that have been renamed, in many cases to avoid conflict with names introduced by the new system. It is usually best to do all of the renames up front, so when you do the porting later, you don't accidentally call the old names, which can be fairly confusing to fix.
A good way to do global renames is with the following Perl command, which will do a global search over all *.ss files and run a regular expression substitution over them. Of course, you need to make sure that the regexp expresses exactly the transformation you wish, without accidentally catching other names that might include the name you with to transform. It is generally good to do this from a clean Subversion working copy, check the diffs to make sure you didn't make any mistakes, and then do a commit.
find . -name "*.ss" | xargs perl -pi.bak -e 's/\((black|white|default)-background\)/\(draw-\1-background\)/g'
If the change only applies to in particular cases (like, on %movie%) then you need to decide if it's best to do a global search and replace and then reject the diffs that you don't want using TortoiseSVN, or if it's best to just search and replace each one manually.
[edit] List of renamed symbols
| Old name | New name | Class |
|---|---|---|
(send obj name ...)
| (obj .name ...)
| |
(send self name ...)
| (.name ...)
| |
(prop obj name)
| (obj .name)
| |
(prop self name)
| (.name)
| |
(call-next-handler)
| (super)
| |
set-volume!
| set-channel-volume!
| %movie%
|
polygon-bounds
| polygon-bounding-box
| |
bounds
| bounding-box
| |
bounds
| bounding-box
| %element%
|
bounds
| drag-bounds
| %simple-draggable-object
|
attr-value
| value
| |
attr-default
| default
| |
box
| new-box
| |
clickable-zone
| new-clickable-zone
| |
text-box
| new-text-box
| |
text
| new-text
| |
graphic
| new-graphic
| |
rectangle
| new-rectangle
| |
rectangle-outline
| new-rectangle-outline
| |
sprite
| new-sprite
| |
browser
| new-browser
| |
edit-box
| new-edit-box
| |
geiger-audio
| new-geiger-audio
| |
geiger-synth
| new-geiger-synth
| |
sine-wave
| new-sine-wave
| |
vorbis-audio
| new-vorbis-audio
| |
movie
| new-movie
| |
button-clicked event
| click
| %basic-button%
|
[edit] Porting from templates to classes
The next step will be to port from the old template syntax to the new class syntax. All of the old templates will just be ported to define-class, with an explicit superclass. This process has to be manual, since there are several places where you need to make judgment calls, minor redesigns, etc. Here are the steps you need to take:
- Change
define-type-templatetodefine-class. - Move the superclass up to right after the name of the class, without any of the initialization list.
- If there is no superclass listed, add the appropriate superclass (%custom-element%, %card%, %group%, or %sequence%).
- Change each of the
:name valueinitializers (that previously followed the superclass) to a(value name value), or a(default ...)if appropriate. - Change property declarations to
attrdeclarations (ordefaultif they were just specifying a:new-default).- Change the square brackets to parens and add in the
attr. - If there is a
:default, move the expression for the default to right after the name of the attr, and remove the:defaultkeyword. - Example:
[[foo :type <string> :default "Hello" :label "Something"]]becomes(attr foo "Hello" :type <string> :label "Something")
- Change the square brackets to parens and add in the
- Change all
(on name (...) ...)definitions into(def (name ...) ...)-
idleno longer takes an event parameter. Thus, if you used to have(on idle (event) ...), it should now be(def (idle) ...). -
call-next-handleris now calledsuper
-
-
createis now gone. There are a few different ways you'll need to translate these, depending the circumstances:- Change any
(create %class% ...)statements that you can into(elem name (%class% ...))directly in the body of the class (usually after anyattrdeclarations and beforesetupor other method definitions). - Alternatively, there are usually helper macros of the form
(class name (...) ...)which work likeelembut don't require you type so many keyword parameters. - If you cannot declare the elements statically (for instance, they are dynamically created based on a list or something), then they should be created with a
(setup ...)block, and should be created with(%class% .new ...)
- Change any
- Any
(on prop-change (...) ...)handlers will have to be ported specially.- Most of the time, these just update some other state whenever something changes, or just call
(.invalidate). For example:(on prop-change (var val prev veto) (case var [[name] body ...]] [else (call-next-handler)]))- Add
:writable? #tto the(attr name ...)declaration. - Now, add an
after-updatingstatement-
(after-updating name body ...) - or if the same code happened for several different attributes, then
(after-updating [names ...] body ...)
-
- Add
- If the
prop-changehandler does something more complicated, come talk to Eric or Brian.
- Most of the time, these just update some other state whenever something changes, or just call
- Any remaining statements in the body of the template should probably go in the
(setup ...)block. - If there were any lexically scoped variables in the body, such as
(define mouse-down? #f), they need to be ported to something else. There are a few possibilities:- Just make it an
attr. This is usually the simplest, but it makes the variable publicly accessible, and it pays a bit of a performance penalty. - If you really can't take the performance penalty, or really need something to be protected (only accessible from methods defined on the current class or subclasses), then you can use a bare
(slot 'name). Please talk to Eric or Brian before doing this. - If it was a definition of a function or constant that really doesn't need any access to
selfor anything else inside the object, then you can sometimes just move it outside of the class.
- Just make it an
- Now, you need to make sure you go through and convert any bare variables that were lexically scoped based on property names, and convert them to the
(.name)form. They can be a bit tricky to spot, so this will likely be one of the most common forms of errors when you try loading; luckily, they should be relatively easy to catch and fix. - In this process, there are several places where you may now get name clashes. You'll have to resolve all of these name clashes by hand:
- Method definitions and
attrgetters now share the same namespace. Thus, if you had a propertyfooand an(on foo () ...)definition, after porting them toattranddef, you will have a name conflict. You'll have to resolve this by hand. In some cases, thedefwill be redundant, as it may have just been used as a getter. In other cases, just rename one of them. - Less common, but still possible, an existing on handler may conflict with the setter of an attribute. Each attribute has a setter of the form
set-name!(even attributes which aren't:writable?), which can conflict with(def (set-name! ...) ...). Again, some of these will be redundant, and some will need to be renamed. - Elements declared with
(elem name (%class% ...) ...)now define a method.name, which may conflict with either a attribute getter or a method definition. In these cases, the convention for now is to rename the elementname-elem.
- Method definitions and
- Now, actually try to load a card that uses the class, and see if you broke anything.
[edit] Porting from the old card syntax to the new
The card syntax has changed a bit less than the template syntax, but still substantially. These instructions also apply to groups and sequences, although in the most common case (a group or sequence with an empty body), you shouldn't need to do anything to port it to the new system.
- There is no need to translate the keyword arguments in the superclass declaration; they still work in the
card,group, andsequenceforms. - Translate any
(on name (...) ...)to(def (name ...) ...), as above. - Translate any
(create %class% ...)to an(elem name (%class% ...))or one of theelemhelper macros as above.- Again, as above, if they have to be computed dynamically, put them into a
(setup ...), create with.new - If, on the other hand, they need to be created while the card is running its movies and animations, then they should be created at the appropriate time during the
(run ...)body, described below.
- Again, as above, if they have to be computed dynamically, put them into a
- Translate any lexically scoped variables to attributes, slots, or globals as described above.
- Anything that is left in the body (the actual playing of movies, waiting on them, playing animations, etc), should be moved into a
(run ...)form. This will be run after everything is set up when normally executing the program, but will not be run while the program is being edited in the GUI editor. - There may be name conflicts, as with the class case above. The same general strategies should work here.
- Now, test it, and make sure it all works!
[edit] Code not in cards or templates
Some code that is not in a card or template form will have to be ported.
- Replace
(create %class% ...)with(%class% .new ...). Of course, this should be much more rare, since usually people should just use the(elem name (%class% ...) ...)form, or one of the helper macros, instead. - Anyone place where
:shapeis specified should be investigated. If the shape is guaranteed to have a(point 0 0)origin (which usually means it's specified along with an:at), then it can still use:shape. Otherwise, it should be translated to:bounds. - If any messages need to propagate up to parent nodes (for instance, if messages on buttons or other controls need to propagate to containing elements or the card), then you will need to do that explicitly now. There are two forms:
- In a class somewhere (for a node), you can write
(.always-propagate 'name), to make.namealways propagate if it is called on a node that doesn't define a handler for it. - You can call
(.propagate 'name ...)(or(obj .propagate 'name ...)if you want to send a propagating message to an object other thanself) if you want to specifically propagate it from one call site, but not declare that method to generally propagate on all nodes.
- In a class somewhere (for a node), you can write
- The
%basic-button%API has changed. In particular:-
(on draw-button (state) ...)is now(def (draw) ...). You will need to get the state by calling.button-state. - Buttons take an optional
:commandparameter. If supplied, the button will call(.propagate command)when the button is clicked. - This also affects the
%answer%, which now uses a regulardrawmethod and aanswer-statemethod.
-
[edit] Renaming Tamale to Halyard
You will need to deal with the following file renames:
- The executable Tamale*.exe is now Halyard*.exe.
- The string "TamaleProgram" in
data.tamshould now be "HalyardProgram". You'll need to update this in three places manually. - The file
data.tamhas been renamed toapplication.halyard. - The file
5L.prefshas been renamed todeveloper.prefs. - The file
tamale.elhas been renamed tohalyard.el. Update your.emacsaccordingly. You may want to see the instructions inhalyard.el.
[edit] Renaming Runtime/5L
The directory Runtime/5L is now named Runtime/halyard. Scripts will need to be updated appropriately. Begin by running the following command:
find Scripts/ -name \*.ss | xargs grep -i ' "5L")' | less
If all you see is (lib ... "5L") expressions, then run the following commands:
find Scripts/ -name \*.ss | xargs perl -pi -e 's/ "5L"\)/ "halyard")/g' find Scripts/ -name \*.ss | xargs perl -pi -e 's/5l.ss/halyard.ss/g' find Scripts/ -name \*.ss | xargs perl -pi -e 's/tamale.ss/elements.ss/g'
At this point, you need to load your script and make sure that it doesn't get any load errors.
[edit] Updating from 0.2 to 0.4
The 0.3 development engines cleaned up the standard API, upgraded to PLT Scheme v360, and merged in the first half of the new object model. They also greatly improved Vista support.
[edit] Updating from 0.3.17 to 0.4.7: Smooth sailing
This should all be pretty painless. In fact, we may jump straight from 0.3.11 to 0.4.7.
[edit] Updating from 0.3.11 to 0.3.17: First half of new object model!
Versions 0.3.11 and 0.3.12 are the object model update, and 0.3.13 through 0.3.17 improve performance, fix bugs, and improve error messages. We did this work towards the end of SAFE, and if I recall correctly, it took Robinson a bit longer than a weekend.
- The
elementmacro has been removed, but it never worked, and nobody should be using it anyway. -
@*now takes a string as argument, not a symbol. Similarly, the FIND-NODE API has changed (not that anybody should be using it). -
@and@*now return proxies, not real nodes. If you encounter performance issues calling methods on proxies, you can turn a proxy into a node using RESOLVE. Also note that these functions now distinguish between static and running nodes--"static" nodes correspond to the old, non-running phantom nodes, and "running" nodes include the current card, its parents, and any running elements. - Similarly, ROOT-NODE has now been split into STATIC-ROOT-NODE and RUNNING-ROOT-NODE. These both return real nodes, not proxies.
- The node class hierarchy has been ported from Swindle to the new object system, and some of the inheritence details have changed. This means that generic functions can no longer dispatch on node types, and that typecheck-style code may need to be updated.
- Removed now useless NODE-CHILDREN from API. Use .ELEMENTS or .MEMBERS instead.
- Instead of writing
(prop @foo title), new code should ideally write(@foo .title). This also works with SET!. - The
:typeof template properties is now enforced. - Top-level
onhandlers must now be wrapped with the form(with-instance (running-root-node) ...)(But note that in 0.5.x+, on-handlers are installed on the static nodes).
[edit] Updating from 0.3.6 to 0.3.11: FreeType upgrade
FreeType has been upgraded, and the Fonts directory has been reorganized. This update should mostly involve verifying screen layouts (again).
[edit] Updating from 0.3.4 to 0.3.6: Some libraries move into engine
- Moved
drag.ssandq-and-a.ssinto Runtime/5L directory. It's better to switch over to using these copies of the files. - Moved various shape-manipulation functions into the engine.
[edit] Animation System
The animation system has been updated to be more consistent, using combinators to compose animations in a variety of ways. Several things that had been implemented in VTRA and IML libs have been pulled up into the engine, cleaned up, and rewritten.
When converting, the main thing to keep in mind is that the definition of an animation function has changed. While before it was simply a function that took a time and modified whatever it needed to display the animation at that time, in the new system, there needs to be a chance for animations to initialize. So, animation functions are now thunks (that is, functions that take no arguments), which return a function that takes a time and displays the animation on that time. For instance, the following animation function:
(define slide-foo
(fn (t)
(set! (prop foo at) (point 100 (* t 50)))))
Becomes:
(define slide-foo
(fn ()
(fn (t)
(set! (prop foo at) (point 100 (* t 50))))))
For full documentation on how the new animation system works, see Runtime/halyard/animate-doc.txt (or Runtime/5L/animate-doc.txt depending on the engine version you're running).
| Old | New | Notes |
|---|---|---|
make-object-mover
| slide
| |
(animate millis anim-fn :ease-in? #t)
| (animate millis (ease-in anim-fn))
| |
(animate millis anim-fn :ease-out? #t)
| (animate millis (ease-out anim-fn))
| |
(animate millis anim-fn :ease-in? #t :ease-out? #)
| (animate millis (ease-in/out anim-fn))
| |
combine-animations
| simultaneously
| combine-animations is from animation-and-visibility.ss in VTRA and IML Libs.
|
[edit] Updating from 0.3.3 to 0.3.4: PLT v360 and text layout
Highlights:
-
string->xmlactually works. - Text layout is subtly different in this engine, because we fixed all the descender and ascender clipping bugs. So we need to look at lots of screens and make sure everything looks reasonably good.
-
%edit-box%actually does something useful, so we can re-enable login screens. - We've upgraded to PLT v360. This makes Scheme case-sensitive. Ideally, all variable and function names should be lowercase. Note that PLT reload performance is horrible in this release, but that it improves somewhat in 0.3.5.
-
not-break-exn?->exn:fail? -
defstructappears to break when given keyword parameters. Try usingdefclassinstead (if possible).
-
The build-path function no longer returns a string. Instead, it returns a path object. If you have your own file-manipulation code, you may need to occasionally insert path->string in a few places.
[edit] Updating from 0.3.2 to 0.3.3: draw-text now wants XML
Replaced old text-formatting system (based on punctuation characters) with XML tags: <i>, <em>, <cite>, <b>, <strong> and the non-standard <h> ("highlight").
Note that the string->xml function can be used to escape strings containing less-than and ampersand signs, but that it doesn't entirely work until 0.3.4.
[edit] Updating from 0.3.1 to 0.3.2: The big reorg
This is probably the biggest single bump on the way to 0.4.x (though the partial update to the new object model is a bit hairy, too). Lots of renaming here, with some parameter reorderings, too. But the big headache is the change to how %zone% works.
[edit] Updating element templates
Most custom element templates will a modest overhaul. The basic theory:
- The old %zone% class has been replaced by three classes:
-
%custom-element%is the superclass for most custom elements.:overlay?defaults to#t. -
%box%is used for "invisible" containers.:overlay?defaults to#f. -
%clickable-zone%is an ordinary touch zone.
-
- Replace old-style
with-dclogic with new-styleon drawhandlers. This may be easy or very hard, depending on the class.- This also means that
with-dccan be removed fromdraw-buttonhandlers.
- This also means that
Please feel free to elaborate on the typical changes here.
[edit] Easier stuff
The following identifiers have been renamed:
| Old | New | Notes |
|---|---|---|
%edit-box-element%
| %edit-box%
| |
%sine-wave-element%
| %sine-wave%
| |
%movie-element%
| %movie%
| |
make-path-from-abstract
| abstract-path->native-path
| |
ensure-dir-exists
| ensure-directory-exists
| |
measure-picture
| measure-graphic
| |
draw-box
| draw-rectangle
| |
draw-box-outline
| draw-rectangle-outline
|
The following functions used to take an element name as their first argument. This has been replaced by an optional keyword parameter :name.
clickable-zone browser edit-box geiger-audio sine-wave vorbis-audio movie
The following functions have parameter changes:
| Old | New | Notes |
|---|---|---|
geiger-synth
| &rest parameter now regular parameter | |
draw-picture
| draw-graphic
| point is now first |
draw-text
| rect is now first | |
%flash-card%
| :location -> :path
| |
%browser%
| :location -> :path
| |
%geiger-audio%
| :location -> :path
| |
%movie%
| :location -> :path
|
The following functions have been replaced with ON handlers, and may be accessed using SEND. In most cases, this means that element references of the form 'FOO are no longer supported; use @FOO instead.
| Old function | New SEND method
|
|---|---|
activex-prop
| activex-prop
|
set-activex-prop!
| set-activex-prop!
|
media-pause
| pause
|
media-resume
| resume
|
set-media-volume!
| set-volume!
|
set-geiger-audio-counts-per-second!
| set-counts-per-second!
|
The following functions still exist, but no longer allow elements to be specified by symbols (unless ENABLE-DEPRECATED-FEATURES! has been called). It may be best to just call ENABLE-DEPRECATED-FEATURES! for now...
delete-element delete-elements wait
If you don't call ENABLE-DEPRECATED-FEATURES!, you can no longer wait on non-existant elements.
[edit] Notes from porting VTRA
-
center-textis also gone. I "borrowed" it back from deprecated.ss - This rev of the engine introduces %text%... of course, VTRA already HAS a %text% element, which probably isn't completely compatible.
-
%graphic%>, <code>graphicis also already present in VTRA.
[edit] Updating from 0.2.4.2 to 0.3.1: Light renaming
The input function is gone, and there's no good replacement yet. Look further up in the porting notes to see when %edit-box% is ready for prime time.
The following functions have been removed or deprecated:
origin set-origin! offset-origin with-offset-origin opacity screenshot *text-x* *text-y* *graphic-x* *graphic-y* text-position set-text-position! with-saved-text-position graphic-position set-graphic-position! with-saved-graphic-position
The following identifiers have been renamed:
| Old | New | Notes |
|---|---|---|
| rect-offset | offset-rect | |
| point-offset | offset-point | |
| point-in-rect? | point-in-shape? | |
| point-in-poly? | point-in-shape? | |
| offset-shape | offset-by-point | |
| %simple-zone% | %clickable-zone% | |
| zone | clickable-zone | |
| clear-screen | clear-dc | |
| current-card-name | (node-full-name (current-card)) | |
| movie-pause | media-pause | |
| movie-resume | media-resume |
The following file is still available, but must be required manually:
-
layout.ss
Things to watch for: The engine no longer uses 100% of the CPU. Keep an open for strange wait, nap, etc., behavior.
[edit] Updating from 0.2.3 to 0.2.4.2: run-deferred
- All calls to TIMEOUT will need to be replaced with a custom timeout implementation. Ask me if you'd like me to write one.
- All calls to CALL-AT-SAFE-TIME will need to be replaced with calls to RUN-DEFERRED.
- Code passed to RUN-DEFERRED will _always_ be called _after_ the body of the card completes. It will no longer be run during WAIT, and it will no longer be run immediately if the current context is "safe". The most common problem: Pressing buttons during audio may have no visible result until _after_ the audio completes, if the button uses RUN-DEFERRED and the main code is waiting on the audio. Also watch for places where you can queue up two or more copies of a deferred action by mashing a button.
- To prematurely end a WAIT, you can use (send @audio end-playback) on the media stream being waited on.
- Code passed to RUN-DEFERRED will not be allowed to cross card boundaries--once you jump, any pending deferred code will be discarded. This can be controlled using the :PARENT argument to RUN-DEFERRED, which specifies which card or group the code should be attached to.

