Crash Course to Hollywood Programming

From MorphOS Library

Revision as of 14:37, 28 October 2017 by JPV (talk | contribs)

Author: jPV/RNO (Proofreading by Marq)


Hollywood - the cross-platform multimedia application layer - is an easy programming/scripting language that is designed for the creation of graphical or multimedia-oriented applications, but it can also be used to create other kinds of programs, especially with its comprehensive selection of plugins. Hollywood is loosely based on the popular Lua language.

Hollywood programs are created by writing script files, which are then run from the Hollywood main program, or they can be compiled into applets or standalone executables to be used without the Hollywood environment.

Applets are small platform-independent binary files which can be run using the free Hollywood player on any supported platform. Standalone executables contain the player, a script and optional data files linked into a single executable file. Linking the whole player binary into an executable makes the minimum size of Hollywood executables around 2 megabytes on MorphOS, but then the resulting programs are completely independent and can be run out of the box without any prior installation.

What makes Hollywood special is its ability to cross-compile programs between all supported platforms. Windows, Linux, OS X, or Amiga programs can be compiled just by using MorphOS, and so on. Cross-compiled programs don't usually need any modifications and the same source script will work on all platforms.

Hollywood is an easy, safe, and very well documented language, and thus also suitable for beginners. All Hollywood programs run inside a sandbox, and you can't break or crash the underlying operating system with your programming mistakes. If there's a bug causing an error in the script execution, Hollywood just closes the program and frees the resources with no further harm.

Getting Started



Hollywood is a commercial product which can be bought here. The main package is enough for Hollywood programming and compiling for all other platforms except Android (Windows-only APK Compiler is required for the Android support).

Hollywood Designer is an Amiga-only add-on to create multimedia presentations in a WYSIWYG GUI. It works on Amiga compatible platforms including MorphOS, but the resulting presentations can be compiled for any other supported platform. Anyway, it isn't required for programming with Hollywood, but your own code can be inserted into presentation scripts too.

Text Editor

Hollywood features an IDE (integrated development environment) for Windows, but on other platforms a text editor or a 3rd party IDE is needed.

Cubic IDE offers the best development environment for Hollywood programming on MorphOS. With the Cubic IDE add-on you'll get syntax highlighting, automatic casing, online help, quick access to documentation, automatic indentation, bracket matching, auto completion of keywords, compile and run options, and a lot more.

MiniHollyEdit NG could also be a good freeware option, but it's broken in its current state and needs an update first.

An IDE with Hollywood support is the most comfortable solution of course, but any other text editor will also do.

Running a Script

Hollywood scripts can be run from the Hollywood main executable (the interpreter), and while the main executable accepts tens of command line arguments, you don't usually need many of them.

When you're developing and testing your application, it's enough to just run the script without compiling it. It's a good practise to save your scripts with the .hws extension.

To run a script from the shell:

Hollywood MyProgram.hws

And without the information window:

Hollywood MyProgram.hws -quiet

Hollywood has a built-in resource monitor, which can be activated with the -resourcemonitor argument. You should check the resources your application uses and that you aren't increasing the memory usage carelessly:

Hollywood MyProgram.hws -resourcemonitor

If you're using Cubic IDE, you don't need the shell at all. You can just press the F4 key to run the script or select other running options from Cubic IDE's menus.

Compiling a Script

When an application is ready for publishing, it should be compiled into a standalone executable, which is done with the -compile argument:

Hollywood MyProgram.hws -compile MyProgram

With the above command you get an executable called "MyProgram" that will run on the same archtecture it was compiled on.

To compile an application for different platforms (OS4 in this example):

Hollywood MyProgram.hws -compile MyProgram -exetype amigaos4

Or for multiple platforms at once:

Hollywood MyProgram.hws -compile MyProgram.exe -exetype morphos|amigaos4|win32|applet

If you want to compress your own data (script and linked data files), add the -compress argument:

Hollywood MyProgram.hws -compile MyProgram.exe -exetype morphos|amigaos4|win32|applet -compress

Compressing can make executables smaller, but it also "hides" any plaintext data from the executables if you prefer that.

Compiling in Cubic IDE is simply done by pressing the F2 key or by selecting the platform for cross-compiling from the menus.

First Test

Let's type this into a text editor and save it as a file called RAM:firsttest.hws:

@BGPIC 1, "SYS:MorphOS/Prefs/Wallpapers/1024x768/Arida.png"
TextOut(100,10,"Hello World!")
; Program exits after the last line in a script, but you can also terminate it with the End() command

