<commit_before>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "daemon.h"
#include "serializer.h"
#include "generator.h"
#include "device.h"

#include <QtCore/QTimer>

#include <kdebug.h>
#include <kdemacros.h>
#include <kaction.h>
#include <KLocalizedString>
#include <KActionCollection>
#include <KPluginFactory>

#include <kscreen/config.h>
#include <kscreen/configmonitor.h>

K_PLUGIN_FACTORY(KScreenDaemonFactory, registerPlugin<KScreenDaemon>();)
K_EXPORT_PLUGIN(KScreenDaemonFactory("kscreen", "kscreen"))

KScreenDaemon::KScreenDaemon(QObject* parent, const QList< QVariant >& )
 : KDEDModule(parent)
 , m_monitoredConfig(0)
 , m_iteration(0)
 , m_pendingSave(false)
 , m_monitoring(false)
 , m_timer(new QTimer())
{
    setenv("KSCREEN_BACKEND", "XRandR", 0);
    KActionCollection *coll = new KActionCollection(this);
    KAction* action = coll->addAction("display");
    action->setText(i18n("Switch Display" ));
    action->setGlobalShortcut(KShortcut(Qt::Key_Display));

    connect(Device::self(), SIGNAL(lidIsClosedChanged(bool,bool)), SLOT(lidClosedChanged(bool)));

    m_timer->setInterval(300);
    m_timer->setSingleShot(true);
    connect(m_timer, SIGNAL(timeout()), SLOT(applyGenericConfig()));
    connect(action, SIGNAL(triggered(bool)), SLOT(displayButton()));
    connect(Generator::self(), SIGNAL(ready()), SLOT(init()));
    monitorConnectedChange();
}

KScreenDaemon::~KScreenDaemon()
{
    Generator::destroy();
    Device::destroy();
}

void KScreenDaemon::init()
{
    applyConfig();
}

void KScreenDaemon::applyConfig()
{
    kDebug() << "Applying config";
    if (Serializer::configExists()) {
        applyKnownConfig();
        return;
    }

    applyIdealConfig();
}

void KScreenDaemon::applyKnownConfig()
{
    kDebug() << "Applying known config";
    setMonitorForChanges(false);
    KScreen::Config::setConfig(Serializer::config(Serializer::currentId()));
    QMetaObject::invokeMethod(this, "scheduleMonitorChange", Qt::QueuedConnection);
}

void KScreenDaemon::applyIdealConfig()
{
    kDebug() << "Applying ideal config";
    setMonitorForChanges(true);
    KScreen::Config::setConfig(Generator::self()->idealConfig());
}

void KScreenDaemon::configChanged()
{
    kDebug() << "Change detected";
    if (m_pendingSave) {
        return;
    }

    kDebug() << "Scheduling screen save";
    m_pendingSave = true;
    QMetaObject::invokeMethod(this, "saveCurrentConfig", Qt::QueuedConnection);
}

void KScreenDaemon::saveCurrentConfig()
{
    kDebug() << "Saving current config";
    m_pendingSave = false;
    Serializer::saveConfig(KScreen::Config::current());
}

void KScreenDaemon::displayButton()
{
    kDebug() << "displayBtn triggered";
    if (m_timer->isActive()) {
        kDebug() << "Too fast cowboy";
        return;
    }

    m_timer->start();
}

void KScreenDaemon::resetDisplaySwitch()
{
    kDebug();
    m_iteration = 0;
}

void KScreenDaemon::applyGenericConfig()
{
    if (m_iteration == 5) {
        m_iteration = 0;
    }

    setMonitorForChanges(true);
    m_iteration++;
    kDebug() << "displayButton: " << m_iteration;
    KDebug::Block genericConfig("Applying display switch");
    KScreen::Config::setConfig(Generator::self()->displaySwitch(m_iteration));
}

void KScreenDaemon::lidClosedChanged(bool lidIsClosed)
{
    KDebug::Block genericConfig(" Lid closed");
    kDebug() << "Lid is closed:" << lidIsClosed;
    //If the laptop is closed, use ideal config WITHOUT saving it
    if (lidIsClosed) {
        setMonitorForChanges(false);
        KScreen::Config::setConfig(Generator::self()->idealConfig());
        return;
    }

    //If the lid is open, restore config (or generate a new one if needed
    applyConfig();
}

void KScreenDaemon::monitorConnectedChange()
{
    if (!m_monitoredConfig) {
        m_monitoredConfig = KScreen::Config::current();
        if (!m_monitoredConfig) {
            return;
        }

        KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig);
    }

    KScreen::OutputList outputs = m_monitoredConfig->outputs();
    Q_FOREACH(KScreen::Output* output, outputs) {
        connect(output, SIGNAL(isConnectedChanged()), SLOT(applyConfig()));
        connect(output, SIGNAL(isConnectedChanged()), SLOT(resetDisplaySwitch()));
    }
}

void KScreenDaemon::setMonitorForChanges(bool enabled)
{
    if (m_monitoring == enabled) {
        return;
    }
    kDebug() << "Monitor for changes: " << enabled;
    if (!m_monitoredConfig) {
        m_monitoredConfig = KScreen::Config::current();
        if (!m_monitoredConfig) {
            return;
        }
        KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig);
    }

    m_monitoring = enabled;

    KScreen::OutputList outputs = m_monitoredConfig->outputs();
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (m_monitoring) {
            enableMonitor(output);
        } else {
            disableMonitor(output);
        }
    }
}

void KScreenDaemon::scheduleMonitorChange()
{
    QMetaObject::invokeMethod(this, "setMonitorForChanges", Qt::QueuedConnection, Q_ARG(bool, true));
}

