root/src/servers/app/ScreenManager.cpp
/*
 * Copyright 2005-2009, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Axel Dörfler, axeld@pinc-software.de
 */

/**     Manages all available physical screens */


#include "ScreenManager.h"

#include "Screen.h"
#include "ServerConfig.h"

#include "RemoteHWInterface.h"

#include <Autolock.h>
#include <Entry.h>
#include <NodeMonitor.h>

#include <new>

using std::nothrow;


#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST
#       include "AccelerantHWInterface.h"
#else
#       include "ViewHWInterface.h"
#       include "DWindowHWInterface.h"
#endif


ScreenManager* gScreenManager;


class ScreenChangeListener : public HWInterfaceListener {
public:
                                                                ScreenChangeListener(ScreenManager& manager,
                                                                        Screen* screen);

private:
virtual void                                    ScreenChanged(HWInterface* interface);

                        ScreenManager&          fManager;
                        Screen*                         fScreen;
};


ScreenChangeListener::ScreenChangeListener(ScreenManager& manager,
        Screen* screen)
        :
        fManager(manager),
        fScreen(screen)
{
}


void
ScreenChangeListener::ScreenChanged(HWInterface* interface)
{
        fManager.ScreenChanged(fScreen);
}


ScreenManager::ScreenManager()
        :
        BLooper("screen manager"),
        fScreenList(4)
{
#ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
#       if defined(USE_DIRECT_WINDOW_TEST_MODE)
        _AddHWInterface(new DWindowHWInterface());
#       else
        _AddHWInterface(new ViewHWInterface());
#       endif
#else
        _ScanDrivers();

        // turn on node monitoring the graphics driver directory
        BEntry entry("/dev/graphics");
        node_ref nodeRef;
        if (entry.InitCheck() == B_OK && entry.GetNodeRef(&nodeRef) == B_OK)
                watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
#endif
}


ScreenManager::~ScreenManager()
{
}


Screen*
ScreenManager::ScreenAt(int32 index) const
{
        if (!IsLocked())
                debugger("Called ScreenManager::ScreenAt() without lock!");

        screen_item* item = fScreenList.ItemAt(index);
        if (item != NULL)
                return item->screen.Get();

        return NULL;
}


int32
ScreenManager::CountScreens() const
{
        if (!IsLocked())
                debugger("Called ScreenManager::CountScreens() without lock!");

        return fScreenList.CountItems();
}


status_t
ScreenManager::AcquireScreens(ScreenOwner* owner, int32* wishList,
        int32 wishCount, const char* target, bool force, ScreenList& list)
{
        BAutolock locker(this);
        int32 added = 0;

        // TODO: don't ignore the wish list

        for (int32 i = 0; i < fScreenList.CountItems(); i++) {
                screen_item* item = fScreenList.ItemAt(i);

                if (item->owner == NULL && list.AddItem(item->screen.Get())) {
                        item->owner = owner;
                        added++;
                }
        }

        if (added == 0 && target != NULL) {
                // there's a specific target screen we want to initialize
                // TODO: right now we only support remote screens, but we could
                // also target specific accelerants to support other graphics cards
                HWInterface* interface;
#ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
                interface = new(nothrow) ViewHWInterface();
#else
                interface = new(nothrow) RemoteHWInterface(target);
#endif
                if (interface != NULL) {
                        screen_item* item = _AddHWInterface(interface);
                        if (item != NULL && list.AddItem(item->screen.Get())) {
                                item->owner = owner;
                                added++;
                        }
                }
        }

        return added > 0 ? B_OK : B_ENTRY_NOT_FOUND;
}


void
ScreenManager::ReleaseScreens(ScreenList& list)
{
        BAutolock locker(this);

        for (int32 i = 0; i < fScreenList.CountItems(); i++) {
                screen_item* item = fScreenList.ItemAt(i);

                for (int32 j = 0; j < list.CountItems(); j++) {
                        Screen* screen = list.ItemAt(j);

                        if (item->screen.Get() == screen)
                                item->owner = NULL;
                }
        }
}


void
ScreenManager::ScreenChanged(Screen* screen)
{
        BAutolock locker(this);

        for (int32 i = 0; i < fScreenList.CountItems(); i++) {
                screen_item* item = fScreenList.ItemAt(i);
                if (item->screen.Get() == screen)
                        item->owner->ScreenChanged(screen);
        }
}


void
ScreenManager::_ScanDrivers()
{
        HWInterface* interface = NULL;

        // Eventually we will loop through drivers until
        // one can't initialize in order to support multiple monitors.
        // For now, we'll just load one and be done with it.

        // ToDo: to make monitoring the driver directory useful, we need more
        //      power and data here, and should do the scanning on our own

#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST
        bool initDrivers = true;
        while (initDrivers) {
                interface = new AccelerantHWInterface();

                _AddHWInterface(interface);
                initDrivers = false;
        }
#endif
}


ScreenManager::screen_item*
ScreenManager::_AddHWInterface(HWInterface* interface)
{
        ObjectDeleter<Screen> screen(
                new(nothrow) Screen(interface, fScreenList.CountItems()));
        if (!screen.IsSet()) {
                delete interface;
                return NULL;
        }

        // The interface is now owned by the screen

        if (screen->Initialize() >= B_OK) {
                screen_item* item = new(nothrow) screen_item;

                if (item != NULL) {
                        item->screen.SetTo(screen.Detach());
                        item->owner = NULL;
                        item->listener.SetTo(
                                new(nothrow) ScreenChangeListener(*this, item->screen.Get()));
                        if (item->listener.IsSet()
                                && interface->AddListener(item->listener.Get())) {
                                if (fScreenList.AddItem(item))
                                        return item;

                                interface->RemoveListener(item->listener.Get());
                        }

                        delete item;
                }
        }

        return NULL;
}


void
ScreenManager::MessageReceived(BMessage* message)
{
        switch (message->what) {
                case B_NODE_MONITOR:
                        // TODO: handle notification
                        break;

                default:
                        BHandler::MessageReceived(message);
        }
}