# Glk and Multimedia

Glk is a portable application programming interface (API) for applications, like JACL, with a predominantly text-based user interface. By communicating with this API, JACL is able to work with a variety of user interfaces implemented by third parties for a variety of platforms. The Glk specification was written by Andrew Plotkin, and many thanks to him for having done so. Of equal importance are the various implementations of the Glk specification.

So what does all this mean? The Glk specification defines a set of multimedia functionality that the native console version of JACL does not provide. Currently JACL can be compiled using WindowsGlk by David Kinder and Gargoyle by Tor Andersson. Many thanks to both these gentlemen for writing their libraries and assisting in the porting of JACL to use them. The commands detailed below are available for use by a JACL interpreter that is compiled with either WindowsGlk or Gargoyle. In the standard Unix distribution, the JACL interpreter is also compiled with GlkTerm. GlkTerm is a Glk library written by Andrew Plotkin using ncurses. GlkTerm does not support graphics or sound. The multimedia commands detailed in this chapter will be ignored without error by interpreters that do not support multimedia.

If you would like to have your game behave differently is certain Glk features are support by the interpreter, you can test whether the integer constants graphics_supported, sound_supported and timer_supported are set to true or false.

Graphics, sounds and timers can also be turned off from within your game by setting the integer variables graphics_enabled, sound_enabled and timer_enabled to false.

Some of the text for this chapter has been cheerfully stolen from the Glk specification. The full Glk Specification can be read at http://www.eblong.com/zarf/glk/.

## Blorb Files and the bjorb Utility

All sound and image files are made available to a Glk interpreter by placing them in a Blorb file with the same base name as the game. For example, the game grail.jacl will look in the file grail.blorb for any sounds or images required.

Blorb is a second specification written by Andrew Plotkin specification for a common format for storing resources associated with interactive fiction games. There are many tools available for creating Blorb files. A program called bjorb is included in the JACL package that is a slightly modified version of the utility blc. blc is part of the iBlorb suite developed by Ross Raszewski. Many thanks to Ross for both his original development effort and the permission to include this program in the JACL package. Information about iBlorb can be found at http://www.trenchcoatsoft.com/projects.html. The full Blorb Specification can be read at http://www.eblong.com/zarf/blorb/.

The utility bjorb creates a blorb file with the help of a .blc control file. This control file is a plain text file that specifies the sounds and images to include in the blorb file. Each line in a .blc control file describes one chunk of the Blorb file, and has the following format:

    Use IndexNumber Type File

Use is the usage of the resource and can either be Pict or Snd.

IndexNumber is the number you will use to refer to this resource in your program.

Type is the resource type. This can be either JPEG, PNG, FORM (Aiff), OGGV or MOD.

File is the name of the file to be included as this resource.

Below is an example .blc file called example.blc:

    Pict 1 PNG /images/title.png
    Snd 3 MOD /music/theme.mod
    Snd 4 OGGV /sounds/explosion.ogg
    Pict 2 JPEG /images/car.jpg

To create a Blorb file from this .blc file, use the bjorb utility in the following manner:

    bjorb example.blc example.blorb

This command will create the Blorb file example.blorb that will be automatically read by the game example.jacl. When the bjorb utility runs, it will output some JACL code that will create a constant for each image or sound. If you find these convenient you can cut and paste this code into your game.

For example, when reading this .blc file:

    Pict 1 PNG images/blackjack.png
    Pict 2 PNG images/chip25.png
    Pict 3 PNG images/chip50.png
    Pict 4 PNG images/chip100.png
    Pict 5 PNG images/club.png
    Pict 6 PNG images/diamond.png
    Pict 7 PNG images/spade.png
    Pict 8 PNG images/heart.png

bjorb will produce the following output:

    bjorb 1.0 (Apr 30 2008) by Stuart Allen, based on
    Blorb Packager Version .5b by L. Ross Raszewski

    # CONSTANTS FOR RESOURCES IN BLORB FILE
    constant IMAGE_blackjack 1
    constant IMAGE_chip25 2
    constant IMAGE_chip50 3
    constant IMAGE_chip100 4
    constant IMAGE_club 5
    constant IMAGE_diamond 6
    constant IMAGE_spade 7
    constant IMAGE_heart 8

If you pass only a single command-line argument to bjorb, that argument will be used as the base name for both the .blc control file and the .blorb output file. As an example, the following two commands are equivalent:

    bjorb example

    bjorb example.blc example.blorb

## The IMAGE Command