The first line is a preprocessor command which loads the given image file as a Hollywood background picture. Objects in Hollywood are handled by their ID numbers, and in this case we load the image as a bgpic number 1. That bgpic is also loaded initially when the application opens its window.

All preprocessor commands are prefixed with the @ character and processed before the actual script execution. Data files loaded with the preprocessor commands are also linked into the executable when compiling a program. If you don't want to link the files, you can load them separately later in the script.

The second line prints the "Hello World!" text at x=100, y=10 (in pixels) over the background image.

The third line waits for the user to press the left mouse button. Without this line, the program would just end and close its window, and we wouldn't see much.

The last line is a comment line, which is just a note inside the code. Comment lines can be inserted between /* and */, or the rest of the line can be commented out with the ; character.

Let's run the script to see the results. Hit the F4 key in Cubic IDE or type this in the shell:

Hollywood RAM:firsttest.hws -quiet

Hollywood opens a new window and takes its dimensions from the background picture. If we wouldn't preload any background picture, then the display would be opened in the default 640x480 resolution, or you could define its dimensions with the @DISPLAY preprocessor command.

Next let's compile the program into a standalone executable. Hit the F2 key in Cubic IDE or use the shell (you don't have to define the paths if you've already changed directory to RAM: in the shell):

Hollywood RAM:firsttest.hws -compile RAM:firsttest

Now we have a new executable file that also contains the preprocessed/linked background graphics in it. The program works even if you don't have the Arida.png file in the system.

Hollywood Programming Example

The previous example could be extended to have more commands to be executed in batch, which could be fine for presentations or slideshows, but let's take a more practical approach and create an interactive script whose structure looks like a real program.

@APPTITLE "Hollywood Example"
@APPVERSION "$VER: Hollywood Example 1.0 (26.10.17)"
@APPDESCRIPTION "A Hollywood example program."

There are several preprocessor commands for adding informative strings to an application. These three are especially useful for Amiga and MorphOS programs, because Hollywood programs act like commodities and you'll see this information in, for example, the Exchange program. The @APPVERSION format should respect the standard version string rules, which means that the date should be given in the DD.MM.YY format etc.

