void MainMenu::updateConnecting()
{
    /*
     * Cubes are in the 'connectingCubes' set between when they first connect
     * and when they become usable for the menu. They go through three states:
     *
     *   1. Displaying the Sifteo logo. This starts in cubeConnect(), and runs on a timer.
     *
     *   2. Loading assets. We start the load itself in cubeConnect(). After the logo
     *      finishes, we switch to displaying a progress animation.
     *
     *   3. When loading finishes, we draw an idle screen on the cube and remove it
     *      form connectingCubes.
     */

    SystemTime now = SystemTime::now();

    /*
     * Look for state transitions from (1) to (2)
     */

    CubeSet beginLoadingAnim;
    beginLoadingAnim.clear();

    for (CubeID cube : connectingCubes & ~loadingCubes) {
        if ((now - Shared::connectTime[cube]).milliseconds() >= kDisplayBlueLogoTimeMS) {
            loadingCubes.mark(cube);
            beginLoadingAnim.mark(cube);
        }
    }

    if (!beginLoadingAnim.empty()) {
        loadingAnimation.begin(beginLoadingAnim);
    }

    /*
     * Let cubes participate in the loading animation until the whole load is done
     */
    if (loadingCubes.empty()) {
        // nothing to do
        return;
    }

    if (!loader.isComplete()) {
        // Still loading, update progress
        loadingAnimation.paint(loadingCubes, loader.averageProgress(100));
        return;
    }

    // Loading is done!
    loadingAnimation.end(loadingCubes);

    // Draw an idle screen on each cube, and remove it from connectingCubes
    for (CubeID cube : loadingCubes) {
        auto& vid = Shared::video[cube];
        vid.initMode(BG0);
        vid.bg0.erase(Menu_StripeTile);
        vid.bg0.image(vec(0,0), Menu_IdleCube);

        connectingCubes.clear(cube);

        // Dispatch connected event to current applet now that the cube is ready
        if (itemIndexCurrent >= 0) {
            ASSERT(itemIndexCurrent < items.count());
            MainMenuItem *item = items[itemIndexCurrent];
            item->onCubeConnect(cube);

            // If a game was waiting on a cube to launch, try again.
            if (cubeRangeSavedIcon && areEnoughCubesConnected(itemIndexCurrent)) {
                itemIndexChoice = itemIndexCurrent;
                toggleCubeRangeAlert(); // remove the warning asking for more cubes
            }
        }

        updateCubeRangeAlert();
    }

    loadingCubes.clear();
}
void MainMenu::eventLoop()
{
    while (1) {

        // Need to bind the menu to a new cube?
        if (!mainCube.isDefined()) {

            /*
             * Make sure we have at least one cube. Until we do, there's nothing
             * we can do, so just play our "cube missing" sound periodically.
             *
             * This exits as soon as at least one cube is in CubeSet::connected(),
             * but that cube may still be busy loading assets or showing the logo.
             */
            waitForACube();

            /*
             * Wait until we have a cube that's usable for our menu
             */

            while (1) {
                CubeSet usable = CubeSet::connected() & ~connectingCubes;

                if (usable.empty()) {
                    System::paint();
                    updateMusic();
                    updateConnecting();
                    System::yield();
                    continue;
                } else {
                    mainCube = *usable.begin();
                    break;
                }
            }

            // try and avoid some of the garbage we often see :P
            System::finish();

            if (itemIndexCurrent >= 0) {
                if (itemIndexCurrent > 0 && items[itemIndexCurrent]->isFirstRun()) {
                    initMenu(itemIndexCurrent, true, 0);
                } else {
                    initMenu(itemIndexCurrent, true);
                }
            } else {
                initMenu(0, false);
            }
        }

        MenuEvent e;
        itemIndexChoice = -1;

        // Keep running until a choice is made or the menu cube disconnects
        while (mainCube.isDefined() && menu.pollEvent(&e)) {
            updateConnecting();
            updateSound();
            updateMusic();
            updateAlerts();
            handleEvent(e);
        }

        if (itemIndexChoice >= 0) {
            ASSERT(itemIndexChoice < items.count());
            return execItem(items[itemIndexChoice]);
        }
    }
}