The image command is used to display one of the images stored in the game's blorb file. The image to display is specified using its index in the blorb file. The index can be supplied either as a literal integer or any JACL container that resolves to an integer. The index of the image can optionally be followed by an alignment. The possible alignments are:

Alignment | Description
--- | ---
up | The image appears at the current point in the text, sticking up. That is, the bottom edge of the image is aligned with the baseline of the line of text.
down | The image appears at the current point, and the top edge is aligned with the top of the line of text.
centre | The image appears at the current point, and it is centered between the top and baseline of the line of text. If the image is taller than the line of text, it will stick up and down equally.
left | The image appears in the left margin. Subsequent text will be displayed to the right of the image, and will flow around it -- that is, it will be left-indented for as many lines as it takes to pass the image.
right | The image appears in the right margin, and subsequent text will flow around it on the left.

The two "margin" alignments require some care. To allow proper positioning, images using left and right must be placed at the beginning of a line. That is, you may only call image (with these two alignments) if you have just printed a newline, or if the screen is entirely empty. If you margin-align an image in a line where text has already be printed, no image will appear at all.

The following code demonstrates the image command:

    constant IMAGE_house 4

    {+display_image
    # DISPLAY IMAGE 4 WITH THE TOP OF IMAGE LEVEL WITH TOP OF THE TEXT
    image IMAGE_house

    # DISPLAY IMAGE 6 WITH THE BOTTOM OF IMAGE LEVEL WITH TOP OF THE TEXT
    image 6 up
    }

## The SOUND Command

The sound command is used to play one of the sounds stored in the game's blorb file. The sound to play is specified using its index in the blorb file. The index can be supplied either as a literal integer or any JACL container that resolves to an integer.

The index of the sound can optionally be followed by a channel to play the sound on. There are four available channels: 0, 1, 2 and 3. If no channel is specified, channel 0 is used as the default.

If a channel is specified, it can be followed by the number of times to repeat the sound. If -1 is specified as the number of times to repeat the sound, the sound will keep playing until it is manually stopped using the stop command. If no number of times to repeat the sound is specified it is played once.

The following code demonstrates the sound command:

    constant SOUND_rain    6
    constant SOUND_thunder 7
    integer  AUDIO_CHANNEL 2

    {+play_sound
    # PLAY SOUND 7 ON CHANNEL 0 ONCE ONLY
    sound SOUND_thunder

    # PLAY SOUND 6 ON CHANNEL 2 ONCE ONLY
    sound SOUND_rain AUDIO_CHANNEL

    # PLAY SOUND 6 ON CHANNEL 3 FOUR TIMES
    sound 6 3 4

    # PLAY SOUND 6 ON CHANNEL 3 INDEFINITELY
    sound SOUND_rain 3 -1
    }

## The VOLUME Command

The volume command is used to set the volume of a sound channel. The volume is specified as an integer between 0 and 100. A second, optional parameter can be used with the volume command specifying which sound channel to set the volume for. If this parameter is omitted the volume is set for channel 0.

The following code demonstrates the volume command:

    {+set_volume
    # SET CHANNEL 0 TO FULL VOLUME
    volume 100

    # SET CHANNEL 2 TO HALF VOLUME
    volume 50 2
    }

## The STOP Command

The stop command simply stops the sound being played on the specified sound channel. The channel to stop is specified as an integer between 0 and 3. If no channel is specified channel 0 is stopped by default.

The following code demonstrates the stop command:

    {+stop_sound
    # STOP THE SOUND PLAYING ON CHANNEL 0
    stop

    # STOP THE SOUND PLAYING ON CHANNEL 2
    stop 2
    }

## The TIMER Command

The timer command tells the interpreter to call the function +timer every so many milliseconds, regardless of whether the player types a command or not. The number of milliseconds to wait between each function call is specified as the timer command's only parameter. If you specify a time of 0 to a timer command, the timer will be turned off.

| It is important to be aware that not all interpreters will support the timer command, so no processing essential to the game should be performed within the +timer function. Periodically playing sound effects that are not essential to the game is an example of valid use of this functionality.

Below is an example of the timer command being used:

    {+intro
    ...
    # SET THE TIMER TO EVERY TEN SECONDS
    timer 10000
    ...
    }

    {+timer
    if here has OUTDOORS
       # PLAY THE THUNDER SOUND ON CHANNEL 3 SO
       # IT DOESN'T INTERFERE WITH OTHER SOUNDS
       play SOUND_thunder 3
    endif
    }

## The STYLE Command