@DISPLAY {Title="Hollywood Example", ScreenTitle="Hollywood Example", Width=400, Height=300, Sizeable=True, Layers=True, ScaleMode=#SCALEMODE_LAYER, SmoothScale=True}

This time we don't use the @BGPIC preprocessor, but define the display size manually and add some extra attributes too.

Title defines what will be shown in the window title bar, and ScreenTitle what is shown in the screen title bar when our program window is active.

The Width and Height attributes define the actual drawing area (display) for our application. The application window itself will be a bit larger with the standard OS window borders added around a Hollywood display. It is also possible to open borderless and fullscreen windows without the standard window borders.

The Sizeable attribute defines if the application window is resizeable by the user.

Layers=True enables Hollywood's layer system. It depends on the program you're developing whether you want to enable layers or not. With layers enabled all drawing operations get their own layer that can be moved, removed, and otherwise processed. When they're disabled, all graphics are just painted over each other on the display and it's harder to change or undo things. But if you're just drawing fullscreen graphics to the display or something like that, it's better to keep layers disabled.

ScaleMode sets if display contents are scaled automatically when the display size changes. Many times it's better to keep this disabled and recalculate your own graphics after a resize event to keep the aspect ratio intact and to avoid slowdowns, but let's enable this now to get more functionality into our application without extra lines of code.

SmoothScale makes the scaling output prettier at the expense of speed.

@BRUSH 1, "MOSSYS:Data/Jalapeno/ReadMode.png", {LoadAlpha=True}

Brushes are the most common way to load, process, and display graphics in Hollywood. This preprocessor line loads a PNG image as a brush with ID number 1. All brush handling operations are done by referring to the ID number from now on. The last argument is an optional table containing any extra options for loading. We are using graphics with an alpha channel (transparency) now, so we tell Hollywood to load alpha channel values too.

Function p_DrawGfx()
    Local brush_number = Rnd(2) + 1
    If LayerExists("gfx") Then RemoveLayerFX("gfx")
    SetDisplayAttributes({Title = "Brush " .. brush_number})
    DisplayBrushFX(brush_number, #CENTER, #CENTER)
    SetLayerName(0, "gfx")

This is how you create your own functions in Hollywood. Our own function names should always start with the p_ prefix to distinguish them from Hollywood's internal functions. In this case the function doesn't take any parameters and doesn't return any values, it can just be called from the program with p_DrawGfx() to run the lines inside it.

The purpose of this function is to show one of the two brushes we have in memory. The brush ID numbers are 1 and 2, and we randomly pick one of them. Because we have the layer system enabled, we get a new layer every time we draw a brush into the display. To avoid ending up with unlimited number of layers we remove an existing layer first. We also change the window title bar text to show the number of the shown brush.

The first line of the function declares a new local variable called brush_number and assigns a random value (1 or 2) to it. You should declare new variables as local whenever possible; read more about it here.

The second line checks if we already have a specific layer created, and removes it with a fancy random effect if we do. This line is just skipped when the function is called the first time, because we don't have the specific layer yet.

The third line changes the window title text to the brush number we got on the first line. Almost all display attributes can be changed on the fly, and you aren't restricted to the ones you defined with the @DISPLAY preprocessor command. Here you can also see how you can combine a string from other strings or variables with the .. operator, which is familiar from Lua.

DisplayBrushFX() shows the selected brush and creates a new layer on the layer stack. The position of the brush can be given in pixels, but also using inbuilt constants like #CENTER. FX at the end of the function name indicates that there's a transition effect displayed when the function is called. In most cases they are a bit over the top, and you probably end up using DisplayBrush() or other options to render the graphics.

Layers get ID numbers automatically and they are assigned dynamically, which means that removing a layer changes the ID numbers of the layers on top of it. To make life easier we can name the layers and handle them by their names instead of their ID numbers. SetLayerName() is one way to do that, and we use it to name the layer with the brush here. The function accepts a layer ID number and name as parameters. Our displayed brush creates the first layer on the layer stack and its ID number will thus be 1. SetLayerName(1,"gfx") would work in this case, and our layer would get the name "gfx" as intended. But to make life even easier we can give ID number 0 as the first parameter, and the name will be given to the last created layer. This way you don't have to keep count of layers at all.

Function p_Input(msg)
    DebugPrint("Input event!", msg.Action)
    Case "OnKeyDown":
        DebugPrint(msg.Key, "key was pressed.")
        Case " ":
        Case "ESC":
    Case "OnMouseDown":

Here's another function created by us. It is used to handle user input events. Event handling functions will get a message table as their parameter, and we'll get it as a local variable called "msg".

DebugPrint() is a useful function for checking what values we have in certain variables during the program execution. Don't hesitate to use it temporarily to learn what results you get from different functions and what values you have in variables. Here it is used to print the Action field from the message table, which tells us the name of the event that occured.

If the user presses a key, we get an OnKeyDown event, and if the user presses the left mouse button, we get an OnMouseDown event. A full list of standard events can be found here.

We handle different events with a Switch-Case statement in this example, but feel free to handle it in other ways if you please. It is also possible to have a separate function for each event.

If we take a look at the documentation, we see that the OnKeyDown event contains the exact key that was pressed in its Key field. It can be accessed with the msg.Key table item in our function.

The inner Switch-Case statement checks if the pressed key was space or escape. If it was space, our previous drawing function is called and you get new graphics on the display, whereas pressing the escape key quits the program with a message. Pressing any other keys will just print the key without doing anything else.

Pressing the left mouse button also calls our previous drawing function, and the display gets refreshed.

LoadBrush(2, "MOSSYS:Data/Jalapeno/TrackMode.png", {LoadAlpha=True})

The first image in our example program was loaded with the @BRUSH preprocessor command, but let's load the second image separately. The difference between them is that by doing the latter the image doesn't get linked into our executable file when we compile the program. The image is loaded from the filesystem every time you run the program, no matter if it's compiled or not.


Now that we have both images loaded as brushes, we can call the function we defined earlier, and we get some graphics shown in the application window.

InstallEventHandler({OnKeyDown = p_Input, OnMouseDown = p_Input})

Our previously defined p_Input() function doesn't get any events unless we install event handlers. A table passed to the InstallEventHandler() function defines which events are listened to and what functions they call when triggered. Here we define both OnKeyDown and OnMouseDown events to call our own p_Input() function, and user input will work from now on. As you can see, events can call the same function or they can be made to call other functions if necessary.


This is the main loop of the program. The WaitEvent function is called repeatedly as long as our program is running and it only waits for any event to happen. The program just sleeps and doesn't waste any CPU time when there aren't any events.