void KScreenDaemon::enableMonitor(KScreen::Output* output)
{
    connect(output, SIGNAL(currentModeIdChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(isEnabledChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(isPrimaryChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(outputChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(clonesChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(posChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(rotationChanged()), SLOT(configChanged()));
}

void KScreenDaemon::disableMonitor(KScreen::Output* output)
{
    disconnect(output, SIGNAL(currentModeIdChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(isEnabledChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(isPrimaryChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(outputChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(clonesChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(posChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(rotationChanged()), this, SLOT(configChanged()));
}
<commit_msg>Check if the backend is loaded correctly before doing anything.<commit_after>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "daemon.h"
#include "serializer.h"
#include "generator.h"
#include "device.h"

#include <QtCore/QTimer>

#include <kdebug.h>
#include <kdemacros.h>
#include <kaction.h>
#include <KLocalizedString>
#include <KActionCollection>
#include <KPluginFactory>

#include <kscreen/config.h>
#include <kscreen/configmonitor.h>

K_PLUGIN_FACTORY(KScreenDaemonFactory, registerPlugin<KScreenDaemon>();)
K_EXPORT_PLUGIN(KScreenDaemonFactory("kscreen", "kscreen"))

KScreenDaemon::KScreenDaemon(QObject* parent, const QList< QVariant >& )
 : KDEDModule(parent)
 , m_monitoredConfig(0)
 , m_iteration(0)
 , m_pendingSave(false)
 , m_monitoring(false)
 , m_timer(new QTimer())
{
    setenv("KSCREEN_BACKEND", "XRandR", 0);
    if (!KScreen::Config::loadBackend()) {
        kWarning() << "Couldn't load backend: " << getenv("KSCREEN_BACKEND");
        return;
    }
    KActionCollection *coll = new KActionCollection(this);
    KAction* action = coll->addAction("display");
    action->setText(i18n("Switch Display" ));
    action->setGlobalShortcut(KShortcut(Qt::Key_Display));

    connect(Device::self(), SIGNAL(lidIsClosedChanged(bool,bool)), SLOT(lidClosedChanged(bool)));

    m_timer->setInterval(300);
    m_timer->setSingleShot(true);
    connect(m_timer, SIGNAL(timeout()), SLOT(applyGenericConfig()));
    connect(action, SIGNAL(triggered(bool)), SLOT(displayButton()));
    connect(Generator::self(), SIGNAL(ready()), SLOT(init()));
    monitorConnectedChange();
}

KScreenDaemon::~KScreenDaemon()
{
    Generator::destroy();
    Device::destroy();
}

void KScreenDaemon::init()
{
    applyConfig();
}

void KScreenDaemon::applyConfig()
{
    kDebug() << "Applying config";
    if (Serializer::configExists()) {
        applyKnownConfig();
        return;
    }

    applyIdealConfig();
}

void KScreenDaemon::applyKnownConfig()
{
    kDebug() << "Applying known config";
    setMonitorForChanges(false);
    KScreen::Config::setConfig(Serializer::config(Serializer::currentId()));
    QMetaObject::invokeMethod(this, "scheduleMonitorChange", Qt::QueuedConnection);
}

void KScreenDaemon::applyIdealConfig()
{
    kDebug() << "Applying ideal config";
    setMonitorForChanges(true);
    KScreen::Config::setConfig(Generator::self()->idealConfig());
}

void KScreenDaemon::configChanged()
{
    kDebug() << "Change detected";
    if (m_pendingSave) {
        return;
    }

    kDebug() << "Scheduling screen save";
    m_pendingSave = true;
    QMetaObject::invokeMethod(this, "saveCurrentConfig", Qt::QueuedConnection);
}

void KScreenDaemon::saveCurrentConfig()
{
    kDebug() << "Saving current config";
    m_pendingSave = false;
    Serializer::saveConfig(KScreen::Config::current());
}

void KScreenDaemon::displayButton()
{
    kDebug() << "displayBtn triggered";
    if (m_timer->isActive()) {
        kDebug() << "Too fast cowboy";
        return;
    }

    m_timer->start();
}

void KScreenDaemon::resetDisplaySwitch()
{
    kDebug();
    m_iteration = 0;
}

void KScreenDaemon::applyGenericConfig()
{
    if (m_iteration == 5) {
        m_iteration = 0;
    }

    setMonitorForChanges(true);
    m_iteration++;
    kDebug() << "displayButton: " << m_iteration;
    KDebug::Block genericConfig("Applying display switch");
    KScreen::Config::setConfig(Generator::self()->displaySwitch(m_iteration));
}

void KScreenDaemon::lidClosedChanged(bool lidIsClosed)
{
    KDebug::Block genericConfig(" Lid closed");
    kDebug() << "Lid is closed:" << lidIsClosed;
    //If the laptop is closed, use ideal config WITHOUT saving it
    if (lidIsClosed) {
        setMonitorForChanges(false);
        KScreen::Config::setConfig(Generator::self()->idealConfig());
        return;
    }

    //If the lid is open, restore config (or generate a new one if needed
    applyConfig();
}

void KScreenDaemon::monitorConnectedChange()
{
    if (!m_monitoredConfig) {
        m_monitoredConfig = KScreen::Config::current();
        if (!m_monitoredConfig) {
            return;
        }

        KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig);
    }

    KScreen::OutputList outputs = m_monitoredConfig->outputs();
    Q_FOREACH(KScreen::Output* output, outputs) {
        connect(output, SIGNAL(isConnectedChanged()), SLOT(applyConfig()));
        connect(output, SIGNAL(isConnectedChanged()), SLOT(resetDisplaySwitch()));
    }
}

void KScreenDaemon::setMonitorForChanges(bool enabled)
{
    if (m_monitoring == enabled) {
        return;
    }
    kDebug() << "Monitor for changes: " << enabled;
    if (!m_monitoredConfig) {
        m_monitoredConfig = KScreen::Config::current();
        if (!m_monitoredConfig) {
            return;
        }
        KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig);
    }

    m_monitoring = enabled;

    KScreen::OutputList outputs = m_monitoredConfig->outputs();
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (m_monitoring) {
            enableMonitor(output);
        } else {
            disableMonitor(output);
        }
    }
}

void KScreenDaemon::scheduleMonitorChange()
{
    QMetaObject::invokeMethod(this, "setMonitorForChanges", Qt::QueuedConnection, Q_ARG(bool, true));
}

void KScreenDaemon::enableMonitor(KScreen::Output* output)
{
    connect(output, SIGNAL(currentModeIdChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(isEnabledChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(isPrimaryChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(outputChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(clonesChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(posChanged()), SLOT(configChanged()));
    connect(output, SIGNAL(rotationChanged()), SLOT(configChanged()));
}

void KScreenDaemon::disableMonitor(KScreen::Output* output)
{
    disconnect(output, SIGNAL(currentModeIdChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(isEnabledChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(isPrimaryChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(outputChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(clonesChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(posChanged()), this, SLOT(configChanged()));
    disconnect(output, SIGNAL(rotationChanged()), this, SLOT(configChanged()));
}
<|endoftext|>


<commit_before>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "device.h"
#include "kded/freedesktop_interface.h"

Device* Device::m_instance = 0;

Device* Device::self()
{
    if (!Device::m_instance) {
        m_instance = new Device();
    }

    return m_instance;
}

void Device::destroy()
{
    delete m_instance;
    m_instance = 0;
}

Device::Device(QObject* parent)
 : QObject(parent)
 , m_isReady(false)
 , m_isLaptop(false)
 , m_isLidClosed(false)
 , m_isDocked(false)
{
    m_freedesktop = new OrgFreedesktopDBusPropertiesInterface("org.freedesktop.UPower",
                                                              "/org/freedesktop/UPower",
                                                              QDBusConnection::systemBus());

    QDBusConnection::systemBus().connect("org.freedesktop.UPower", "/org/freedesktop/UPower",
                                         "org.freedesktop.UPower", "Changed", this, SLOT(changed()));

    QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
}

Device::~Device()
{
    delete m_freedesktop;
}

void Device::init()
{
    fetchIsLaptop();
}

void Device::changed()
{
    fetchLidIsClosed();
}

void Device::setReady()
{
    m_isReady = true;
    Q_EMIT ready();
}

bool Device::isReady()
{
    return m_isReady;
}

bool Device::isLaptop()
{
    return m_isLaptop;
}

bool Device::isLidClosed()
{
    return m_isLidClosed;
}

bool Device::isDocked()
{
    return m_isDocked;
}

void Device::fetchIsLaptop()
{
    QDBusPendingReply<QVariant> res = m_freedesktop->Get("org.freedesktop.UPower", "LidIsPresent");
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(res);
    connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(isLaptopFetched(QDBusPendingCallWatcher*)));
}

void Device::isLaptopFetched(QDBusPendingCallWatcher* watcher)
{
    const QDBusPendingReply<QVariant> reply = *watcher;
    if (reply.isError()) {
        qDebug() << "Couldn't get if the device is a laptop: " << reply.error().message();
        return;
    }

    m_isLaptop = reply.value().toBool();
    watcher->deleteLater();

    if (!m_isLaptop) {
        setReady();
        return;
    }

    fetchLidIsClosed();
}

void Device::fetchLidIsClosed()
{
    QDBusPendingReply<QVariant> res = m_freedesktop->Get("org.freedesktop.UPower", "LidIsClosed");
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(res);
    connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(isLidClosedFetched(QDBusPendingCallWatcher*)));
}

void Device::isLidClosedFetched(QDBusPendingCallWatcher* watcher)
{
    const QDBusPendingReply<QVariant> reply = *watcher;
    if (reply.isError()) {
        qDebug() << "Couldn't get if the device is a laptop: " << reply.error().message();
        return;
    }

    bool oldValue = m_isLidClosed;;
    m_isLidClosed = reply.value().toBool();
    watcher->deleteLater();

    if (m_isReady && (oldValue != m_isLidClosed)) {
        Q_EMIT lidIsClosedChanged(m_isLidClosed, oldValue);
    }

    fetchIsDocked();
}

void Device::fetchIsDocked()
{
    setReady();
}<commit_msg>minor fixes<commit_after>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "device.h"
#include "kded/freedesktop_interface.h"

Device* Device::m_instance = 0;

Device* Device::self()
{
    if (!Device::m_instance) {
        m_instance = new Device();
    }

    return m_instance;
}

void Device::destroy()
{
    delete m_instance;
    m_instance = 0;
}

Device::Device(QObject* parent)
 : QObject(parent)
 , m_isReady(false)
 , m_isLaptop(false)
 , m_isLidClosed(false)
 , m_isDocked(false)
{
    m_freedesktop = new OrgFreedesktopDBusPropertiesInterface("org.freedesktop.UPower",
                                                              "/org/freedesktop/UPower",
                                                              QDBusConnection::systemBus());

    QDBusConnection::systemBus().connect("org.freedesktop.UPower", "/org/freedesktop/UPower",
                                         "org.freedesktop.UPower", "Changed", this, SLOT(changed()));

    QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
}

Device::~Device()
{
    delete m_freedesktop;
}

void Device::init()
{
    fetchIsLaptop();
}

void Device::changed()
{
    fetchLidIsClosed();
}

void Device::setReady()
{
    m_isReady = true;
    Q_EMIT ready();
}

bool Device::isReady()
{
    return m_isReady;
}

bool Device::isLaptop()
{
    return m_isLaptop;
}

bool Device::isLidClosed()
{
    return m_isLidClosed;
}

bool Device::isDocked()
{
    return m_isDocked;
}

void Device::fetchIsLaptop()
{
    QDBusPendingReply<QVariant> res = m_freedesktop->Get("org.freedesktop.UPower", "LidIsPresent");
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(res);
    connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(isLaptopFetched(QDBusPendingCallWatcher*)));
}

void Device::isLaptopFetched(QDBusPendingCallWatcher* watcher)
{
    const QDBusPendingReply<QVariant> reply = *watcher;
    if (reply.isError()) {
        qDebug() << "Couldn't get if the device is a laptop: " << reply.error().message();
        return;
    }

    m_isLaptop = reply.value().toBool();
    watcher->deleteLater();

    if (!m_isLaptop) {
        setReady();
        return;
    }

    fetchLidIsClosed();
}

void Device::fetchLidIsClosed()
{
    QDBusPendingReply<QVariant> res = m_freedesktop->Get("org.freedesktop.UPower", "LidIsClosed");
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(res);
    connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(isLidClosedFetched(QDBusPendingCallWatcher*)));
}

void Device::isLidClosedFetched(QDBusPendingCallWatcher* watcher)
{
    const QDBusPendingReply<QVariant> reply = *watcher;
    if (reply.isError()) {
        qDebug() << "Couldn't get if the laptop has the lid closed: " << reply.error().message();
        return;
    }

    bool oldValue = m_isLidClosed;
    m_isLidClosed = reply.value().toBool();
    watcher->deleteLater();

    if (m_isReady && (oldValue != m_isLidClosed)) {
        Q_EMIT lidIsClosedChanged(m_isLidClosed, oldValue);
    }

    fetchIsDocked();
}

void Device::fetchIsDocked()
{
    setReady();
}
<|endoftext|>


<commit_before>#include "buddy.hh"
#if BUDDY_DEBUG
#include "kstream.hh"
#endif

#include <cassert>
#include <cstring>
#include <iterator>

using namespace std;

buddy_allocator::buddy_allocator(void *base, size_t len)
{
  uintptr_t end = (uintptr_t)base + len;

  // Use a linear allocator to allocate buddy bitmaps.
  // XXX(Austin) Should we check if we have more unaligned space at
  // the end?  Currently, we skip 16 megs between each buddy region.
  size_t nBlocks = len / MIN_SIZE;
  for (size_t i = 0; i < MAX_ORDER; ++i) {
    size_t bitmapSize = (nBlocks + 7) / 8;
    orders[i].bitmap = (unsigned char*)base;
    memset(orders[i].bitmap, 0, bitmapSize);
    base = (char*)base + bitmapSize;
#if BUDDY_DEBUG
    orders[i].debug = (unsigned char*)base;
    memset(orders[i].debug, 0, bitmapSize);
    base = (char*)base + bitmapSize;
#endif
    nBlocks /= 2;
  }

  // The MAX_ORDER bitmap is unused because it doesn't have buddy
  // pairs.
  orders[MAX_ORDER].bitmap = nullptr;
#if BUDDY_DEBUG
  orders[MAX_ORDER].debug = nullptr;
#endif

  // Round both bounds to multiples of MIN_SIZE << MAX_ORDER.  Without
  // this, some blocks wouldn't have buddies, which would complicate
  // other logic.
  this->base = ((uintptr_t)base + MAX_SIZE - 1) & ~(MAX_SIZE - 1);
  limit = end & ~(MAX_SIZE - 1);
  assert(this->base < limit);

  // Create block list
  for (uintptr_t block = this->base; block < limit; block += MAX_SIZE)
    orders[MAX_ORDER].blocks.push_back((struct block*)block);
}

void*
buddy_allocator::alloc_order(size_t order)
{
  // Is a block of this order available?
  if (!orders[order].blocks.empty()) {
    // Get a block
    struct block *block = &orders[order].blocks.front();
    orders[order].blocks.pop_front();

    // Mark it as allocated
    if (order < MAX_ORDER) {
      bool state = flip_bit(block, order);
      // Now both buddies must be allocated (otherwise they would have
      // been promoted).
      assert(state == 0);
      mark_allocated(block, order, true);
    }

    return (void*)block;
  } else if (order == MAX_ORDER) {
    return nullptr;
  } else {
    // We need to split a block.  We'll allocate one of order + 1, use
    // the first half of it and add the second half to order's free
    // list.
    void *parent = alloc_order(order + 1);
    if (!parent)
      return nullptr;
    struct block *second_half =
      (struct block*)((char*)parent + (MIN_SIZE << order));
    orders[order].blocks.push_front(second_half);

    // Mark this pair as half-allocated
    bool state = flip_bit(parent, order);
    assert(state == 1);
    mark_allocated(parent, order, true);

    return parent;
  }
}

void
buddy_allocator::free_order(void *ptr, size_t order)
{
  assert(base <= (uintptr_t)ptr && (uintptr_t)ptr < limit);
#if BUDDY_DEBUG
  if (order < MAX_ORDER)
    if (!is_allocated(ptr, order))
      spanic.println("double free or too-small free of ", ptr,
                     " of size ", MIN_SIZE << order, " (order ", order, ")");
  if (order > 0)
    if (is_allocated(ptr, order - 1))
      spanic.println("too-large free of ", ptr,
                     " of size ", MIN_SIZE << order, " (order ", order, ")");
#endif
  mark_allocated(ptr, order, false);

  if (order < MAX_ORDER && flip_bit(ptr, order) == 0) {
    // This block's buddy is also free.  Remove the buddy from its
    // list, combine them, and free to the higher order.
    uintptr_t buddy = (uintptr_t)ptr ^ ((uintptr_t)MIN_SIZE << order);
    orders[order].blocks.erase(
      orders[order].blocks.iterator_to((struct block*)buddy));
    uintptr_t parent = (uintptr_t)ptr & ~((uintptr_t)MIN_SIZE << order);
    free_order((void*)parent, order + 1);
  } else {
    // This block's buddy is allocated.  Release this block to the
    // current order.
    orders[order].blocks.push_front((struct block*)ptr);
  }
}

bool
buddy_allocator::flip_bit(void *ptr, size_t order)
{
  size_t bit = (((uintptr_t)ptr - base) / MIN_SIZE) >> (order + 1);
  unsigned char mask = 1 << (bit % 8);
  return (orders[order].bitmap[bit / 8] ^= mask) & mask;
}

#if BUDDY_DEBUG
bool
buddy_allocator::is_allocated(void *ptr, size_t order)
{
  size_t bit = (((uintptr_t)ptr - base) / MIN_SIZE) >> (order + 1);
  unsigned char mask = 1 << (bit % 8);
  uintptr_t parent = (uintptr_t)ptr & ~((uintptr_t)MIN_SIZE << order);
  bool debug = orders[order].debug[bit / 8] & mask;
  if ((uintptr_t)ptr == parent)
    // First of buddy pair
    return debug;
  else
    // Second of buddy pair
    return debug ^ !!(orders[order].bitmap[bit / 8] & mask);
}

void
buddy_allocator::mark_allocated(void *ptr, std::size_t order, bool allocated)
{
  uintptr_t parent = (uintptr_t)ptr & ~((uintptr_t)MIN_SIZE << order);
  if ((uintptr_t)ptr != parent)
    return;
  size_t bit = (((uintptr_t)ptr - base) / MIN_SIZE) >> (order + 1);
  unsigned char mask = 1 << (bit % 8);
  if (allocated)
    orders[order].debug[bit / 8] |= mask;
  else
    orders[order].debug[bit / 8] &= ~mask;
}
#else
void
buddy_allocator::mark_allocated(void *ptr, std::size_t order, bool allocated)
{
}
#endif

void
buddy_allocator::get_stats(stats *out) const
{
  out->free = 0;
  for (size_t order = 0; order <= MAX_ORDER; ++order) {
    out->nfree[order] = 0;
    for (auto &b : orders[order].blocks) {
      (void)b;                  // Hush g++
      ++out->nfree[order];
    }
    out->free += out->nfree[order] * (MIN_SIZE << order);
  }
}
<commit_msg>buddy: Avoid nullptr dereference in debugging code<commit_after>#include "buddy.hh"
#if BUDDY_DEBUG
#include "kstream.hh"
#endif

#include <cassert>
#include <cstring>
#include <iterator>

using namespace std;

buddy_allocator::buddy_allocator(void *base, size_t len)
{
  uintptr_t end = (uintptr_t)base + len;

  // Use a linear allocator to allocate buddy bitmaps.
  // XXX(Austin) Should we check if we have more unaligned space at
  // the end?  Currently, we skip 16 megs between each buddy region.
  size_t nBlocks = len / MIN_SIZE;
  for (size_t i = 0; i < MAX_ORDER; ++i) {
    size_t bitmapSize = (nBlocks + 7) / 8;
    orders[i].bitmap = (unsigned char*)base;
    memset(orders[i].bitmap, 0, bitmapSize);
    base = (char*)base + bitmapSize;
#if BUDDY_DEBUG
    orders[i].debug = (unsigned char*)base;
    memset(orders[i].debug, 0, bitmapSize);
    base = (char*)base + bitmapSize;
#endif
    nBlocks /= 2;
  }

  // The MAX_ORDER bitmap is unused because it doesn't have buddy
  // pairs.
  orders[MAX_ORDER].bitmap = nullptr;
#if BUDDY_DEBUG
  orders[MAX_ORDER].debug = nullptr;
#endif

  // Round both bounds to multiples of MIN_SIZE << MAX_ORDER.  Without
  // this, some blocks wouldn't have buddies, which would complicate
  // other logic.
  this->base = ((uintptr_t)base + MAX_SIZE - 1) & ~(MAX_SIZE - 1);
  limit = end & ~(MAX_SIZE - 1);
  assert(this->base < limit);

  // Create block list
  for (uintptr_t block = this->base; block < limit; block += MAX_SIZE)
    orders[MAX_ORDER].blocks.push_back((struct block*)block);
}

void*
buddy_allocator::alloc_order(size_t order)
{
  // Is a block of this order available?
  if (!orders[order].blocks.empty()) {
    // Get a block
    struct block *block = &orders[order].blocks.front();
    orders[order].blocks.pop_front();

    // Mark it as allocated
    if (order < MAX_ORDER) {
      bool state = flip_bit(block, order);
      // Now both buddies must be allocated (otherwise they would have
      // been promoted).
      assert(state == 0);
      mark_allocated(block, order, true);
    }

    return (void*)block;
  } else if (order == MAX_ORDER) {
    return nullptr;
  } else {
    // We need to split a block.  We'll allocate one of order + 1, use
    // the first half of it and add the second half to order's free
    // list.
    void *parent = alloc_order(order + 1);
    if (!parent)
      return nullptr;
    struct block *second_half =
      (struct block*)((char*)parent + (MIN_SIZE << order));
    orders[order].blocks.push_front(second_half);

    // Mark this pair as half-allocated
    bool state = flip_bit(parent, order);
    assert(state == 1);
    mark_allocated(parent, order, true);

    return parent;
  }
}

void
buddy_allocator::free_order(void *ptr, size_t order)
{
  assert(base <= (uintptr_t)ptr && (uintptr_t)ptr < limit);
#if BUDDY_DEBUG
  if (order < MAX_ORDER)
    if (!is_allocated(ptr, order))
      spanic.println("double free or too-small free of ", ptr,
                     " of size ", MIN_SIZE << order, " (order ", order, ")");
  if (order > 0)
    if (is_allocated(ptr, order - 1))
      spanic.println("too-large free of ", ptr,
                     " of size ", MIN_SIZE << order, " (order ", order, ")");
#endif
  mark_allocated(ptr, order, false);

  if (order < MAX_ORDER && flip_bit(ptr, order) == 0) {
    // This block's buddy is also free.  Remove the buddy from its
    // list, combine them, and free to the higher order.
    uintptr_t buddy = (uintptr_t)ptr ^ ((uintptr_t)MIN_SIZE << order);
    orders[order].blocks.erase(
      orders[order].blocks.iterator_to((struct block*)buddy));
    uintptr_t parent = (uintptr_t)ptr & ~((uintptr_t)MIN_SIZE << order);
    free_order((void*)parent, order + 1);
  } else {
    // This block's buddy is allocated.  Release this block to the
    // current order.
    orders[order].blocks.push_front((struct block*)ptr);
  }
}

bool
buddy_allocator::flip_bit(void *ptr, size_t order)
{
  size_t bit = (((uintptr_t)ptr - base) / MIN_SIZE) >> (order + 1);
  unsigned char mask = 1 << (bit % 8);
  return (orders[order].bitmap[bit / 8] ^= mask) & mask;
}

#if BUDDY_DEBUG
bool
buddy_allocator::is_allocated(void *ptr, size_t order)
{
  size_t bit = (((uintptr_t)ptr - base) / MIN_SIZE) >> (order + 1);
  unsigned char mask = 1 << (bit % 8);
  uintptr_t parent = (uintptr_t)ptr & ~((uintptr_t)MIN_SIZE << order);
  bool debug = orders[order].debug[bit / 8] & mask;
  if ((uintptr_t)ptr == parent)
    // First of buddy pair
    return debug;
  else
    // Second of buddy pair
    return debug ^ !!(orders[order].bitmap[bit / 8] & mask);
}

void
buddy_allocator::mark_allocated(void *ptr, std::size_t order, bool allocated)
{
  if (order == MAX_ORDER)
    return;
  uintptr_t parent = (uintptr_t)ptr & ~((uintptr_t)MIN_SIZE << order);
  if ((uintptr_t)ptr != parent)
    return;
  size_t bit = (((uintptr_t)ptr - base) / MIN_SIZE) >> (order + 1);
  unsigned char mask = 1 << (bit % 8);
  if (allocated)
    orders[order].debug[bit / 8] |= mask;
  else
    orders[order].debug[bit / 8] &= ~mask;
}
#else
void
buddy_allocator::mark_allocated(void *ptr, std::size_t order, bool allocated)
{
}
#endif

void
buddy_allocator::get_stats(stats *out) const
{
  out->free = 0;
  for (size_t order = 0; order <= MAX_ORDER; ++order) {
    out->nfree[order] = 0;
    for (auto &b : orders[order].blocks) {
      (void)b;                  // Hush g++
      ++out->nfree[order];
    }
    out->free += out->nfree[order] * (MIN_SIZE << order);
  }
}
<|endoftext|>


<commit_before>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"

#include "material.h"
#include "renderer.h"
#include "Globals.h"
#include "utilities.h"
#include "Logs.h"

opengl_material::opengl_material()
{
    for (size_t i = 0; i < params.size(); i++)
        params[i] = glm::vec4(std::numeric_limits<float>::quiet_NaN());
}

bool
opengl_material::deserialize( cParser &Input, bool const Loadnow ) {
    parse_info = std::make_unique<parse_info_s>();

    bool result { false };
    while( true == deserialize_mapping( Input, 0, Loadnow ) ) {
        result = true; // once would suffice but, eh
    }

    return result;
}

void opengl_material::log_error(const std::string &str)
{
    ErrorLog("bad material: " + name + ": " + str, logtype::material);
}

void opengl_material::finalize(bool Loadnow)
{
    if (parse_info)
    {
        for (auto it : parse_info->tex_mapping)
        {
            std::string key = it.first;
            std::string value = it.second.name;

            if (key.size() > 0 && key[0] != '_')
            {
                size_t num = std::stoi(key) - 1;
                if (num < gl::MAX_TEXTURES)
                    textures[num] = GfxRenderer.Fetch_Texture(value, Loadnow);
                else
                    log_error("invalid texture binding: " + std::to_string(num));
            }
            else if (key.size() > 2)
            {
                key.erase(0, 1);
                key.pop_back();
                if (shader && shader->texture_conf.find(key) != shader->texture_conf.end())
                    textures[shader->texture_conf[key].id] = GfxRenderer.Fetch_Texture(value, Loadnow);
                else
                    log_error("unknown texture binding: " + key);
            }
            else
                log_error("unrecognized texture binding: " + key);
        }

        if (!shader)
        {
            if (textures[0] == null_handle)
            {
                log_error("shader not specified, assuming \"default_0\"");
                shader = GfxRenderer.Fetch_Shader("default_0");
            }
            else if (textures[1] == null_handle)
            {
                log_error("shader not specified, assuming \"default_1\"");
                shader = GfxRenderer.Fetch_Shader("default_1");
            }
            else if (textures[2] == null_handle)
            {
                log_error("shader not specified, assuming \"default_2\"");
                shader = GfxRenderer.Fetch_Shader("default_2");
            }
        }

        for (auto it : parse_info->param_mapping)
        {
            std::string key = it.first;
            glm::vec4 value = it.second.data;

            if (key.size() > 1 && key[0] != '_')
            {
                size_t num = std::stoi(key) - 1;
                if (num < gl::MAX_PARAMS)
                    params[num] = value;
                else
                    log_error("invalid param binding: " + std::to_string(num));
            }
            else if (key.size() > 2)
            {
                key.erase(0, 1);
                key.pop_back();
                if (shader->param_conf.find(key) != shader->param_conf.end())
                {
                    gl::shader::param_entry entry = shader->param_conf[key];
                    for (size_t i = 0; i < entry.size; i++)
                        params[entry.location][entry.offset + i] = value[i];
                }
                else
                    log_error("unknown param binding: " + key);
            }
            else
                log_error("unrecognized param binding: " + key);
        }

        parse_info.reset();
    }

    for (auto it : shader->param_conf)
    {
        gl::shader::param_entry entry = it.second;
        if (std::isnan(params[entry.location][entry.offset]))
        {
            float value = std::numeric_limits<float>::quiet_NaN();
            if (entry.defaultparam == gl::shader::defaultparam_e::one)
                value = 1.0f;
            else if (entry.defaultparam == gl::shader::defaultparam_e::zero)
                value = 0.0f;
            else if (entry.defaultparam == gl::shader::defaultparam_e::required)
                log_error("unspecified required param: " + it.first);
            else if (entry.defaultparam != gl::shader::defaultparam_e::nan)
            {
                params_state.push_back(entry);
                continue;
            }

            for (size_t i = 0; i < entry.size; i++)
                params[entry.location][entry.offset + i] = value;
        }
    }

    for (auto it : shader->texture_conf)
    {
        gl::shader::texture_entry &entry = it.second;
        texture_handle handle = textures[entry.id];
        if (handle)
            GfxRenderer.Texture(handle).set_components_hint((GLint)entry.components);
    }
}

// imports member data pair from the config file
bool
opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow ) {
    // token can be a key or block end
    std::string key = Input.getToken<std::string>( true, "\n\r\t  ,;[]" );

    if( ( true == key.empty() ) || ( key == "}" ) ) { return false; }

    auto value { Input.getToken<std::string>( true, "\n\r\t ,;" ) };

    if( Priority != -1 ) {
        // regular attribute processing mode
        if( key == Global.Weather ) {
            // weather textures override generic (pri 0) and seasonal (pri 1) textures
            // seasonal weather textures (pri 1+2=3) override generic weather (pri 2) textures
            while( true == deserialize_mapping( Input, Priority + 2, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
        else if( key == Global.Season ) {
            // seasonal textures override generic textures
            while( true == deserialize_mapping( Input, 1, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
        else if (key.compare(0, 7, "texture") == 0) {
            key.erase(0, 7);

            std::replace(value.begin(), value.end(), '\\', '/');
            auto it = parse_info->tex_mapping.find(key);
            if (it == parse_info->tex_mapping.end())
                parse_info->tex_mapping.emplace(std::make_pair(key, parse_info_s::tex_def({ value, Priority })));
            else if (Priority > it->second.priority)
            {
                parse_info->tex_mapping.erase(it);
                parse_info->tex_mapping.emplace(std::make_pair(key, parse_info_s::tex_def({ value, Priority })));
            }
/*
            size_t num = std::stoi(key) - 1;
            if (num < textures.size() &&
                    (textures[num] == null_handle || Priority > m_texture_priority[num]))
            {
				std::replace(value.begin(), value.end(), '\\', '/');
                textures[num] = GfxRenderer.Fetch_Texture( value, Loadnow );
                m_texture_priority[num] = Priority;
            }
            */
        }
        else if (key.compare(0, 5, "param") == 0) {
            key.erase(0, 5);

            std::istringstream stream(value);
            glm::vec4 data;
            stream >> data.r;
            stream >> data.g;
            stream >> data.b;
            stream >> data.a;

            auto it = parse_info->param_mapping.find(key);
            if (it == parse_info->param_mapping.end())
                parse_info->param_mapping.emplace(std::make_pair(key, parse_info_s::param_def({ data, Priority })));
            else if (Priority > it->second.priority)
            {
                parse_info->param_mapping.erase(it);
                parse_info->param_mapping.emplace(std::make_pair(key, parse_info_s::param_def({ data, Priority })));
            }

            /*
            size_t num = std::stoi(key) - 1;
            if (num < params.size() &&
                    (Priority > m_param_priority[num]))
            {
                std::istringstream stream(value);

                stream >> params[num].r;
                stream >> params[num].g;
                stream >> params[num].b;
                stream >> params[num].a;

                m_param_priority[num] = Priority;
            }*/
        }
        else if (key == "shader:" &&
                (!shader || Priority > m_shader_priority))
        {
            try
            {
                shader = GfxRenderer.Fetch_Shader(value);
                m_shader_priority = Priority;
            }
            catch (gl::shader_exception const &e)
            {
                log_error("invalid shader: " + std::string(e.what()));
            }
        }
        else if (key == "opacity:" &&
                Priority > m_opacity_priority)
        {
            opacity = std::stoi(value); //m7t: handle exception
            m_opacity_priority = Priority;
        }
        else if (key == "selfillum:" &&
                Priority > m_selfillum_priority)
        {
            selfillum = std::stoi(value); //m7t: handle exception
            m_selfillum_priority = Priority;
        }
        else if( value == "{" ) {
            // unrecognized or ignored token, but comes with attribute block and potential further nesting
            // go through it and discard the content
            while( true == deserialize_mapping( Input, -1, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
    }
    else {
        // discard mode; ignores all retrieved tokens
        if( value == "{" ) {
            // ignored tokens can come with their own blocks, ignore these recursively
            // go through it and discard the content
            while( true == deserialize_mapping( Input, -1, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
    }

    return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized
}

float opengl_material::get_or_guess_opacity()
{
    if (!std::isnan(opacity))
        return opacity;

    if (textures[0] != null_handle)
    {
        opengl_texture &tex = GfxRenderer.Texture(textures[0]);
        if (tex.has_alpha)
            return 0.0f;
        else
            return 0.5f;
    }

    return 0.0f;
}

// create material object from data stored in specified file.
// NOTE: the deferred load parameter is passed to textures defined by material, the material itself is always loaded immediately
material_handle
material_manager::create( std::string const &Filename, bool const Loadnow ) {

    auto filename { Filename };

    if( filename.find( '|' ) != std::string::npos )
        filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury

    erase_extension( filename );

    if( filename[ 0 ] == '/' ) {
        // filename can potentially begin with a slash, and we don't need it
        filename.erase( 0, 1 );
    }

    // try to locate requested material in the databank
    auto const databanklookup { find_in_databank( filename ) };
    if( databanklookup != null_handle ) {
        return databanklookup;
    }
    // if this fails, try to look for it on disk
    opengl_material material;
    auto const disklookup { find_on_disk( filename ) };
    if( false == disklookup.first.empty() ) {
        cParser materialparser( disklookup.first + disklookup.second, cParser::buffer_FILE );
        if( false == material.deserialize( materialparser, Loadnow ) ) {
            // deserialization failed but the .mat file does exist, so we give up at this point
            return null_handle;
        }
        material.name = disklookup.first;
    }
    else {
        // if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case
        material.textures[0] = GfxRenderer.Fetch_Texture( Filename, Loadnow );
        if( material.textures[0] == null_handle ) {
            // if there's also no texture, give up
            return null_handle;
        }

        // material would attach default shader anyway, but it would spit to error log
        material.shader = GfxRenderer.Fetch_Shader("default_1");

        // use texture path and name to tell the newly created materials apart
        filename = GfxRenderer.Texture( material.textures[0] ).name;
        erase_extension( filename );
        material.name = filename;
    }

    material.finalize(Loadnow);

    material_handle handle = m_materials.size();
    m_materialmappings.emplace(material.name, handle);
    m_materials.emplace_back( std::move(material) );
    return handle;
};

// checks whether specified material is in the material bank. returns handle to the material, or a null handle
material_handle
material_manager::find_in_databank( std::string const &Materialname ) const {

    std::vector<std::string> const filenames {
        Global.asCurrentTexturePath + Materialname,
        Materialname,
        szTexturePath + Materialname };

    for( auto const &filename : filenames ) {
        auto const lookup { m_materialmappings.find( filename ) };
        if( lookup != m_materialmappings.end() ) {
            return lookup->second;
        }
    }
    // all lookups failed
    return null_handle;
}

// checks whether specified file exists.
// NOTE: technically could be static, but we might want to switch from global texture path to instance-specific at some point
std::pair<std::string, std::string>
material_manager::find_on_disk( std::string const &Materialname ) const {

    return (
        FileExists(
            { Global.asCurrentTexturePath + Materialname, Materialname, szTexturePath + Materialname },
            { ".mat" } ) );
}

//---------------------------------------------------------------------------
<commit_msg>log missing material texture errors<commit_after>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"

#include "material.h"
#include "renderer.h"
#include "Globals.h"
#include "utilities.h"
#include "Logs.h"

opengl_material::opengl_material()
{
    for (size_t i = 0; i < params.size(); i++)
        params[i] = glm::vec4(std::numeric_limits<float>::quiet_NaN());
}

bool
opengl_material::deserialize( cParser &Input, bool const Loadnow ) {
    parse_info = std::make_unique<parse_info_s>();

    bool result { false };
    while( true == deserialize_mapping( Input, 0, Loadnow ) ) {
        result = true; // once would suffice but, eh
    }

    return result;
}

void opengl_material::log_error(const std::string &str)
{
    ErrorLog("bad material: " + name + ": " + str, logtype::material);
}

void opengl_material::finalize(bool Loadnow)
{
    if (parse_info)
    {
        for (auto it : parse_info->tex_mapping)
        {
            std::string key = it.first;
            std::string value = it.second.name;

            if (key.size() > 0 && key[0] != '_')
            {
                size_t num = std::stoi(key) - 1;
                if (num < gl::MAX_TEXTURES)
                    textures[num] = GfxRenderer.Fetch_Texture(value, Loadnow);
                else
                    log_error("invalid texture binding: " + std::to_string(num));
            }
            else if (key.size() > 2)
            {
                key.erase(0, 1);
                key.pop_back();
                if (shader && shader->texture_conf.find(key) != shader->texture_conf.end())
                    textures[shader->texture_conf[key].id] = GfxRenderer.Fetch_Texture(value, Loadnow);
                else
                    log_error("unknown texture binding: " + key);
            }
            else
                log_error("unrecognized texture binding: " + key);
        }

        if (!shader)
        {
            if (textures[0] == null_handle)
            {
                log_error("shader not specified, assuming \"default_0\"");
                shader = GfxRenderer.Fetch_Shader("default_0");
            }
            else if (textures[1] == null_handle)
            {
                log_error("shader not specified, assuming \"default_1\"");
                shader = GfxRenderer.Fetch_Shader("default_1");
            }
            else if (textures[2] == null_handle)
            {
                log_error("shader not specified, assuming \"default_2\"");
                shader = GfxRenderer.Fetch_Shader("default_2");
            }
        }

        for (auto it : parse_info->param_mapping)
        {
            std::string key = it.first;
            glm::vec4 value = it.second.data;

            if (key.size() > 1 && key[0] != '_')
            {
                size_t num = std::stoi(key) - 1;
                if (num < gl::MAX_PARAMS)
                    params[num] = value;
                else
                    log_error("invalid param binding: " + std::to_string(num));
            }
            else if (key.size() > 2)
            {
                key.erase(0, 1);
                key.pop_back();
                if (shader->param_conf.find(key) != shader->param_conf.end())
                {
                    gl::shader::param_entry entry = shader->param_conf[key];
                    for (size_t i = 0; i < entry.size; i++)
                        params[entry.location][entry.offset + i] = value[i];
                }
                else
                    log_error("unknown param binding: " + key);
            }
            else
                log_error("unrecognized param binding: " + key);
        }

        parse_info.reset();
    }

    for (auto it : shader->param_conf)
    {
        gl::shader::param_entry entry = it.second;
        if (std::isnan(params[entry.location][entry.offset]))
        {
            float value = std::numeric_limits<float>::quiet_NaN();
            if (entry.defaultparam == gl::shader::defaultparam_e::one)
                value = 1.0f;
            else if (entry.defaultparam == gl::shader::defaultparam_e::zero)
                value = 0.0f;
            else if (entry.defaultparam == gl::shader::defaultparam_e::required)
                log_error("unspecified required param: " + it.first);
            else if (entry.defaultparam != gl::shader::defaultparam_e::nan)
            {
                params_state.push_back(entry);
                continue;
            }

            for (size_t i = 0; i < entry.size; i++)
                params[entry.location][entry.offset + i] = value;
        }
    }

    for (auto it : shader->texture_conf)
    {
        gl::shader::texture_entry &entry = it.second;
        texture_handle handle = textures[entry.id];
        if (handle)
            GfxRenderer.Texture(handle).set_components_hint((GLint)entry.components);
        else
            log_error("missing texture: " + it.first);
    }
}

// imports member data pair from the config file
bool
opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool const Loadnow ) {
    // token can be a key or block end
    std::string key = Input.getToken<std::string>( true, "\n\r\t  ,;[]" );

    if( ( true == key.empty() ) || ( key == "}" ) ) { return false; }

    auto value { Input.getToken<std::string>( true, "\n\r\t ,;" ) };

    if( Priority != -1 ) {
        // regular attribute processing mode
        if( key == Global.Weather ) {
            // weather textures override generic (pri 0) and seasonal (pri 1) textures
            // seasonal weather textures (pri 1+2=3) override generic weather (pri 2) textures
            while( true == deserialize_mapping( Input, Priority + 2, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
        else if( key == Global.Season ) {
            // seasonal textures override generic textures
            while( true == deserialize_mapping( Input, 1, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
        else if (key.compare(0, 7, "texture") == 0) {
            key.erase(0, 7);

            std::replace(value.begin(), value.end(), '\\', '/');
            auto it = parse_info->tex_mapping.find(key);
            if (it == parse_info->tex_mapping.end())
                parse_info->tex_mapping.emplace(std::make_pair(key, parse_info_s::tex_def({ value, Priority })));
            else if (Priority > it->second.priority)
            {
                parse_info->tex_mapping.erase(it);
                parse_info->tex_mapping.emplace(std::make_pair(key, parse_info_s::tex_def({ value, Priority })));
            }
/*
            size_t num = std::stoi(key) - 1;
            if (num < textures.size() &&
                    (textures[num] == null_handle || Priority > m_texture_priority[num]))
            {
				std::replace(value.begin(), value.end(), '\\', '/');
                textures[num] = GfxRenderer.Fetch_Texture( value, Loadnow );
                m_texture_priority[num] = Priority;
            }
            */
        }
        else if (key.compare(0, 5, "param") == 0) {
            key.erase(0, 5);

            std::istringstream stream(value);
            glm::vec4 data;
            stream >> data.r;
            stream >> data.g;
            stream >> data.b;
            stream >> data.a;

            auto it = parse_info->param_mapping.find(key);
            if (it == parse_info->param_mapping.end())
                parse_info->param_mapping.emplace(std::make_pair(key, parse_info_s::param_def({ data, Priority })));
            else if (Priority > it->second.priority)
            {
                parse_info->param_mapping.erase(it);
                parse_info->param_mapping.emplace(std::make_pair(key, parse_info_s::param_def({ data, Priority })));
            }

            /*
            size_t num = std::stoi(key) - 1;
            if (num < params.size() &&
                    (Priority > m_param_priority[num]))
            {
                std::istringstream stream(value);

                stream >> params[num].r;
                stream >> params[num].g;
                stream >> params[num].b;
                stream >> params[num].a;

                m_param_priority[num] = Priority;
            }*/
        }
        else if (key == "shader:" &&
                (!shader || Priority > m_shader_priority))
        {
            try
            {
                shader = GfxRenderer.Fetch_Shader(value);
                m_shader_priority = Priority;
            }
            catch (gl::shader_exception const &e)
            {
                log_error("invalid shader: " + std::string(e.what()));
            }
        }
        else if (key == "opacity:" &&
                Priority > m_opacity_priority)
        {
            opacity = std::stoi(value); //m7t: handle exception
            m_opacity_priority = Priority;
        }
        else if (key == "selfillum:" &&
                Priority > m_selfillum_priority)
        {
            selfillum = std::stoi(value); //m7t: handle exception
            m_selfillum_priority = Priority;
        }
        else if( value == "{" ) {
            // unrecognized or ignored token, but comes with attribute block and potential further nesting
            // go through it and discard the content
            while( true == deserialize_mapping( Input, -1, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
    }
    else {
        // discard mode; ignores all retrieved tokens
        if( value == "{" ) {
            // ignored tokens can come with their own blocks, ignore these recursively
            // go through it and discard the content
            while( true == deserialize_mapping( Input, -1, Loadnow ) ) {
                ; // all work is done in the header
            }
        }
    }

    return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized
}

float opengl_material::get_or_guess_opacity()
{
    if (!std::isnan(opacity))
        return opacity;

    if (textures[0] != null_handle)
    {
        opengl_texture &tex = GfxRenderer.Texture(textures[0]);
        if (tex.has_alpha)
            return 0.0f;
        else
            return 0.5f;
    }

    return 0.0f;
}

// create material object from data stored in specified file.
// NOTE: the deferred load parameter is passed to textures defined by material, the material itself is always loaded immediately
material_handle
material_manager::create( std::string const &Filename, bool const Loadnow ) {

    auto filename { Filename };

    if( filename.find( '|' ) != std::string::npos )
        filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury

    erase_extension( filename );

    if( filename[ 0 ] == '/' ) {
        // filename can potentially begin with a slash, and we don't need it
        filename.erase( 0, 1 );
    }

    // try to locate requested material in the databank
    auto const databanklookup { find_in_databank( filename ) };
    if( databanklookup != null_handle ) {
        return databanklookup;
    }
    // if this fails, try to look for it on disk
    opengl_material material;
    auto const disklookup { find_on_disk( filename ) };
    if( false == disklookup.first.empty() ) {
        cParser materialparser( disklookup.first + disklookup.second, cParser::buffer_FILE );
        if( false == material.deserialize( materialparser, Loadnow ) ) {
            // deserialization failed but the .mat file does exist, so we give up at this point
            return null_handle;
        }
        material.name = disklookup.first;
    }
    else {
        // if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case
        material.textures[0] = GfxRenderer.Fetch_Texture( Filename, Loadnow );
        if( material.textures[0] == null_handle ) {
            // if there's also no texture, give up
            return null_handle;
        }

        // material would attach default shader anyway, but it would spit to error log
        material.shader = GfxRenderer.Fetch_Shader("default_1");

        // use texture path and name to tell the newly created materials apart
        filename = GfxRenderer.Texture( material.textures[0] ).name;
        erase_extension( filename );
        material.name = filename;
    }

    material.finalize(Loadnow);

    material_handle handle = m_materials.size();
    m_materialmappings.emplace(material.name, handle);
    m_materials.emplace_back( std::move(material) );
    return handle;
};

// checks whether specified material is in the material bank. returns handle to the material, or a null handle
material_handle
material_manager::find_in_databank( std::string const &Materialname ) const {

    std::vector<std::string> const filenames {
        Global.asCurrentTexturePath + Materialname,
        Materialname,
        szTexturePath + Materialname };

    for( auto const &filename : filenames ) {
        auto const lookup { m_materialmappings.find( filename ) };
        if( lookup != m_materialmappings.end() ) {
            return lookup->second;
        }
    }
    // all lookups failed
    return null_handle;
}

// checks whether specified file exists.
// NOTE: technically could be static, but we might want to switch from global texture path to instance-specific at some point
std::pair<std::string, std::string>
material_manager::find_on_disk( std::string const &Materialname ) const {

    return (
        FileExists(
            { Global.asCurrentTexturePath + Materialname, Materialname, szTexturePath + Materialname },
            { ".mat" } ) );
}

//---------------------------------------------------------------------------
<|endoftext|>


<commit_before>#include "Genes/Pawn_Advancement_Gene.h"

#include <string>

#include "Game/Board.h"
#include "Pieces/Piece.h"
#include "Genes/Piece_Strength_Gene.h"
#include "Utility.h"

Pawn_Advancement_Gene::Pawn_Advancement_Gene(const std::shared_ptr<const Piece_Strength_Gene>& piece_strength_source_in) :
    Gene(0.0),
    piece_strength_source(piece_strength_source_in),
    promoted_pawn_bonus(0.0)
{
}

void Pawn_Advancement_Gene::reset_properties() const
{
    reset_base_properties();
    properties["Promoted Pawn Bonus"] = promoted_pawn_bonus;
}

void Pawn_Advancement_Gene::load_properties()
{
    load_base_properties();
    promoted_pawn_bonus = properties["Promoted Pawn Bonus"];
}

double Pawn_Advancement_Gene::score_board(const Board& board, Color color) const
{
    double score = 0.0;

    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = 1; rank <= 8; ++rank)
        {
            auto piece = board.piece_on_square(file, rank);
            if(piece && piece->color() == color && toupper(piece->fen_symbol()) == 'P')
            {
                // 1 point per pawn + 1 point per move towards promotion
                score += std::abs((color == WHITE ? 1 : 8) - rank);
            }
        }
    }

    for(size_t i = (color == WHITE ? 0 : 1); i < board.get_game_record().size(); i += 2)
    {
        if(board.get_game_record()[i].find('=') != std::string::npos)
        {
            auto piece_symbol = toupper(String::split(board.get_game_record()[i], "=")[1][0]); // char
            score += promoted_pawn_bonus*piece_strength_source->piece_value(piece_symbol);
        }
    }

    return score;
}

Pawn_Advancement_Gene* Pawn_Advancement_Gene::duplicate() const
{
    return new Pawn_Advancement_Gene(*this);
}

std::string Pawn_Advancement_Gene::name() const
{
    return "Pawn Advancement Gene";
}

void Pawn_Advancement_Gene::mutate()
{
    Gene::mutate();
    promoted_pawn_bonus += Random::random_normal(1.0);
    promoted_pawn_bonus = std::max(promoted_pawn_bonus, 0.0);
}

void Pawn_Advancement_Gene::reset_piece_strength_gene(const std::shared_ptr<const Piece_Strength_Gene>& psg)
{
    piece_strength_source = psg;
}
<commit_msg>Pawn_Advancement_Gene board_score() change<commit_after>#include "Genes/Pawn_Advancement_Gene.h"

#include <string>

#include "Game/Board.h"
#include "Pieces/Piece.h"
#include "Genes/Piece_Strength_Gene.h"
#include "Utility.h"

Pawn_Advancement_Gene::Pawn_Advancement_Gene(const std::shared_ptr<const Piece_Strength_Gene>& piece_strength_source_in) :
    Gene(0.0),
    piece_strength_source(piece_strength_source_in),
    promoted_pawn_bonus(0.0)
{
}

void Pawn_Advancement_Gene::reset_properties() const
{
    reset_base_properties();
    properties["Promoted Pawn Bonus"] = promoted_pawn_bonus;
}

void Pawn_Advancement_Gene::load_properties()
{
    load_base_properties();
    promoted_pawn_bonus = properties["Promoted Pawn Bonus"];
}

double Pawn_Advancement_Gene::score_board(const Board& board, Color color) const
{
    double score = 0.0;

    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = 1; rank <= 8; ++rank)
        {
            auto piece = board.piece_on_square(file, rank);
            if(piece && piece->color() == color && toupper(piece->fen_symbol()) == 'P')
            {
                // 1 point per pawn + 1 point per move towards promotion
                score += std::abs((color == WHITE ? 1 : 8) - rank);
            }
        }
    }

    if(board.get_game_record().size() < 2)
    {
        return score;
    }

    auto last_move_index = board.get_game_record().size() - (color == board.whose_turn() ? 2 : 1);
    auto last_move = board.get_game_record()[last_move_index];
    if(last_move.find('=') != std::string::npos)
    {
        auto piece_symbol = String::split(last_move, "=")[1][0]; // char
        score += promoted_pawn_bonus*piece_strength_source->piece_value(piece_symbol);
    }

    return score;
}

Pawn_Advancement_Gene* Pawn_Advancement_Gene::duplicate() const
{
    return new Pawn_Advancement_Gene(*this);
}

std::string Pawn_Advancement_Gene::name() const
{
    return "Pawn Advancement Gene";
}

void Pawn_Advancement_Gene::mutate()
{
    Gene::mutate();
    promoted_pawn_bonus += Random::random_normal(1.0);
    promoted_pawn_bonus = std::max(promoted_pawn_bonus, 0.0);
}

void Pawn_Advancement_Gene::reset_piece_strength_gene(const std::shared_ptr<const Piece_Strength_Gene>& psg)
{
    piece_strength_source = psg;
}
<|endoftext|>


<commit_before>//===-- GRConstants.cpp - Simple, Path-Sens. Constant Prop. ------*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//               Constant Propagation via Graph Reachability
//
//  This files defines a simple analysis that performs path-sensitive
//  constant propagation within a function.  An example use of this analysis
//  is to perform simple checks for NULL dereferences.
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/PathSensitive/GREngine.h"
#include "clang/AST/Expr.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/Visitors/CFGStmtVisitor.h"

#include "llvm/Support/Casting.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"

#ifndef NDEBUG
#include "llvm/Support/GraphWriter.h"
#include <sstream>
#endif

using namespace clang;
using llvm::APInt;
using llvm::APFloat;
using llvm::dyn_cast;
using llvm::cast;

//===----------------------------------------------------------------------===//
/// DSPtr - A variant smart pointer that wraps either a ValueDecl* or a
///  Stmt*.  Use cast<> or dyn_cast<> to get actual pointer type
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN DSPtr {
  uintptr_t Raw;
public:
  enum  VariantKind { IsValueDecl=0x1, IsBlkLvl=0x2, IsSubExp=0x3, Flags=0x3 };
  inline void* getPtr() const { return reinterpret_cast<void*>(Raw & ~Flags); }
  inline VariantKind getKind() const { return (VariantKind) (Raw & Flags); }

  DSPtr(ValueDecl* D) : Raw(reinterpret_cast<uintptr_t>(D) | IsValueDecl) {}
  DSPtr(Stmt* S, bool isBlkLvl)
    : Raw(reinterpret_cast<uintptr_t>(S) | (isBlkLvl ? IsBlkLvl : IsSubExp)) {}

  bool isSubExpr() const { return getKind() == IsSubExp; }

  inline void Profile(llvm::FoldingSetNodeID& ID) const {
    ID.AddPointer(getPtr());
    ID.AddInteger((unsigned) getKind());
  }
  inline bool operator==(const DSPtr& X) const { return Raw == X.Raw; }
  inline bool operator!=(const DSPtr& X) const { return Raw != X.Raw; }
  inline bool operator<(const DSPtr& X) const { return Raw < X.Raw; }
};
} // end anonymous namespace

// Machinery to get cast<> and dyn_cast<> working with DSPtr.
namespace llvm {
  template<> inline bool isa<ValueDecl,DSPtr>(const DSPtr& V) {
    return V.getKind() == DSPtr::IsValueDecl;
  }
  template<> inline bool isa<Stmt,DSPtr>(const DSPtr& V) {
    return ((unsigned) V.getKind()) > DSPtr::IsValueDecl;
  }
  template<> struct VISIBILITY_HIDDEN cast_retty_impl<ValueDecl,DSPtr> {
    typedef const ValueDecl* ret_type;
  };
  template<> struct VISIBILITY_HIDDEN cast_retty_impl<Stmt,DSPtr> {
    typedef const Stmt* ret_type;
  };
  template<> struct VISIBILITY_HIDDEN simplify_type<DSPtr> {
    typedef void* SimpleType;
    static inline SimpleType getSimplifiedValue(const DSPtr &V) {
      return V.getPtr();
    }
  };
} // end llvm namespace

//===----------------------------------------------------------------------===//
// DeclStmtMapTy - A ImmutableMap type from Decl*/Stmt* to integers.
//
//  FIXME: We may eventually use APSInt, or a mixture of APSInt and
//         integer primitives to do this right; this will handle both
//         different bit-widths and allow us to detect integer overflows, etc.
//
//===----------------------------------------------------------------------===//

typedef llvm::ImmutableMap<DSPtr,uint64_t> DeclStmtMapTy;

namespace clang {
template<>
struct VISIBILITY_HIDDEN GRTrait<DeclStmtMapTy> {
  static inline void* toPtr(DeclStmtMapTy M) {
    return reinterpret_cast<void*>(M.getRoot());
  }
  static inline DeclStmtMapTy toState(void* P) {
    return DeclStmtMapTy(static_cast<DeclStmtMapTy::TreeTy*>(P));
  }
};
}

//===----------------------------------------------------------------------===//
// The Checker!
//===----------------------------------------------------------------------===//

namespace {
class VISIBILITY_HIDDEN ExprVariantTy {
  const uint64_t val;
  const bool isConstant;
public:
  ExprVariantTy() : val(0), isConstant(false) {}
  ExprVariantTy(uint64_t v) : val(v), isConstant(true) {}

  operator bool() const { return isConstant; }
  uint64_t getVal() const { assert (isConstant); return val; }

  ExprVariantTy operator+(const ExprVariantTy& X) const {
    if (!isConstant || !X.isConstant) return ExprVariantTy();
    else return ExprVariantTy(val+X.val);
  }

  ExprVariantTy operator-(const ExprVariantTy& X) const {
    if (!isConstant || !X.isConstant) return ExprVariantTy();
    else return ExprVariantTy(val-X.val);
  }
};
} // end anonymous namespace

//===----------------------------------------------------------------------===//
// The Checker!
//===----------------------------------------------------------------------===//

namespace {
class VISIBILITY_HIDDEN GRConstants : public CFGStmtVisitor<GRConstants> {

public:
  typedef DeclStmtMapTy StateTy;
  typedef GRNodeBuilder<GRConstants> NodeBuilder;
  typedef ExplodedNode<StateTy> NodeTy;

protected:
  // Liveness - live-variables information the ValueDecl* and Expr* (block-level)
  //  in the CFG.  Used to prune out dead state.
  LiveVariables* Liveness;

  // Builder - The current GRNodeBuilder which is used when building the nodes
  //  for a given statement.
  NodeBuilder* Builder;

  DeclStmtMapTy::Factory StateMgr;

  // cfg - the current CFG.
  CFG* cfg;

  typedef llvm::SmallVector<NodeTy*,8> NodeSetTy;
  NodeSetTy NodeSetA;
  NodeSetTy NodeSetB;
  NodeSetTy* Nodes;
  NodeSetTy* OldNodes;
  StateTy CurrentState;

public:
  GRConstants() : Liveness(NULL), Builder(NULL), cfg(NULL),
    Nodes(&NodeSetA), OldNodes(&NodeSetB),
    CurrentState(StateMgr.GetEmptyMap()) {}

  ~GRConstants() { delete Liveness; }

  CFG& getCFG() { assert (cfg); return *cfg; }

  void Initialize(CFG& c) {
    cfg = &c;
    Liveness = new LiveVariables(c);
    Liveness->runOnCFG(c);
  }

  StateTy getInitialState() {
    return StateMgr.GetEmptyMap();
  }

  void ProcessStmt(Stmt* S, NodeBuilder& builder);
  void SwitchNodeSets();
  void DoStmt(Stmt* S);
  StateTy RemoveGrandchildrenMappings(Stmt* S, StateTy M);

  void AddBinding(Expr* E, ExprVariantTy V, bool isBlkLvl = false);
  void AddBinding(ValueDecl* D, ExprVariantTy V);

  ExprVariantTy GetBinding(Expr* E);

  void BlockStmt_VisitStmt(Stmt* S) { DoStmt(S); }

  void VisitAssign(BinaryOperator* O);
  void VisitIntegerLiteral(IntegerLiteral* L);
  void VisitBinAdd(BinaryOperator* O);
  void VisitBinSub(BinaryOperator* O);
  void VisitBinAssign(BinaryOperator* D);
};
} // end anonymous namespace

void GRConstants::ProcessStmt(Stmt* S, NodeBuilder& builder) {
  Builder = &builder;
  Nodes->clear();
  OldNodes->clear();
  NodeTy* N = Builder->getLastNode();
  assert (N);
  OldNodes->push_back(N);
  BlockStmt_Visit(S);
  Builder = NULL;
}

ExprVariantTy GRConstants::GetBinding(Expr* E) {
  DSPtr P(NULL);

  if (DeclRefExpr* D = dyn_cast<DeclRefExpr>(E)) P = DSPtr(D->getDecl());
  else P = DSPtr(E, getCFG().isBlkExpr(E));

  StateTy::iterator I = CurrentState.find(P);

  if (I == CurrentState.end())
    return ExprVariantTy();

  return (*I).second;
}

void GRConstants::AddBinding(Expr* E, ExprVariantTy V, bool isBlkLvl) {
  if (V)
    CurrentState = StateMgr.Add(CurrentState, DSPtr(E,isBlkLvl), V.getVal());
}

void GRConstants::AddBinding(ValueDecl* D, ExprVariantTy V) {
  if (V)
    CurrentState = StateMgr.Add(CurrentState, DSPtr(D), V.getVal());
  else
    CurrentState = StateMgr.Remove(CurrentState, DSPtr(D));
}

void GRConstants::SwitchNodeSets() {
  NodeSetTy* Tmp = OldNodes;
  OldNodes = Nodes;
  Nodes = Tmp;
  Nodes->clear();
}

GRConstants::StateTy
GRConstants::RemoveGrandchildrenMappings(Stmt* S, GRConstants::StateTy State) {

  typedef Stmt::child_iterator iterator;

  for (iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I)
    if (Stmt* C = *I)
      for (iterator CI=C->child_begin(), CE=C->child_end(); CI!=CE; ++CI) {
        // Observe that this will only remove mappings to non-block level
        // expressions.  This is valid even if *CI is a block-level expression,
        // since it simply won't be in the map in the first place.
        // Note: This should also work if 'C' is a block-level expression,
        // although ideally we would want to skip processing C's children.
        State = StateMgr.Remove(State, DSPtr(*CI,false));
      }

  return State;
}

void GRConstants::DoStmt(Stmt* S) {
  for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I)
    if (*I) DoStmt(*I);

  for (NodeSetTy::iterator I=OldNodes->begin(), E=OldNodes->end(); I!=E; ++I) {
    NodeTy* Pred = *I;
    CurrentState = Pred->getState();

    StateTy OldState = CurrentState;
    CurrentState = RemoveGrandchildrenMappings(S, CurrentState);

    Visit(S);

    if (CurrentState != OldState) {
      NodeTy* N = Builder->generateNode(S, CurrentState, Pred);
      if (N) Nodes->push_back(N);
    }
    else Nodes->push_back(Pred);
  }

  SwitchNodeSets();
}

void GRConstants::VisitIntegerLiteral(IntegerLiteral* L) {
  AddBinding(L, L->getValue().getZExtValue());
}

void GRConstants::VisitBinAdd(BinaryOperator* B) {
  AddBinding(B, GetBinding(B->getLHS()) + GetBinding(B->getRHS()));
}

void GRConstants::VisitBinSub(BinaryOperator* B) {
  AddBinding(B, GetBinding(B->getLHS()) - GetBinding(B->getRHS()));
}

static inline Expr* IgnoreParen(Expr* E) {
  while (ParenExpr* P = dyn_cast<ParenExpr>(E))
    E = P->getSubExpr();

  return E;
}

void GRConstants::VisitBinAssign(BinaryOperator* B) {
  if (DeclRefExpr* D = dyn_cast<DeclRefExpr>(IgnoreParen(B->getLHS())))
    AddBinding(D->getDecl(), GetBinding(B->getRHS()));
}

//===----------------------------------------------------------------------===//
// Driver.
//===----------------------------------------------------------------------===//

#ifndef NDEBUG
namespace llvm {
template<>
struct VISIBILITY_HIDDEN DOTGraphTraits<GRConstants::NodeTy*> :
  public DefaultDOTGraphTraits {

  static std::string getNodeLabel(const GRConstants::NodeTy* N, void*) {
    std::ostringstream Out;

    Out << "Vertex: " << (void*) N << '\n';
    ProgramPoint Loc = N->getLocation();

    switch (Loc.getKind()) {
      case ProgramPoint::BlockEntranceKind:
        Out << "Block Entrance: B"
            << cast<BlockEntrance>(Loc).getBlock()->getBlockID();
        break;

      case ProgramPoint::BlockExitKind:
        assert (false);
        break;

      case ProgramPoint::PostStmtKind: {
        const PostStmt& L = cast<PostStmt>(Loc);
        Out << "Stmt: " << (void*) L.getStmt() << '\n';
        L.getStmt()->printPretty(Out);
        break;
      }

      default: {
        const BlockEdge& E = cast<BlockEdge>(Loc);
        Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B"
            << E.getDst()->getBlockID()  << ')';
      }
    }

    Out << "\n{";

    GRConstants::StateTy M = N->getState();
    bool isFirst = true;

    for (GRConstants::StateTy::iterator I=M.begin(), E=M.end(); I!=E; ++I) {
      if (!isFirst)
        Out << '\n';
      else
        isFirst = false;

      if (ValueDecl* V = dyn_cast<ValueDecl>(I.getKey())) {
        Out << "Decl: " << (void*) V << ", " << V->getName();
      }
      else {
        Stmt* E = cast<Stmt>(I.getKey());
        Out << "Stmt: " << (void*) E;
      }

      Out << " => " << I.getData();
    }

    Out << " }";

    return Out.str();
  }
};
} // end llvm namespace
#endif

namespace clang {
void RunGRConstants(CFG& cfg) {
  GREngine<GRConstants> Engine(cfg);
  Engine.ExecuteWorkList();
#ifndef NDEBUG
  llvm::ViewGraph(*Engine.getGraph().roots_begin(),"GRConstants");
#endif
}
}
<commit_msg>IntegerLiterals are no longer evaluated to create separate nodes; their values are determined when evaluating the parent expression.<commit_after>//===-- GRConstants.cpp - Simple, Path-Sens. Constant Prop. ------*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//               Constant Propagation via Graph Reachability
//
//  This files defines a simple analysis that performs path-sensitive
//  constant propagation within a function.  An example use of this analysis
//  is to perform simple checks for NULL dereferences.
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/PathSensitive/GREngine.h"
#include "clang/AST/Expr.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/Visitors/CFGStmtVisitor.h"

#include "llvm/Support/Casting.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"

#ifndef NDEBUG
#include "llvm/Support/GraphWriter.h"
#include <sstream>
#endif

using namespace clang;
using llvm::APInt;
using llvm::APFloat;
using llvm::dyn_cast;
using llvm::cast;

//===----------------------------------------------------------------------===//
/// DSPtr - A variant smart pointer that wraps either a ValueDecl* or a
///  Stmt*.  Use cast<> or dyn_cast<> to get actual pointer type
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN DSPtr {
  uintptr_t Raw;
public:
  enum  VariantKind { IsValueDecl=0x1, IsBlkLvl=0x2, IsSubExp=0x3, Flags=0x3 };
  inline void* getPtr() const { return reinterpret_cast<void*>(Raw & ~Flags); }
  inline VariantKind getKind() const { return (VariantKind) (Raw & Flags); }

  DSPtr(ValueDecl* D) : Raw(reinterpret_cast<uintptr_t>(D) | IsValueDecl) {}
  DSPtr(Stmt* S, bool isBlkLvl)
    : Raw(reinterpret_cast<uintptr_t>(S) | (isBlkLvl ? IsBlkLvl : IsSubExp)) {}

  bool isSubExpr() const { return getKind() == IsSubExp; }

  inline void Profile(llvm::FoldingSetNodeID& ID) const {
    ID.AddPointer(getPtr());
    ID.AddInteger((unsigned) getKind());
  }
  inline bool operator==(const DSPtr& X) const { return Raw == X.Raw; }
  inline bool operator!=(const DSPtr& X) const { return Raw != X.Raw; }
  inline bool operator<(const DSPtr& X) const { return Raw < X.Raw; }
};
} // end anonymous namespace

// Machinery to get cast<> and dyn_cast<> working with DSPtr.
namespace llvm {
  template<> inline bool isa<ValueDecl,DSPtr>(const DSPtr& V) {
    return V.getKind() == DSPtr::IsValueDecl;
  }
  template<> inline bool isa<Stmt,DSPtr>(const DSPtr& V) {
    return ((unsigned) V.getKind()) > DSPtr::IsValueDecl;
  }
  template<> struct VISIBILITY_HIDDEN cast_retty_impl<ValueDecl,DSPtr> {
    typedef const ValueDecl* ret_type;
  };
  template<> struct VISIBILITY_HIDDEN cast_retty_impl<Stmt,DSPtr> {
    typedef const Stmt* ret_type;
  };
  template<> struct VISIBILITY_HIDDEN simplify_type<DSPtr> {
    typedef void* SimpleType;
    static inline SimpleType getSimplifiedValue(const DSPtr &V) {
      return V.getPtr();
    }
  };
} // end llvm namespace

//===----------------------------------------------------------------------===//
// DeclStmtMapTy - A ImmutableMap type from Decl*/Stmt* to integers.
//
//  FIXME: We may eventually use APSInt, or a mixture of APSInt and
//         integer primitives to do this right; this will handle both
//         different bit-widths and allow us to detect integer overflows, etc.
//
//===----------------------------------------------------------------------===//

typedef llvm::ImmutableMap<DSPtr,uint64_t> DeclStmtMapTy;

namespace clang {
template<>
struct VISIBILITY_HIDDEN GRTrait<DeclStmtMapTy> {
  static inline void* toPtr(DeclStmtMapTy M) {
    return reinterpret_cast<void*>(M.getRoot());
  }
  static inline DeclStmtMapTy toState(void* P) {
    return DeclStmtMapTy(static_cast<DeclStmtMapTy::TreeTy*>(P));
  }
};
}

//===----------------------------------------------------------------------===//
// The Checker!
//===----------------------------------------------------------------------===//

namespace {
class VISIBILITY_HIDDEN ExprVariantTy {
  const uint64_t val;
  const bool isConstant;
public:
  ExprVariantTy() : val(0), isConstant(false) {}
  ExprVariantTy(uint64_t v) : val(v), isConstant(true) {}

  operator bool() const { return isConstant; }
  uint64_t getVal() const { assert (isConstant); return val; }

  ExprVariantTy operator+(const ExprVariantTy& X) const {
    if (!isConstant || !X.isConstant) return ExprVariantTy();
    else return ExprVariantTy(val+X.val);
  }

  ExprVariantTy operator-(const ExprVariantTy& X) const {
    if (!isConstant || !X.isConstant) return ExprVariantTy();
    else return ExprVariantTy(val-X.val);
  }
};
} // end anonymous namespace

//===----------------------------------------------------------------------===//
// The Checker!
//===----------------------------------------------------------------------===//

namespace {
class VISIBILITY_HIDDEN GRConstants : public CFGStmtVisitor<GRConstants> {

public:
  typedef DeclStmtMapTy StateTy;
  typedef GRNodeBuilder<GRConstants> NodeBuilder;
  typedef ExplodedNode<StateTy> NodeTy;

protected:
  // Liveness - live-variables information the ValueDecl* and Expr* (block-level)
  //  in the CFG.  Used to prune out dead state.
  LiveVariables* Liveness;

  // Builder - The current GRNodeBuilder which is used when building the nodes
  //  for a given statement.
  NodeBuilder* Builder;

  DeclStmtMapTy::Factory StateMgr;

  // cfg - the current CFG.
  CFG* cfg;

  typedef llvm::SmallVector<NodeTy*,8> NodeSetTy;
  NodeSetTy NodeSetA;
  NodeSetTy NodeSetB;
  NodeSetTy* Nodes;
  NodeSetTy* OldNodes;
  StateTy CurrentState;

public:
  GRConstants() : Liveness(NULL), Builder(NULL), cfg(NULL),
    Nodes(&NodeSetA), OldNodes(&NodeSetB),
    CurrentState(StateMgr.GetEmptyMap()) {}

  ~GRConstants() { delete Liveness; }

  CFG& getCFG() { assert (cfg); return *cfg; }

  void Initialize(CFG& c) {
    cfg = &c;
    Liveness = new LiveVariables(c);
    Liveness->runOnCFG(c);
  }

  StateTy getInitialState() {
    return StateMgr.GetEmptyMap();
  }

  void ProcessStmt(Stmt* S, NodeBuilder& builder);
  void SwitchNodeSets();
  void DoStmt(Stmt* S);
  StateTy RemoveGrandchildrenMappings(Stmt* S, StateTy M);

  void AddBinding(Expr* E, ExprVariantTy V, bool isBlkLvl = false);
  void AddBinding(ValueDecl* D, ExprVariantTy V);

  ExprVariantTy GetBinding(Expr* E);

  void BlockStmt_VisitStmt(Stmt* S) { DoStmt(S); }

  void VisitAssign(BinaryOperator* O);
  void VisitBinAdd(BinaryOperator* O);
  void VisitBinSub(BinaryOperator* O);
  void VisitBinAssign(BinaryOperator* D);
};
} // end anonymous namespace

static inline Expr* IgnoreParen(Expr* E) {
  while (ParenExpr* P = dyn_cast<ParenExpr>(E))
    E = P->getSubExpr();

  return E;
}

void GRConstants::ProcessStmt(Stmt* S, NodeBuilder& builder) {
  Builder = &builder;
  Nodes->clear();
  OldNodes->clear();
  NodeTy* N = Builder->getLastNode();
  assert (N);
  OldNodes->push_back(N);
  BlockStmt_Visit(S);
  Builder = NULL;
}

ExprVariantTy GRConstants::GetBinding(Expr* E) {
  DSPtr P(NULL);
  E = IgnoreParen(E);

  switch (E->getStmtClass()) {
    case Stmt::DeclRefExprClass:
      P = DSPtr(cast<DeclRefExpr>(E)->getDecl());
      break;

    case Stmt::IntegerLiteralClass:
      return cast<IntegerLiteral>(E)->getValue().getZExtValue();

    default:
      P = DSPtr(E, getCFG().isBlkExpr(E));
      break;
  }

  StateTy::iterator I = CurrentState.find(P);

  if (I == CurrentState.end())
    return ExprVariantTy();

  return (*I).second;
}

void GRConstants::AddBinding(Expr* E, ExprVariantTy V, bool isBlkLvl) {
  if (V)
    CurrentState = StateMgr.Add(CurrentState, DSPtr(E,isBlkLvl), V.getVal());
}

void GRConstants::AddBinding(ValueDecl* D, ExprVariantTy V) {
  if (V)
    CurrentState = StateMgr.Add(CurrentState, DSPtr(D), V.getVal());
  else
    CurrentState = StateMgr.Remove(CurrentState, DSPtr(D));
}

void GRConstants::SwitchNodeSets() {
  NodeSetTy* Tmp = OldNodes;
  OldNodes = Nodes;
  Nodes = Tmp;
  Nodes->clear();
}

GRConstants::StateTy
GRConstants::RemoveGrandchildrenMappings(Stmt* S, GRConstants::StateTy State) {

  typedef Stmt::child_iterator iterator;

  for (iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I)
    if (Stmt* C = *I)
      for (iterator CI=C->child_begin(), CE=C->child_end(); CI!=CE; ++CI) {
        // Observe that this will only remove mappings to non-block level
        // expressions.  This is valid even if *CI is a block-level expression,
        // since it simply won't be in the map in the first place.
        // Note: This should also work if 'C' is a block-level expression,
        // although ideally we would want to skip processing C's children.
        State = StateMgr.Remove(State, DSPtr(*CI,false));
      }

  return State;
}

void GRConstants::DoStmt(Stmt* S) {
  for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I)
    if (*I) DoStmt(*I);

  for (NodeSetTy::iterator I=OldNodes->begin(), E=OldNodes->end(); I!=E; ++I) {
    NodeTy* Pred = *I;
    CurrentState = Pred->getState();

    StateTy OldState = CurrentState;
    CurrentState = RemoveGrandchildrenMappings(S, CurrentState);

    Visit(S);

    if (CurrentState != OldState) {
      NodeTy* N = Builder->generateNode(S, CurrentState, Pred);
      if (N) Nodes->push_back(N);
    }
    else Nodes->push_back(Pred);
  }

  SwitchNodeSets();
}

void GRConstants::VisitBinAdd(BinaryOperator* B) {
  AddBinding(B, GetBinding(B->getLHS()) + GetBinding(B->getRHS()));
}

void GRConstants::VisitBinSub(BinaryOperator* B) {
  AddBinding(B, GetBinding(B->getLHS()) - GetBinding(B->getRHS()));
}

void GRConstants::VisitBinAssign(BinaryOperator* B) {
  if (DeclRefExpr* D = dyn_cast<DeclRefExpr>(IgnoreParen(B->getLHS())))
    AddBinding(D->getDecl(), GetBinding(B->getRHS()));
}

//===----------------------------------------------------------------------===//
// Driver.
//===----------------------------------------------------------------------===//

#ifndef NDEBUG
namespace llvm {
template<>
struct VISIBILITY_HIDDEN DOTGraphTraits<GRConstants::NodeTy*> :
  public DefaultDOTGraphTraits {

  static std::string getNodeLabel(const GRConstants::NodeTy* N, void*) {
    std::ostringstream Out;

    Out << "Vertex: " << (void*) N << '\n';
    ProgramPoint Loc = N->getLocation();

    switch (Loc.getKind()) {
      case ProgramPoint::BlockEntranceKind:
        Out << "Block Entrance: B"
            << cast<BlockEntrance>(Loc).getBlock()->getBlockID();
        break;

      case ProgramPoint::BlockExitKind:
        assert (false);
        break;

      case ProgramPoint::PostStmtKind: {
        const PostStmt& L = cast<PostStmt>(Loc);
        Out << "Stmt: " << (void*) L.getStmt() << '\n';
        L.getStmt()->printPretty(Out);
        break;
      }

      default: {
        const BlockEdge& E = cast<BlockEdge>(Loc);
        Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B"
            << E.getDst()->getBlockID()  << ')';
      }
    }

    Out << "\n{";

    GRConstants::StateTy M = N->getState();
    bool isFirst = true;

    for (GRConstants::StateTy::iterator I=M.begin(), E=M.end(); I!=E; ++I) {
      if (!isFirst)
        Out << '\n';
      else
        isFirst = false;

      if (ValueDecl* V = dyn_cast<ValueDecl>(I.getKey())) {
        Out << "Decl: " << (void*) V << ", " << V->getName();
      }
      else {
        Stmt* E = cast<Stmt>(I.getKey());
        Out << "Stmt: " << (void*) E;
      }

      Out << " => " << I.getData();
    }

    Out << " }";

    return Out.str();
  }
};
} // end llvm namespace
#endif

namespace clang {
void RunGRConstants(CFG& cfg) {
  GREngine<GRConstants> Engine(cfg);
  Engine.ExecuteWorkList();
#ifndef NDEBUG
  llvm::ViewGraph(*Engine.getGraph().roots_begin(),"GRConstants");
#endif
}
}
<|endoftext|>