The style command is used to output either ANSI terminal codes or set Glk styles depending on the interpreter being used. The style command accepts a single string containing the name of the style to set. Here is an example of using the style command to output some bold text:

    style bold
    write "This is bold.^"
    style normal

Below is a table showing styles available in JACL and the Glk styles they map to:

JACL Style | Glk Style
--- | ---
bold | style_Emphasized
note | style_Note
input | style_Input
header | style_Header
subheader | style_Subheader
reverse | style_Note
pre | style_Preformatted
normal | style_Normal

## The Status Window

The status window at the top of the screen is implemented as a Glk window, and is therefore covered here. It is possible to have no status window at all, use the built-in, default status window or design a custom one yourself. The vertical height of the status window in rows is defined by using the constant status_window. If the constant status_window is set to 0, no status window will be created. For example, to create a status window that is three rows tall, define the following constant:

    constant status_window 3

The status window is always created using a fixed-width font and its current dimensions can be read at any time using the values of status_width and status_height. Although status_height will most often be equal to status_window (unless status_window exceeded the available space), status_width will change as the player's resizes the game window.

If a status window is created, the interpreter will attempt to call the global function +update_status_window. If this function exists, it will be executed and must contain code to draw the contents of the status window. If this function doesn't exist, the interpreter will use internal code to generate a standard interactive fiction status line. This consists of a single line with the name of the current location against the left side and the number of moves and current score against the right.

If the function +update_status_window does exist, the current Glk stream is set to that window before it is called so all write or print commands will output to it. The window is also first cleared of all previous contents and the cursor is positioned in the top left corner.

The location at which to start printing text within the status window is changed using the cursor command. The cursor command is passed two integer parameters, the row and column to move the cursor to. Counting starts in the top left corner at 0, 0. For example, to move the cursor to the far right hand column of the second row, use the command:

    cursor status_width 1

Often when positioning text it is important to know the length of the string you are going to print. This is determined using the length command. The length command requires two parameters: the container to hold the length of the string and the string itself. For example, the following command determines the length of the string constant game_title and stores the result in the variable index:

    length index game_title

Putting this all together, it is possible to display the title of the game centred in a single-line status window using the following code:

    constant game_title "The Unholy Grail"

    constant status_window 1

    integer index
    integer offset

    {+update_status_window
    set offset = status_width
    length index game_title
    set offset - index
    set offset / 2
    cursor offset 0
    write game_title
    }

Status windows are often displayed using reverse text to make it clearly stand out from the main window. This is achieved using the following command:

    style reverse

This command, however, will only reverse the text output, not the whole window. In order to achieve the effect of an entirely reversed window, blank spaces will need to be printed wherever there isn't any other text. The easiest way to do this is to print entire rows of blank spaces then move the cursor back to print over the top. The padstring command exists to help with this process. The padstring command takes three parameters. The label of the string to fill, the text to fill the string with and an integer specifying the number of times to copy the text into the string. To print a blank line in the status window, the text that will by copied is a single space in quotes (" ") and it will be copied status_width times. The code below is an expanded version of the above function that prints the title of the game centred in an inverse status window:

    constant game_title "The Lovely Test Game"
    string status_text

    integer index
    integer offset

    constant status_window 1

    {+update_status_window
    style reverse
    padstring status_text " " status_width
    write status_text
    set offset = status_width
    length index game_title
    set offset - index
    set offset / 2
    cursor offset 0
    write game_title
    }

As a final example of a +update_status_window function, below is the JACL code to replicate the internal status line produced if no custom function is provided:

    string status_text

    integer index

    constant status_window 1

    {+update_status_window
    style reverse
    padstring status_text " " status_width
    write status_text
    cursor 1 0
    write here{The}
    setstring status_text "Score: " score " Moves: " total_moves
    set offset = status_width
    length index status_text
    set offset - index
    set offset - 1
    cursor offset 0
    write status_text
    }

## The UPDATESTATUS Command

The interpreter will call the +update_status_window function after each of the player's moves and when the game window is resized. If you require the status window to be updated at other times such as in a loop or from the +timer function, use the updatestatus command. The updatestatus command takes no parameters. It sets the current output stream to the status window and clears the status window before calling the function +update_status_window. When +update_status_window has finished executing the current output stream is set back to the main window.

| It is not possible to call the +update_status_window directly. It will be called by the interpreter automatically after each of the player's moves or when the window is resized. If you do require the window to be updated at other times use the updatestatus command. If you call +update_status_window directly the current output stream will not be set to the correct window.

Back to Contents