Xoink Animaniacs

Xoink! - Simple Bitmap Flash Interface Library

Download

The current realease of the Xoink library source and the XoinkView Windows viewer/converter utility can be found here.

What Xoink Is

What Xoink Is Not

Why Use Xoink?

Some Screenshots

Player application skin...

Embedded video device playlist UI..

What FLASH Features are Supported?

What FLASH Features are NOT Supported?

Using the XoinkView Viewer Utility (Windows)

The Windows XoinkView utility can be used to preview animations and convert xoink files to the xoink native file formats.

The file menu allows you to read FLASH .SWF or Xoink xml .XOX or binary .XOI files. Once the files are loaded, you may preview them by pressing the play button in from the tool bar. Simple flash keyboard events and buttons will be active, allowing you to navigate your interface with either the keyboard or the mouse to test its functionality.

When you have finished previewing the file, you can export the file using the "Save As.." menu option to the XOI or XOX file format. A dialog box will be displayed allowing you to select the various file format options before saving the file.

File Format - Selects either the xml XOX file format or the binary XOI file format.

File Version - Allows you to save XOI files in a previous 1.0 version (for compatibility with an older version of the Xoink player).

Embed Bitmaps - Causes bitmaps to be embedded in the export file, and not saved independently as .PNG or JPG files.

Export Bitmaps to JPG/PNG Files - Saves the bitmaps read from a .SWF file to be exported as .JPG or .PNG files. Small palettized bitmaps or bitmaps with alpha channel will be exported as .PNG files, larger 24 bit bitmaps will be exported as JPG files.

Embed only Raw Bitmaps - Stores bitmaps in raw format when embedding bitmaps. This allows files to be read without using the ZLIB or JPEGLIB or LIBPNG dependencies (i.e. NO cOMPRESSION DEPENDENCIES).

Use Original PNG/JPEG Data - When file was loaded as a .XOX of .XOI file with external bitmaps (not embedded) will use the original compression data when re-saving the file (will not RECOMPRESS bitmaps thus degrading quality).

Use PNG Compression - If not checked, will not use PNG compression when embedding bitmaps (only ZLIB compression or JPEG).

Use JPEG Compression - If not checked, will not use JPEG compression when embedding bitmaps (only PNG, or ZLIB compression).

(NOTE: IF both PNG and JPEG are disabled, bitmaps will be compressed using ZLIB DEFLATE ONLY. This allows the pnglib and jpeglib dependencies to be eliminated while still compressing the embedded bitmaps).

Save as JPEG if Size Greater Than - Size of bitmap data threshold for saving a 24 bit bitmap as a JPEG file.

Using XoinkLib

A. Compiler Switches/Dependencies...

Depending on the features you use, Xoink will depend on the following external libraries:

zlib - XOINK_HAS_ZLIB - Zip deflate/inflate compression (used by libpng)
libpng - PNG bitmap read/write support
jpeglib - JPEG bitmap read/write support
expat - Xoink XML animation file format (vs. binary file format)
jpgdlib - Used by the swf reader module (not needed if you don't include SWF file reading)

Compiler switches:

XOINK_HAS_LIBPNG - Define to allow reading embedding PNG compressed images (requires libpng, zlib)
XOINK_HAS_JPEGLIB - Define to allow reading embedding JPEG compressed images (requires jpeglib)
XOINK_HAS_ZLIB - Define to allow compressed embedded bitmaps (other than PNG/JPEG.. requires zlib)
XOINK_READ_SWF - Define to allow reading FLASH SWF files (requires zlib, jpgdlib)

Xoink comes with current versions of the dependencies it requires by default, but you can and probably should think about installing the latest versions of these libraries for production.

In order to use Xoink in an application, you need to first decide what your usage profile is. This will determine the dependencies that Xoink will have on other external libraries.

1. Do you want to read external PNG or JPEG files, or embed your bitmap graphics in your Xoink interface files. You will have a dependency on the jpeglib, libpng, and zlib libraries if you need this capability.

2. If embedding graphics in your interface files, do you want to use ZLIB compression, or is will you use uncompressed graphics (which might be a better choice for very small UI's with 1 or 2 bpp bitmaps). If you use compression, you will need zlib.

3. Do you want to use XML xoink files, or binary xoink files. If you use XML you will need the expat library.

The maximal version of XOINK with all options enabled will read SWF files, read XOINK XML interface files, and read and write PNG and JPEG files and will depend on the standard c library, libpng, jpeglib, zlib, expat, and jpgdlib.

The minimal versiion of XOINK with all options disabled will only read xoink binary files with uncompressed embedded bitmaps, and will only require a standard C library.

B. Making a Xoink Compatible FLASH SWF Files

In most cases, when using the Xoink library, you will want to use a FLASH compatible authoring tool (i.e. Macromedia Flash MX) to author the animations you will use in Xoink. If you compile the library with the XOINK_READ_SWF preprocessor define, the xoink library can read these .SWF files directly. However typically you will want to convert these files to either the .XOX xoink XML file format or the .XOI xoink binary format using the XoinkView player utility to provide smaller file sizes or XML compatibility.

Remember, Xoink is NOT flash. Xoink is a 2D bitmap hardware blitter based interface animator, it is not a vector based software web animation player. If you keep this in mind, it will make authoring of content for Xoink somewhat easier.

To author a Xoink file in flash, you should keep in mind the following guidlines:

1. NEVER use non bitmap shapes in your flash animation. Xoink ignores all vector shapes when reading/importing .swf files.

2. NEVER use flash shape tweens. These are ignored by Xoink.

3. Always remember to click the "Use Device Fonts" checkbox for text elements in flash, and set the name and the style of the font you wish to use. If you don't, your text will be assigned the default font and will not look correct.

4. NEVER use masks in your flash animations. Masks are ignored by Xoink.

5. NEVER use rotations or transforms in your flash animations. These effects are ignored by Xoink.

6. In order to name an object in your animation (for example a button, text element, etc.) set the Instance Name in the properties panel for that object. The C++ api provides simple functions for finding animation elements using these instance names.

7. DON'T provide more than 1 (ONE) action script track for any movie clip or other symbol. Xoink will only use the first actionscipt it finds in each movie clip/symbol.

8. DON'T use any other commands in an action script except goto, gotoAndPlay, stop, getURL, and gotoLabel. Other commands will be ignored.

9. DON'T use any other events besides button or key press events in action scripts. Other events are ignored by Xoink.

10. When you export your Flash file to .SWF, use the following options:

Flash Player 6 (IMPORTANT)
Load Order Bottom Up
Action Script 1.0 (IMPORTANT)
All Options Unchecked (IMPORTANT)
No Password

(All other export options are ignored by Xoink)

C. Simple C++ Hello World

Below is a simple hellow world program (using Microsoft Windows).



#include "Xoink.h"

// Other windows initialization here

void hello_world_msg(int msg, const char* name, void* ptr, int data1, int data2);

void hello_world(HWND hWnd)
{
    bool load_swf = true;   // load from swf file
    bool load_xox = false;  // load from xox (xoink xml) file
    bool load_xoi = false;  // load from xoi (xoink binary) file

    // Initialize the library (always succeeeds)
    XoinkInit();

    // We can optionally set the image path to any folder.  By default
    // images are loaded from the same folder the animation file is in.
    // (Note: that this path can use either / or \ directory separators any any platform)
//    XoinkSetImagePath("c:/hello_world/images");

    // Create an empty movie, tell it to keep it's "generic" bitmap data and compressed bitmap data
    XoinkMovie* movie = new XoinkMovie(IMOVIE_KEEP_GENERIC_GFX | IMOVIE_KEEP_COMP_GFX);

    // Load the movie file
    if (load_swf)
    {
        movie->ReadSWF("hello_world.swf");  // Import movie data from .swf FLASH file
    }
    else if (load_xox)
    {
        XoinkXMLStream* stream = new XoinkXMLStream("hello_world.xox", IFILE_READ);
        movie->Read(*stream);
        delete stream;
    }
    else if (load_xoi)
    {
        XoinkBinaryStream* stream = new XoinkBinaryStream("hello_world.xoi", IFILE_READ);
        movie->Read(*stream);
        delete stream;
    }

    // Check for errors
    if (XoinkGetError())
    {
         printf("Error reading file: %d %s\n", XoinkGetError(), XoinkGetErrorStr());
         return;
    }

    // Make a windows device canvas
    XoinkCanvasWin32* canvas = new XoinkCanvasWin32(0, 0, 640, 480, XRGBA(192,192,192,255), ICANVAS_USE_OFFSCREEN);

    // Create a player object to play the movie
    XoinkPlayer* player = new XoinkPlayer;

    // Set the movie into the player
    player->SetMovie(movie);

    // Set the canvas the player will output to
    player->SetCanvas(canvas);

    // Initialize the player to time value 0.  The GetRootPlayer function gets the root player object
    // in the animation's heirarchical player tree.
    player->GetRootPlayer()->Update(0);

    // Give the player a callback function for button events or other messages
    player->SetMsgCallback(hello_world_msg);

    // Find animation "my_anim" and set it's frame to 1, and its playing status to stopped.
    // "my_anim" would be a named animation clip or "symbol" in your flash file with it's own independent
    // timeline.
    XoinkAnimElemPlayer* anim_player = (XoinkAnimElemPlayer*)player->FindPlayer("my_symbol");
    if (anim_player)
    {
        anim_player->SetOverrideAnim(IANIM_OVERRIDE_POSITION); // I'm setting the position manually, so don't allow animation to change it
        anim_player->SetPos(100,100); // Set the position to 100, 100
        anim_player->SetFrame(1); // Set the frame to 1
        anim_player->Stop(); // Stop this element (it will remain on frame one until restarted with Play())
    }

    // Find the text element "my_text" by it's flash symbol instance name, and set it to show "Some Text"
    XoinkTextElemPlayer* text_player = (XoinkTextElemPlayer*)player->FindPlayer("my_text");
    if (text_player)
        text_player->SetText("Some Text");

    // Now animate our interface
    int timedelta = 0;
    while (timedelta < XOINK_SECOND * 60) // Animate for 30 seconds
    {
        // Animate the player (add 1/30th of a second to current player time)
        player->Update(XOINK_SECOND / 30);

        // Set which windows dc to use to draw animation
        HDC hdc = GetWindowDC(hWnd);
        canvas->SetDC(hdc);

        // Restore the canvas, then draw it, then show it
        canvas->RestoreCanvas();
        canvas->DrawCanvas();
	canvas->ShowCanvas();

        // Release the dc
        ReleaseDC(hdc);

        // Wait for 1/30th of a second (using Win32 API Sleep() function)
        Sleep(1000 / 30);
	timedelta += XOINK_SECOND / 30;
    }

    // Delete our resources and exit
    delete canvas;
    delete player;
    delete movie;

    // Close xoink
    XoinkClose();
}

// We can have a callback function to handle various messages from our animation.  Currently only
// button messages are supported.
void hello_world_msg(int msg, const char* name, void* ptr, int data1, int data2)
{
    // "name" is the instance name of the element sending the message (i.e. flash button instance name)
    // "ptr" is the pointer to the XoinkPlayer element (usually XoinkButtonElemPlayer)
    // "data1" and "data2" are not used presently
    switch (msg)
    {
    case XMSG_BUTTON_DOWN:
        // Do something when a button is pressed down
        break;
    case XMSG_BUTTON_UP:
        // Do something when a buttin is released up
        break;
    case XMSG_BUTTON_OVER:
        // Do something when the mouse cursor is over this element
        break;
    case XMSG_BUTTON_OUT:
        // Do something when the mouse cursor goes out of this element
        break;
    }
}

This example shows how to simply open a Xoink animation file. In a normal program, typically the animation loop would be placed in a timer callback function (i.e. WM_TIMER in Windows), and would query the elapsed time between each call and use that value when calling the player->Update() method to update the player animation.

Controlling the animation from C++ involves getting pointers to the element player objects in your animation objects (XoinkAnimElemPlayer, XoinkTextAnimPlayer, XoinkBitmapAnimPlayer, and XoinkButtonAnimPlayer) by name from your animation player object, then calling the various control functions such as SetTime(), SetFrame(), Play(), Stop(), SetText(), SetEnabled(), SetPos(), SetColor(), SetScale(), etc.

An important point to note is that you can neither add or remove an animation element from a movie dynamically. However you can use the SetEnabled() function on your player elements to show or hide an animation, and use SetTime() or SetFrame() to change the state of an element where it may have an invisible frame.

If an element player's timeline is set to "play", the properties (enabled, pos, scale, color) for that player element will change over time as the timeline for that player element progresses. However it is often desirable to override these values to prevent them from being changed during play. This can be accomplished by calling the SetOverrideAnim() function to select which values to override (enabled status, position, scale, color). When a value is overridden, the animation track for that property is ignored during playback and the value will not change from how it is set in C++.

To send keyboard and mouse events to Xoink, you call the following functions:

    player->GetRootPlayer()->MouseDown(x, y);
    player->GetRootPlayer()->MouseUp(x, y);
    player->GetRootPlayer()->MouseMove(x, y);

    player->GetRootPlayer()->KeyDown(key);
    player->GetRootPlayer()->KeyUp(key);

The key codes for the key down and key up functions are normal ascii character's, with special keyboard specific key codes (number pad, arrows, etc) defined as kyXXXX macros in XoinkAction.h.

D. Setting up Keyboard Control in Flash

This below actionscript shows how you can add simple keyboard navigation to your animations:

First create an invisible top level button element in your animation, and place it on a top level layer. Then add your event handlers to the button's action script as shown in the example below.

on (keyPress "<Down>") {
	tellTarget("/"){
		nextFrame();
	}
}
on (keyPress "<Up>") {
	tellTarget("/"){
		prevFrame();
	}
}
on (keyPress "<Space>"){
	tellTarget("/filelist/play1"){
		nextFrame();
	}
}

The above actionscript sets the frame of the root animation to the next frame if the down button is pressed, the previous frame if the up button is pressed, and it sets the frame of the animation "/filelist/play1" to the next frame when the space bar is pressed.

Note that Xoink does not understand actionscript 2.0 semantics (i.e. Object.function()), so the deprecated actionscript 1.0 tellTarget() function must be used to set the current animation to apply commands to.

This navigation is adequate for simple menus. More complex animation can be accomplished using the C++ API.

E. Making Various Controls

Making a selection bar...

Showing the current selection of items on your interface can easily be accomplished by creating a Flash movie clip with a selection bar or highlight which moves to a different selection location each frame. You then create an action script which stops this clip on the first frame, or you can stop the animation from C++. Then using either actionscript keyh events, or C++ SetFrame() function, you set the frame of that element to the selection you wish to highlight (i.e. frame 1 is item 1, frame 2 is item 2, etc.)

Making an edit box...

Edit controls can be created in Xoink by handling the keyboard input from your application in C++, and dynamically changing the contents of a TextElemPlayer object in your animation when keys are pressed.

Making a slider...

Slider controls can be created from two buttons for up/down or left/right, and an animated "slider" bar button which stretches from small to large in an animation clip for the slider tab. You then override the position property of the slider button to set it's position in the slider control, and set its frame value to change it's size.

Making a list box...

List boxes can be created by displaying a column of text elements, with a slider (above). As the cursor in the list box changes, the text elements are updated to indicate the contents, and the slider is updated to indicate the new position.

Making a drop down menu/list...

Create a movie clip of the drop down box with the initial frame being empty, and animation of it becoming visible, and an animation of it becoming invisible. Add an action script to cause the animation to stop at the endof each transition. Then in C++, when the drop down is activated, call SetFrame() and then Play() to set the frame to the first frame of the show animation and let it play until it reaches the next stop command in its script. To hide, call SetFrame() and Play() again with the first frame of the hide transition animation, and let it play till it reaches the next stop script again.

Creating control classes in C++...

Since the style and mechanics of controls in most applications vary widely, no pre-defined set of user control classes have been provided in the Xoink library. However, you may find it useful to create your own custom set of control classes in C++ to automatically handle these functions for you in your application. Typically these classes would follow the form:

class MyDropDown
{
   MyList(XoinkPlayer* player, const char* elem_name, const char* show_label, const char* hide_label);

   void Show();
   void Hide();
   bool IsShowing();
};

Where the constructor would be passed the player object, the names of each of the element instances (i.e. flash movie clip symbol names) used by the control, and possibly frame numbers or frame labels used to control the animation timelines of these elements.

Xoink Architecture

The Xoink animation system has three primary sets of objects, the display objects which represent the graphics display system (XoinkCanvas, XoinkBitmap, XoinkFont), the movie "player" objects which keep track of the playback of an animation and control it (XoinkPlayer, XoinkTextElemPlayer, XoinkBitmapElemPlayer, XoinkAnimElemPlayer, XoinkButtonElemPlayer), and the "movie" data objects which statically store the data for an animation (XoinkMovie, XoinkTextElement, XoinkBitmapElement, XoinkAnimElement, XoinkButtonElement).

The XoinkCanvas object represents a platform dependent drawing surface. It supplies functions to draw platform bitmaps, and fonts, using the platform's blitter function. Each platform will have it's own XoinkCanvasXXXX, XoinkBitmapXXXX, and XoinkFontXXXX which provide the device specific functions relevant for each of these objects such as drawing clearing, etc.

The XoinkPlayer object represents a nested tree of XoinkAnimElemPlayer objects, with the leaves of the tree being XoinkBitmapElemPlayer or XoinkTextElemPlayer objects. Each animation player in the nested tree provides an independent timeline with the current time value, stopped/playing status, current x/y position, current enabled status, current scale, and current color for the bitmaps, text elements, or sub-animation beneath it. Each ElemPlayer object in the tree also has an index into the list of bitmaps, fonts, or animations stored in the XoinkMovie object it is playing. Note that there is a "many to one" relationship between the various "ElementPlayers" in the XoinkPlayer's player tree, and the "Elements" they index to in the static XoinkMovie object. In other words a single animation element in a movie object can have multiple instances in the player object just just as a single "symbol" in flash can be instanced many times in a flash animation.

The XoinkMovie object stores the static data for a movie. The movie object contains an array of generic XoinkBitmap objects, generic XoinkFont objects, and XoinkAnimation objects. XoinkAnimations correspond to the "symbols" in FLASH. Inside each animation is a list of XoinkBitmapElement which represent bitmap animations, XoinkTextElement objects which represent text animations, and XoinkAnimElement objects which are index another XoinkAnimation in the movie's animation array. Animations can not index themselves, or have circular references. This allows the player object to build a tree structure of players for the animations in a movie by recursively following the indexes between the animations and animation elements in the movies' animation array.

Each element (bitmap, text element, or animation) stores a keyframe animation array for its enabled status, position, scale, and color containing the time and value for each key. (NOTE: The XoinkButtonElem class is a minor variation on the XoinkAnimElement class, and is also stored in the animation's element list as an ordinary animation element.)

Each XoinkAnimElemPlayer object in the XoinkPlayer player tree has it's own timeline with it's own independent playing or stopped state and current time value. Like flash, all enabled animations will begin animating on the first frame of their parents timeline, and will independently stop and hold on the last frame unless an action-script command stops, restarts, or changes the timeline position for that timeline. One important point to note is that unlike flash, the time units in Xoink are not frames but continous time values measured in 1/3000 of a second. This means that unlike flash, xoink animations will interpolate smoothly between keyframes causing animations to appear more fluid.

To control the timelines in an animation, you use the FindPlayer() function to find the animation by FLASH instance name, then use the SetTime(), SetFrame(), Start() and Stop() functions. This will control the timeline for THAT PLAYER only, and will not affect the timelines for child players. Flash experts use this sort of independent control of animation timelines to produce a variety of effects such as changes in button states, resizable windows, etc. For example, a flash symbol with three different bitmaps in frames 1, 2, and 3, can be controlled from C++ to implement a 3 state image area by first using Stop() to stop that animation, then calling SetFrame(1), SetFrame(2), or SetFrame(3) to set the image you wish to show. Many other similar effects are possible. Since it is not possible to dynamically "add" or "remove" animations from a movie from C++, calling SetEnable(), Stop(), and SetFrame() allow you to control the visiblity and imagery of a particular element.

Xoink Graphics

The Xoink system displays animations using a dirty rectangle algorithm. This means that when positions or states of an item change, only those items which are different are redrawn. This means that on embedded devices with low CPU power, simple interfaces with selection bars and menus will be responsive, and no CPU power is wasted when there are no animations playing.

In effect, calling the animation system's draw function when no elements have changed does nothing. Elements in the animation are checked for changes, and if no changes have occured, the function returns.

The Xoink system was specifically designed to use only the features available on most embedded systems graphics blitter hardware. The minimal requirement is that the blitter support alpha channel blending, though scaling is also desirable as it allows more visually appealing UI effects.

Xoink also will use native device fonts if they are available, and native device font drawing. Only the font name and style are supplied as abstract values to the device specific drawing subsystem.

Porting Xoink

Xoink was designed to be trivial to port to new platforms. To allow this, the xoink library itself was designed to work with no dependencies on any external library except a minimal C runtime library (string functions, malloc, file io).

The device specific functions which must be ported are creating device bitmaps, performing device blits, and drawing device text. There are only about 12 functions to port, of which only 4 are non-trivial.

To port Xoink to a different platform, you must do the following:

1. Set the appropriate C runtime libraries used in your c runtime in Xoink.cpp XoinkInit() funciton.

2. Port the XoinkCanvasXXXX class to your platform. You must reprogram the following functions:

DrawBitmapToDevice - Draws a device bitmap to the device surface represented by this canvas
StretchBitmapToDevice - Stretches a device bitmap to the device surface represented by this canvas
DrawRectToDevice - Draws a rectangle to the device surface represented by the canvas
ClearDevice - Clears the device surface

The following functions can be overriden to provide additional functionality.

CreateNativeBitmap - Creates a device bitmap from a generic xoink bitmap
CreateNativeTextBitmap - Creates a device bitmap for a line of text given a device font
CreateNativeFont - Creates a native device font

3. Port the XoinkBitmapXXXX class to your platform. You must reprogram the following functions:

CreateNativeBitmap - Creates a device bitmap (usually called by canvas CreateNativeBitmap)
FreeNativeBitmap - Frees a device bitmap (usually called by canvas CreateNativeBitmap)
SetBits - Sets the bits in a device bitmap

The base implementation of the canvas functions in XoinkCanvas use the following bitmap functions:

DrawBitmap - Draws a device bitmap to another bitmap
StretchBitmap - Stretches a device bitmap to another bitmap
DrawText - Draws text to the the device bitmap (called by CreateNativeTextBitmap)
DrawRect - Draws a rect to a device bitmap
Clear - Clears a device bitmap

If your derived implementation of XoinkCanvasXXXX functions does not using drawing bitmaps to bitmaps, can leave these functions unimplemented. (Most embedded devices do not require or permit bitmap to bitmap drawing).

Typically the bitmap object will expose a pointer/handle to the devices native bitmap for use in the above canvas functions.

4. Port the XoinkFontXXX class to your platform. You must reprogram the following functions:

CreateNativeFont - Creates a native font given the font name/style
FreeNativeFont - Frees the native font

Typically the font object will expose a pointer/handle to the devices native font for use in the above canvas/bitmap functions.

If you port Xoink to a popular platform.. please consider contributing the source to your port back to the community. Remember what goes around comes around.

History/Credits (who done that thing?)

Xoink was first used to implement a simple UI for an HDTV video decoder card. For this application, both the Windows GDI graphics platform and a proprietary Zoran TL880 platform were created. The user interface files were loaded in a XoinkMovie, two canvases were created (one for Windows and one for the TL880), and then the movie was played back simultaneously on both canvases.

All interface elements for this project were created in MacroMedia flash, then converted to the Xoink binary XOI file format using the Xoink Viewer.

Later, the Xoink library was modified to be used as the user interface library for another set top project as well as a digital signage project to play back user designed SWF flash animations.

Contact

benjcooley at users.sourceforge.net

LICENSE

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.