<commit_before>#include "SampleCode.h"
#include "SkCanvas.h"
#include "SkParsePath.h"
#include "SkPath.h"
#include "SkRandom.h"
#include "SkView.h"

#include "SkBlurMaskFilter.h"
#include "SkBlurMask.h"

static void test_blur() {
    uint8_t cell[9];
    memset(cell, 0xFF, sizeof(cell));
    SkMask src;
    src.fImage = cell;
    src.fFormat = SkMask::kA8_Format;
    SkMask dst;

    for (int y = 1; y <= 3; y++) {
        for (int x = 1; x <= 3; x++) {
            src.fBounds.set(0, 0, x, y);
            src.fRowBytes = src.fBounds.width();

            SkScalar radius = 1.f;

            printf("src [%d %d %d %d] radius %g\n", src.fBounds.fLeft, src.fBounds.fTop,
                   src.fBounds.fRight, src.fBounds.fBottom, radius);

            SkBlurMask::Blur(&dst, src, radius, SkBlurMask::kNormal_Style);
            uint8_t* dstPtr = dst.fImage;

            for (int y = 0; y < dst.fBounds.height(); y++) {
                for (int x = 0; x < dst.fBounds.width(); x++) {
                    printf(" %02X", dstPtr[x]);
                }
                printf("\n");
                dstPtr += dst.fRowBytes;
            }
        }
    }
}

static void scale_to_width(SkPath* path, SkScalar dstWidth) {
    const SkRect& bounds = path->getBounds();
    SkScalar scale = dstWidth / bounds.width();
    SkMatrix matrix;

    matrix.setScale(scale, scale);
    path->transform(matrix);
}

static const struct {
    SkPaint::Style  fStyle;
    SkPaint::Join   fJoin;
    int             fStrokeWidth;
} gRec[] = {
    { SkPaint::kFill_Style,             SkPaint::kMiter_Join,   0 },
    { SkPaint::kStroke_Style,           SkPaint::kMiter_Join,   0 },
    { SkPaint::kStroke_Style,           SkPaint::kMiter_Join,   10 },
    { SkPaint::kStrokeAndFill_Style,    SkPaint::kMiter_Join,   10 },
};

class StrokePathView : public SkView {
    SkScalar    fWidth;
    SkPath      fPath;
public:
	StrokePathView() {
        test_blur();
        fWidth = SkIntToScalar(120);

#if 0
        const char str[] =
            "M 0, 3"
            "C 10, -10, 30, -10, 0, 28"
            "C -30, -10, -10, -10, 0, 3"
            "Z";
        SkParsePath::FromSVGString(str, &fPath);
#else
        fPath.addCircle(0, 0, SkIntToScalar(50), SkPath::kCW_Direction);
        fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPath::kCW_Direction);
#endif

        scale_to_width(&fPath, fWidth);
        const SkRect& bounds = fPath.getBounds();
        fPath.offset(-bounds.fLeft, -bounds.fTop);
    }

protected:
    // overrides from SkEventSink
    virtual bool onQuery(SkEvent* evt) {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "StrokePath");
            return true;
        }
        return this->INHERITED::onQuery(evt);
    }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFDDDDDD);
    }

    SkRandom rand;

    void drawSet(SkCanvas* canvas, SkPaint* paint) {
        SkAutoCanvasRestore acr(canvas, true);

        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
            paint->setStyle(gRec[i].fStyle);
            paint->setStrokeJoin(gRec[i].fJoin);
            paint->setStrokeWidth(SkIntToScalar(gRec[i].fStrokeWidth));
            canvas->drawPath(fPath, *paint);
            canvas->translate(fWidth * 5 / 4, 0);
        }
    }

    virtual void onDraw(SkCanvas* canvas) {
        drawBG(canvas);
        //return;
        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));

        SkPaint paint;
        paint.setAntiAlias(true);

        if (true) {
            canvas->drawColor(SK_ColorBLACK);

            paint.setTextSize(24);
            paint.setColor(SK_ColorWHITE);
            canvas->translate(10, 30);

            static const SkBlurMaskFilter::BlurStyle gStyle[] = {
                SkBlurMaskFilter::kNormal_BlurStyle,
                SkBlurMaskFilter::kInner_BlurStyle,
                SkBlurMaskFilter::kOuter_BlurStyle,
                SkBlurMaskFilter::kSolid_BlurStyle,
            };
            for (int x = 0; x < 5; x++) {
                SkMaskFilter* mf;
                SkScalar radius = 4;
                for (int y = 0; y < 10; y++) {
                    if (x) {
                        mf = SkBlurMaskFilter::Create(radius, gStyle[x - 1]);
                        paint.setMaskFilter(mf)->unref();
                    }
                    canvas->drawText("Title Bar", 9, x*100, y*30, paint);
                    radius *= 0.75f;
                }

            }
            return;
        }

        paint.setColor(SK_ColorBLUE);

#if 1
        SkPath p;
        float r = rand.nextUScalar1() + 0.5f;
        SkScalar x = 0, y = 0;
        p.moveTo(x, y);
#if 0
        p.cubicTo(x-75*r, y+75*r, x-40*r, y+125*r, x, y+85*r);
        p.cubicTo(x+40*r, y+125*r, x+75*r, y+75*r, x, y);
#else
        p.cubicTo(x+75*r, y+75*r, x+40*r, y+125*r, x, y+85*r);
        p.cubicTo(x-40*r, y+125*r, x-75*r, y+75*r, x, y);
#endif
        p.close();
        fPath = p;
        fPath.offset(100, 0);
#endif

        fPath.setFillType(SkPath::kWinding_FillType);
        drawSet(canvas, &paint);

        canvas->translate(0, fPath.getBounds().height() * 5 / 4);
        fPath.setFillType(SkPath::kEvenOdd_FillType);
        drawSet(canvas, &paint);
    }

    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
        this->inval(NULL);
        return this->INHERITED::onFindClickHandler(x, y);
    }
private:
    typedef SkView INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static SkView* MyFactory() { return new StrokePathView; }
static SkViewRegister reg(MyFactory);

<commit_msg>#if 0 test code for blur, since it references a private header<commit_after>#include "SampleCode.h"
#include "SkCanvas.h"
#include "SkParsePath.h"
#include "SkPath.h"
#include "SkRandom.h"
#include "SkView.h"

#include "SkBlurMaskFilter.h"

#if 0
#include "SkBlurMask.h"
static void test_blur() {
    uint8_t cell[9];
    memset(cell, 0xFF, sizeof(cell));
    SkMask src;
    src.fImage = cell;
    src.fFormat = SkMask::kA8_Format;
    SkMask dst;

    for (int y = 1; y <= 3; y++) {
        for (int x = 1; x <= 3; x++) {
            src.fBounds.set(0, 0, x, y);
            src.fRowBytes = src.fBounds.width();

            SkScalar radius = 1.f;

            printf("src [%d %d %d %d] radius %g\n", src.fBounds.fLeft, src.fBounds.fTop,
                   src.fBounds.fRight, src.fBounds.fBottom, radius);

            SkBlurMask::Blur(&dst, src, radius, SkBlurMask::kNormal_Style);
            uint8_t* dstPtr = dst.fImage;

            for (int y = 0; y < dst.fBounds.height(); y++) {
                for (int x = 0; x < dst.fBounds.width(); x++) {
                    printf(" %02X", dstPtr[x]);
                }
                printf("\n");
                dstPtr += dst.fRowBytes;
            }
        }
    }
}
#endif

static void scale_to_width(SkPath* path, SkScalar dstWidth) {
    const SkRect& bounds = path->getBounds();
    SkScalar scale = dstWidth / bounds.width();
    SkMatrix matrix;

    matrix.setScale(scale, scale);
    path->transform(matrix);
}

static const struct {
    SkPaint::Style  fStyle;
    SkPaint::Join   fJoin;
    int             fStrokeWidth;
} gRec[] = {
    { SkPaint::kFill_Style,             SkPaint::kMiter_Join,   0 },
    { SkPaint::kStroke_Style,           SkPaint::kMiter_Join,   0 },
    { SkPaint::kStroke_Style,           SkPaint::kMiter_Join,   10 },
    { SkPaint::kStrokeAndFill_Style,    SkPaint::kMiter_Join,   10 },
};

class StrokePathView : public SkView {
    SkScalar    fWidth;
    SkPath      fPath;
public:
	StrokePathView() {
//        test_blur();
        fWidth = SkIntToScalar(120);

#if 0
        const char str[] =
            "M 0, 3"
            "C 10, -10, 30, -10, 0, 28"
            "C -30, -10, -10, -10, 0, 3"
            "Z";
        SkParsePath::FromSVGString(str, &fPath);
#else
        fPath.addCircle(0, 0, SkIntToScalar(50), SkPath::kCW_Direction);
        fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPath::kCW_Direction);
#endif

        scale_to_width(&fPath, fWidth);
        const SkRect& bounds = fPath.getBounds();
        fPath.offset(-bounds.fLeft, -bounds.fTop);
    }

protected:
    // overrides from SkEventSink
    virtual bool onQuery(SkEvent* evt) {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "StrokePath");
            return true;
        }
        return this->INHERITED::onQuery(evt);
    }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFDDDDDD);
    }

    SkRandom rand;

    void drawSet(SkCanvas* canvas, SkPaint* paint) {
        SkAutoCanvasRestore acr(canvas, true);

        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
            paint->setStyle(gRec[i].fStyle);
            paint->setStrokeJoin(gRec[i].fJoin);
            paint->setStrokeWidth(SkIntToScalar(gRec[i].fStrokeWidth));
            canvas->drawPath(fPath, *paint);
            canvas->translate(fWidth * 5 / 4, 0);
        }
    }

    virtual void onDraw(SkCanvas* canvas) {
        drawBG(canvas);
        //return;
        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));

        SkPaint paint;
        paint.setAntiAlias(true);

        if (true) {
            canvas->drawColor(SK_ColorBLACK);

            paint.setTextSize(24);
            paint.setColor(SK_ColorWHITE);
            canvas->translate(10, 30);

            static const SkBlurMaskFilter::BlurStyle gStyle[] = {
                SkBlurMaskFilter::kNormal_BlurStyle,
                SkBlurMaskFilter::kInner_BlurStyle,
                SkBlurMaskFilter::kOuter_BlurStyle,
                SkBlurMaskFilter::kSolid_BlurStyle,
            };
            for (int x = 0; x < 5; x++) {
                SkMaskFilter* mf;
                SkScalar radius = 4;
                for (int y = 0; y < 10; y++) {
                    if (x) {
                        mf = SkBlurMaskFilter::Create(radius, gStyle[x - 1]);
                        paint.setMaskFilter(mf)->unref();
                    }
                    canvas->drawText("Title Bar", 9, x*100, y*30, paint);
                    radius *= 0.75f;
                }

            }
            return;
        }

        paint.setColor(SK_ColorBLUE);

#if 1
        SkPath p;
        float r = rand.nextUScalar1() + 0.5f;
        SkScalar x = 0, y = 0;
        p.moveTo(x, y);
#if 0
        p.cubicTo(x-75*r, y+75*r, x-40*r, y+125*r, x, y+85*r);
        p.cubicTo(x+40*r, y+125*r, x+75*r, y+75*r, x, y);
#else
        p.cubicTo(x+75*r, y+75*r, x+40*r, y+125*r, x, y+85*r);
        p.cubicTo(x-40*r, y+125*r, x-75*r, y+75*r, x, y);
#endif
        p.close();
        fPath = p;
        fPath.offset(100, 0);
#endif

        fPath.setFillType(SkPath::kWinding_FillType);
        drawSet(canvas, &paint);

        canvas->translate(0, fPath.getBounds().height() * 5 / 4);
        fPath.setFillType(SkPath::kEvenOdd_FillType);
        drawSet(canvas, &paint);
    }

    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
        this->inval(NULL);
        return this->INHERITED::onFindClickHandler(x, y);
    }
private:
    typedef SkView INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static SkView* MyFactory() { return new StrokePathView; }
static SkViewRegister reg(MyFactory);

<|endoftext|>


<commit_before>/****************************************************************************
** Copyright (c) 2000-2003 Wayne Roth
** Copyright (c) 2004-2007 Stefan Sander
** Copyright (c) 2007 Michal Policht
** Copyright (c) 2008 Brandon Fosdick
** Copyright (c) 2009-2010 Liam Staskawicz
** Copyright (c) 2011 Debao Zhang
** Copyright (c) 2012 Doug Brown
** All right reserved.
** Web: http://code.google.com/p/qextserialport/
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/

#include "qextserialenumerator.h"
#include "qextserialenumerator_p.h"
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtCore/QDir>

void QextSerialEnumeratorPrivate::platformSpecificInit()
{
#ifndef QESP_NO_UDEV
    monitor = NULL;
    notifierFd = -1;
    notifier = NULL;

    udev = udev_new();
    if (!udev)
        qCritical() << "Unable to initialize udev notifications";
#endif
}

void QextSerialEnumeratorPrivate::platformSpecificDestruct()
{
#ifndef QESP_NO_UDEV
    if (notifier) {
        notifier->setEnabled(false);
        delete notifier;
    }

    if (monitor)
        udev_monitor_unref(monitor);

    if (udev)
        udev_unref(udev);
#endif
}

#ifndef QESP_NO_UDEV
static QextPortInfo portInfoFromDevice(struct udev_device *dev)
{
    QString vendor = QString::fromLatin1(udev_device_get_property_value(dev, "ID_VENDOR_ID"));
    QString product = QString::fromLatin1(udev_device_get_property_value(dev, "ID_MODEL_ID"));

    QextPortInfo pi;
    pi.vendorID = vendor.toInt(0, 16);
    pi.productID = product.toInt(0, 16);
    pi.portName = QString::fromLatin1(udev_device_get_devnode(dev));
    pi.physName = pi.portName;

    return pi;
}
#endif

QList<QextPortInfo> QextSerialEnumeratorPrivate::getPorts_sys()
{
    QList<QextPortInfo> infoList;
#ifndef QESP_NO_UDEV
    struct udev *ud = udev_new();
    if (!ud) {
        qCritical() << "Unable to enumerate ports because udev is not initialized.";
        return infoList;
    }

    struct udev_enumerate *enumerate = udev_enumerate_new(ud);
    udev_enumerate_add_match_subsystem(enumerate, "tty");
    udev_enumerate_scan_devices(enumerate);
    struct udev_list_entry *list = udev_enumerate_get_list_entry(enumerate);
    struct udev_list_entry *entry;
    udev_list_entry_foreach(entry, list) {
        const char *path;
        struct udev_device *dev;

        // Have to grab the actual udev device here...
        path = udev_list_entry_get_name(entry);
        dev = udev_device_new_from_syspath(ud, path);

        infoList.append(portInfoFromDevice(dev));

        // Done with this device
        udev_device_unref(dev);
    }
    // Done with the list and this udev
    udev_enumerate_unref(enumerate);
    udev_unref(ud);

    // get the non standard serial ports names
    // (USB-serial, bluetooth-serial, 18F PICs, and so on)
    // if you know an other name prefix for serial ports please let us know
    //    portNamePrefixes.clear();
    //    portNamePrefixes << QLatin1String("rfcomm*");
    //    portNamePrefixes << QLatin1String("serial/by-id/usb-*");
    //    portNameList += dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name);

    //    foreach (QString str , portNameList) {
    //        QextPortInfo inf;
    //        inf.physName = QLatin1String("/dev/")+str;
    //        inf.portName = str;

    //        if (str.contains(QLatin1String("rfcomm"))) {
    //            inf.friendName = QLatin1String("Bluetooth-serial adapter ")+str.remove(0, 6);
    //        }
    //        else if (str.contains(QLatin1String("serial/by-id/usb-"))) {
    //            inf.friendName = QLatin1String("USB-serial adapter ")+str.remove(0, 17);
    //        }
    //        inf.enumName = QLatin1String("/dev"); // is there a more helpful name for this?
    //        infoList.append(inf);
    //    }

    QList<QextPortInfo>::iterator it = infoList.begin();
    while (it != infoList.end()) {
        if (it->portName.contains(QString("ttyACM")))
        {
            qDebug() << __FILE__ << __LINE__ << "Found: " << it->portName;
            ++it;
        }
        else
        {
            it = infoList.erase(it);
        }
    }

#else
    QStringList portNamePrefixes, portNameList;
    portNamePrefixes << QLatin1String("ttyS*"); // list normal serial ports first

    QDir dir(QLatin1String("/dev"));
    portNameList = dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name);

    // remove the values which are not serial ports for e.g.  /dev/ttysa
    for (int i = 0; i < portNameList.size(); i++) {
        bool ok;
        QString current = portNameList.at(i);
        // remove the ttyS part, and check, if the other part is a number
        current.remove(0,4).toInt(&ok, 10);
        if (!ok) {
            portNameList.removeAt(i);
            i--;
        }
    }

    // get the non standard serial ports names
    // (USB-serial, bluetooth-serial, 18F PICs, and so on)
    // if you know an other name prefix for serial ports please let us know
    portNamePrefixes.clear();
    portNamePrefixes << QLatin1String("ttyACM*") << QLatin1String("ttyUSB*") << QLatin1String("rfcomm*");
    portNamePrefixes << QLatin1String("serial/by-id/usb-*");
    portNameList += dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name);

    foreach (QString str , portNameList) {
        QextPortInfo inf;
        inf.physName = QLatin1String("/dev/")+str;
        inf.portName = str;

        if (str.contains(QLatin1String("ttyS"))) {
            inf.friendName = QLatin1String("Serial port ")+str.remove(0, 4);
        }
        else if (str.contains(QLatin1String("ttyUSB"))) {
            inf.friendName = QLatin1String("USB-serial adapter ")+str.remove(0, 6);
        }
        else if (str.contains(QLatin1String("rfcomm"))) {
            inf.friendName = QLatin1String("Bluetooth-serial adapter ")+str.remove(0, 6);
        }
        else if (str.contains(QLatin1String("serial/by-id/usb-"))) {
            inf.friendName = QLatin1String("USB-serial adapter ")+str.remove(0, 17);
        }
        inf.enumName = QLatin1String("/dev"); // is there a more helpful name for this?
        infoList.append(inf);
    }
#endif

    return infoList;
}

bool QextSerialEnumeratorPrivate::setUpNotifications_sys(bool setup)
{
    Q_UNUSED(setup);
#ifndef QESP_NO_UDEV
    Q_Q(QextSerialEnumerator);
    if (!udev) {
        qCritical() << "Unable to initialize notifications because udev is not initialized.";
        return false;
    }

    // Emit signals immediately for devices already connected (Windows version seems to behave
    // this way)
    foreach (QextPortInfo i, getPorts_sys())
        Q_EMIT q->deviceDiscovered(i);

    // Look for tty devices from udev.
    monitor = udev_monitor_new_from_netlink(udev, "udev");
    udev_monitor_filter_add_match_subsystem_devtype(monitor, "tty", NULL);
    udev_monitor_enable_receiving(monitor);
    notifierFd = udev_monitor_get_fd(monitor);
    notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read);
    q->connect(notifier, SIGNAL(activated(int)), q, SLOT(_q_deviceEvent()));
    notifier->setEnabled(true);

    return true;
#else
    return false;
#endif
}

#ifndef QESP_NO_UDEV
void QextSerialEnumeratorPrivate::_q_deviceEvent()
{
    Q_Q(QextSerialEnumerator);
    struct udev_device *dev = udev_monitor_receive_device(monitor);
    if (dev) {
        QextPortInfo pi = portInfoFromDevice(dev);
        QLatin1String action(udev_device_get_action(dev));

        if (action == QLatin1String("add"))
            Q_EMIT q->deviceDiscovered(pi);
        else if (action == QLatin1String("remove"))
            Q_EMIT q->deviceRemoved(pi);

        udev_device_unref(dev);
    }
}
#endif
<commit_msg>More qextserial hacking<commit_after>/****************************************************************************
** Copyright (c) 2000-2003 Wayne Roth
** Copyright (c) 2004-2007 Stefan Sander
** Copyright (c) 2007 Michal Policht
** Copyright (c) 2008 Brandon Fosdick
** Copyright (c) 2009-2010 Liam Staskawicz
** Copyright (c) 2011 Debao Zhang
** Copyright (c) 2012 Doug Brown
** All right reserved.
** Web: http://code.google.com/p/qextserialport/
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/

#include "qextserialenumerator.h"
#include "qextserialenumerator_p.h"
#include <QtCore/QDebug>
#include <QtCore/QStringList>
#include <QtCore/QDir>

void QextSerialEnumeratorPrivate::platformSpecificInit()
{
// #ifndef QESP_NO_UDEV
//     monitor = NULL;
//     notifierFd = -1;
//     notifier = NULL;

//     udev = udev_new();
//     if (!udev)
//         qCritical() << "Unable to initialize udev notifications";
// #endif
}

void QextSerialEnumeratorPrivate::platformSpecificDestruct()
{
// #ifndef QESP_NO_UDEV
//     if (notifier) {
//         notifier->setEnabled(false);
//         delete notifier;
//     }

//     if (monitor)
//         udev_monitor_unref(monitor);

//     if (udev)
//         udev_unref(udev);
// #endif
}

#ifndef QESP_NO_UDEV
static QextPortInfo portInfoFromDevice(struct udev_device *dev)
{
    QString vendor = QString::fromLatin1(udev_device_get_property_value(dev, "ID_VENDOR_ID"));
    QString product = QString::fromLatin1(udev_device_get_property_value(dev, "ID_MODEL_ID"));

    QextPortInfo pi;
    pi.vendorID = vendor.toInt(0, 16);
    pi.productID = product.toInt(0, 16);
    pi.portName = QString::fromLatin1(udev_device_get_devnode(dev));
    pi.physName = pi.portName;

    return pi;
}
#endif

QList<QextPortInfo> QextSerialEnumeratorPrivate::getPorts_sys()
{
    QList<QextPortInfo> infoList;
// #ifndef QESP_NO_UDEV
//     struct udev *ud = udev_new();
//     if (!ud) {
//         qCritical() << "Unable to enumerate ports because udev is not initialized.";
//         return infoList;
//     }

//     struct udev_enumerate *enumerate = udev_enumerate_new(ud);
//     udev_enumerate_add_match_subsystem(enumerate, "tty");
//     udev_enumerate_scan_devices(enumerate);
//     struct udev_list_entry *list = udev_enumerate_get_list_entry(enumerate);
//     struct udev_list_entry *entry;
//     udev_list_entry_foreach(entry, list) {
//         const char *path;
//         struct udev_device *dev;

//         // Have to grab the actual udev device here...
//         path = udev_list_entry_get_name(entry);
//         dev = udev_device_new_from_syspath(ud, path);

//         infoList.append(portInfoFromDevice(dev));

//         // Done with this device
//         udev_device_unref(dev);
//     }
//     // Done with the list and this udev
//     udev_enumerate_unref(enumerate);
//     udev_unref(ud);

//     // get the non standard serial ports names
//     // (USB-serial, bluetooth-serial, 18F PICs, and so on)
//     // if you know an other name prefix for serial ports please let us know
//     //    portNamePrefixes.clear();
//     //    portNamePrefixes << QLatin1String("rfcomm*");
//     //    portNamePrefixes << QLatin1String("serial/by-id/usb-*");
//     //    portNameList += dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name);

//     //    foreach (QString str , portNameList) {
//     //        QextPortInfo inf;
//     //        inf.physName = QLatin1String("/dev/")+str;
//     //        inf.portName = str;

//     //        if (str.contains(QLatin1String("rfcomm"))) {
//     //            inf.friendName = QLatin1String("Bluetooth-serial adapter ")+str.remove(0, 6);
//     //        }
//     //        else if (str.contains(QLatin1String("serial/by-id/usb-"))) {
//     //            inf.friendName = QLatin1String("USB-serial adapter ")+str.remove(0, 17);
//     //        }
//     //        inf.enumName = QLatin1String("/dev"); // is there a more helpful name for this?
//     //        infoList.append(inf);
//     //    }

//     QList<QextPortInfo>::iterator it = infoList.begin();
//     while (it != infoList.end()) {
//         if (it->portName.contains(QString("ttyACM")))
//         {
//             qDebug() << __FILE__ << __LINE__ << "Found: " << it->portName;
//             ++it;
//         }
//         else
//         {
//             it = infoList.erase(it);
//         }
//     }

// #else
    QStringList portNamePrefixes, portNameList;
    portNamePrefixes << QLatin1String("ttyS*"); // list normal serial ports first

    QDir dir(QLatin1String("/dev"));
    portNameList = dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name);

    // remove the values which are not serial ports for e.g.  /dev/ttysa
    for (int i = 0; i < portNameList.size(); i++) {
        bool ok;
        QString current = portNameList.at(i);
        // remove the ttyS part, and check, if the other part is a number
        current.remove(0,4).toInt(&ok, 10);
        if (!ok) {
            portNameList.removeAt(i);
            i--;
        }
    }

    // get the non standard serial ports names
    // (USB-serial, bluetooth-serial, 18F PICs, and so on)
    // if you know an other name prefix for serial ports please let us know
    portNamePrefixes.clear();
    portNamePrefixes << QLatin1String("ttyACM*") << QLatin1String("ttyUSB*") << QLatin1String("rfcomm*");
    portNamePrefixes << QLatin1String("serial/by-id/usb-*");
    portNameList += dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name);

    foreach (QString str , portNameList) {
        QextPortInfo inf;
        inf.physName = QLatin1String("/dev/")+str;
        inf.portName = str;

        if (str.contains(QLatin1String("ttyS"))) {
            inf.friendName = QLatin1String("Serial port ")+str.remove(0, 4);
        }
        else if (str.contains(QLatin1String("ttyUSB"))) {
            inf.friendName = QLatin1String("USB-serial adapter ")+str.remove(0, 6);
        }
        else if (str.contains(QLatin1String("rfcomm"))) {
            inf.friendName = QLatin1String("Bluetooth-serial adapter ")+str.remove(0, 6);
        }
        else if (str.contains(QLatin1String("serial/by-id/usb-"))) {
            inf.friendName = QLatin1String("USB-serial adapter ")+str.remove(0, 17);
        }
        inf.enumName = QLatin1String("/dev"); // is there a more helpful name for this?
        infoList.append(inf);
    }
// #endif

    return infoList;
}

bool QextSerialEnumeratorPrivate::setUpNotifications_sys(bool setup)
{
    Q_UNUSED(setup);
// #ifndef QESP_NO_UDEV
//     Q_Q(QextSerialEnumerator);
//     if (!udev) {
//         qCritical() << "Unable to initialize notifications because udev is not initialized.";
//         return false;
//     }

//     // Emit signals immediately for devices already connected (Windows version seems to behave
//     // this way)
//     foreach (QextPortInfo i, getPorts_sys())
//         Q_EMIT q->deviceDiscovered(i);

//     // Look for tty devices from udev.
//     monitor = udev_monitor_new_from_netlink(udev, "udev");
//     udev_monitor_filter_add_match_subsystem_devtype(monitor, "tty", NULL);
//     udev_monitor_enable_receiving(monitor);
//     notifierFd = udev_monitor_get_fd(monitor);
//     notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read);
//     q->connect(notifier, SIGNAL(activated(int)), q, SLOT(_q_deviceEvent()));
//     notifier->setEnabled(true);

//     return true;
// #else
    return false;
// #endif
}

// #ifndef QESP_NO_UDEV
// void QextSerialEnumeratorPrivate::_q_deviceEvent()
// {
//     Q_Q(QextSerialEnumerator);
//     struct udev_device *dev = udev_monitor_receive_device(monitor);
//     if (dev) {
//         QextPortInfo pi = portInfoFromDevice(dev);
//         QLatin1String action(udev_device_get_action(dev));

//         if (action == QLatin1String("add"))
//             Q_EMIT q->deviceDiscovered(pi);
//         else if (action == QLatin1String("remove"))
//             Q_EMIT q->deviceRemoved(pi);

//         udev_device_unref(dev);
//     }
// }
// #endif
<|endoftext|>


<commit_before>// Main.cpp
// UniversalPauseButton
// Ryan Ries, 2015
// ryan@myotherpcisacloud.com
//
// Must compile in Unicode.

#include <Windows.h>
#include <Psapi.h>
#include <stdio.h>
#include "resource.h"

#define WM_TRAYICON (WM_USER + 1)

// WARNING: Undocumented Win32 API functions!
// Microsoft may change these at any time; they are not guaranteed to work on the next version of Windows.
typedef LONG(NTAPI* _NtSuspendProcess) (IN HANDLE ProcessHandle);
typedef LONG(NTAPI* _NtResumeProcess)  (IN HANDLE ProcessHandle);

_NtSuspendProcess NtSuspendProcess = (_NtSuspendProcess)GetProcAddress(GetModuleHandle(L"ntdll"), "NtSuspendProcess");
_NtResumeProcess NtResumeProcess = (_NtResumeProcess)GetProcAddress(GetModuleHandle(L"ntdll"), "NtResumeProcess");

NOTIFYICONDATA G_TrayNotifyIconData;
HANDLE         G_Mutex;

// NOTE(Ryan): This function returns true if the string ends with the specified Suffix/substring.
// Uses wide characters. Not case sensitive.
int StringEndsWithW(_In_ const wchar_t *Str, _In_ const wchar_t *Suffix)
{
	if (Str == NULL || Suffix == NULL)
	{
		return 0;
	}

	size_t str_len = wcslen(Str);
	size_t suffix_len = wcslen(Suffix);

	if (suffix_len > str_len)
	{
		return 0;
	}

	return 0 == _wcsnicmp(Str + str_len - suffix_len, Suffix, suffix_len);
}

// The WindowProc (callback) for WinMain's WindowClass.
// Basically the system tray does nothing except lets the user know that it's running.
// If the user clicks the tray icon it will ask if they want to exit the app.
LRESULT CALLBACK WindowClassCallback(_In_ HWND Window, _In_ UINT Message, _In_ WPARAM WParam, _In_ LPARAM LParam)
{
	LRESULT Result = 0;
	static BOOL QuitMessageBoxIsShowing = FALSE;

	switch (Message)
	{
		case WM_TRAYICON:
		{
			if (!QuitMessageBoxIsShowing && (LParam == WM_LBUTTONDOWN || LParam == WM_RBUTTONDOWN || LParam == WM_MBUTTONDOWN))
			{
				QuitMessageBoxIsShowing = TRUE;
				if (MessageBox(Window, L"Quit UniversalPauseButton?", L"Are you sure?", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL) == IDYES)
				{
					Shell_NotifyIcon(NIM_DELETE, &G_TrayNotifyIconData);
					PostQuitMessage(0);
				}
				else
				{
					QuitMessageBoxIsShowing = FALSE;
				}
			}
			break;
		}
		default:
		{
			Result = DefWindowProc(Window, Message, WParam, LParam);
			break;
		}
	}
	return(Result);
}

// Entry point.
int CALLBACK WinMain(_In_ HINSTANCE Instance, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int)
{
	G_Mutex = CreateMutex(NULL, FALSE, L"UniversalPauseButton");
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		MessageBox(NULL, L"An instance of the program is already running.", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(ERROR_ALREADY_EXISTS);
	}

	WNDCLASS SysTrayWindowClass = { 0 };

	SysTrayWindowClass.style         = CS_HREDRAW | CS_VREDRAW;
	SysTrayWindowClass.hInstance     = Instance;
	SysTrayWindowClass.lpszClassName = L"UniversalPauseButton_Systray_WindowClass";
	SysTrayWindowClass.hbrBackground = CreateSolidBrush(RGB(255, 0, 255));
	SysTrayWindowClass.lpfnWndProc   = WindowClassCallback;

	if (RegisterClass(&SysTrayWindowClass) == 0)
	{
		MessageBox(NULL, L"Failed to register WindowClass!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	HWND SystrayWindow = CreateWindowEx(
		WS_EX_TOOLWINDOW,
		SysTrayWindowClass.lpszClassName,
		L"UniversalPauseButton_Systray_Window",
		WS_ICONIC,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		0,
		0,
		Instance,
		0);

	if (SystrayWindow == 0)
	{
		MessageBox(NULL, L"Failed to create SystrayWindow!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	G_TrayNotifyIconData.cbSize           = sizeof(NOTIFYICONDATA);
	G_TrayNotifyIconData.hWnd             = SystrayWindow;
	G_TrayNotifyIconData.uID              = 1982;
	G_TrayNotifyIconData.uFlags           = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	G_TrayNotifyIconData.uCallbackMessage = WM_TRAYICON;

	wcscpy_s(G_TrayNotifyIconData.szTip, L"Universal Pause Button v1.0.1");

	G_TrayNotifyIconData.hIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, NULL);

	if (G_TrayNotifyIconData.hIcon == NULL)
	{
		MessageBox(NULL, L"Failed to load systray icon resource!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	if (Shell_NotifyIcon(NIM_ADD, &G_TrayNotifyIconData) == FALSE)
	{
		MessageBox(NULL, L"Failed to register systray icon!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	MSG        SysTrayWindowMessage                 = { 0 };
	static int PauseKeyWasDown                      = 0;
	DWORD      PreviouslySuspendedProcessID         = 0;
	wchar_t    PreviouslySuspendedProcessText[256]  = { 0 };
	HANDLE     ProcessHandle                        = 0;

	while (SysTrayWindowMessage.message != WM_QUIT)
	{
		while (PeekMessage(&SysTrayWindowMessage, SystrayWindow, 0, 0, PM_REMOVE))
		{
			DispatchMessage(&SysTrayWindowMessage);
		}

		int PauseKeyIsDown = GetAsyncKeyState(VK_PAUSE);

		if (PauseKeyIsDown && !PauseKeyWasDown)
		{
			HWND ForegroundWindow = GetForegroundWindow();
			if (!ForegroundWindow)
			{
				MessageBox(NULL, L"Unable to detect foreground window!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				goto EndOfLoop;
			}

			DWORD ProcessID = 0;
			GetWindowThreadProcessId(ForegroundWindow, &ProcessID);
			if (ProcessID == 0)
			{
				MessageBox(NULL, L"Unable to get process ID of foreground window!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				goto EndOfLoop;
			}

			ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
			if (ProcessHandle == 0)
			{
				MessageBox(NULL, L"OpenProcess failed!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				goto EndOfLoop;
			}

			if (PreviouslySuspendedProcessID == 0)
			{
				// I won't let you pause your shell. Nothing good will come of that.
				// Later I will get the user's shell from the registry, just to cover the 0.001% case
				// that the user has a custom shell.
				wchar_t ImageFileName[MAX_PATH] = { 0 };
				GetProcessImageFileName(ProcessHandle, ImageFileName, sizeof(ImageFileName) / sizeof(wchar_t));
				if (!StringEndsWithW(ImageFileName, L"explorer.exe"))
				{
					NtSuspendProcess(ProcessHandle);
					PreviouslySuspendedProcessID = ProcessID;
					GetWindowText(ForegroundWindow, PreviouslySuspendedProcessText, sizeof(PreviouslySuspendedProcessText) / sizeof(wchar_t));
				}
				else
				{
					MessageBox(NULL, L"You cannot pause your shell.", L"UniversalPauseButton Error", MB_OK | MB_ICONWARNING | MB_SYSTEMMODAL);
				}
			}
			else if (PreviouslySuspendedProcessID == ProcessID)
			{
				NtResumeProcess(ProcessHandle);
				PreviouslySuspendedProcessID = 0;
				memset(PreviouslySuspendedProcessText, 0, sizeof(PreviouslySuspendedProcessText));
			}
			else
			{
				// The user pressed the pause button while focused on another process than what was
				// originally paused and the first process is still paused.
				DWORD AllProcesses[2048] = { 0 };
				DWORD BytesReturned = 0;
				BOOL PreviouslySuspendedProcessIsStillRunning = FALSE;

				if (EnumProcesses(AllProcesses, sizeof(AllProcesses), &BytesReturned) != 0)
				{
					for (DWORD Counter = 0; Counter < (BytesReturned / sizeof(DWORD)); Counter++)
					{
						if ((AllProcesses[Counter] != 0) && AllProcesses[Counter] == PreviouslySuspendedProcessID)
						{
							PreviouslySuspendedProcessIsStillRunning = TRUE;
						}
					}
					if (PreviouslySuspendedProcessIsStillRunning)
					{
						wchar_t MessageBoxBuffer[1024] = { 0 };
						_snwprintf_s(MessageBoxBuffer, sizeof(MessageBoxBuffer), L"You must first unpause %s (PID %d) before pausing another program.", PreviouslySuspendedProcessText, PreviouslySuspendedProcessID);
						MessageBox(ForegroundWindow, MessageBoxBuffer, L"Universal Pause Button", MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL);
					}
					else
					{
						// The paused process is no more, so reset.
						PreviouslySuspendedProcessID = 0;
						memset(PreviouslySuspendedProcessText, 0, sizeof(PreviouslySuspendedProcessText));
					}
				}
				else
				{
					MessageBox(NULL, L"EnumProcesses failed!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				}
			}
		}

		// http://stackoverflow.com/questions/245742/examples-of-good-gotos-in-c-or-c

	EndOfLoop:
		if (ProcessHandle)
		{
			CloseHandle(ProcessHandle);
		}

		PauseKeyWasDown = PauseKeyIsDown;
		Sleep(10);
	}

	return(S_OK);
}<commit_msg>Update version number<commit_after>// Main.cpp
// UniversalPauseButton
// Ryan Ries, 2015
// ryan@myotherpcisacloud.com
//
// Must compile in Unicode.

#include <Windows.h>
#include <Psapi.h>
#include <stdio.h>
#include "resource.h"

#define WM_TRAYICON (WM_USER + 1)

// WARNING: Undocumented Win32 API functions!
// Microsoft may change these at any time; they are not guaranteed to work on the next version of Windows.
typedef LONG(NTAPI* _NtSuspendProcess) (IN HANDLE ProcessHandle);
typedef LONG(NTAPI* _NtResumeProcess)  (IN HANDLE ProcessHandle);

_NtSuspendProcess NtSuspendProcess = (_NtSuspendProcess)GetProcAddress(GetModuleHandle(L"ntdll"), "NtSuspendProcess");
_NtResumeProcess NtResumeProcess = (_NtResumeProcess)GetProcAddress(GetModuleHandle(L"ntdll"), "NtResumeProcess");

NOTIFYICONDATA G_TrayNotifyIconData;
HANDLE         G_Mutex;

// NOTE(Ryan): This function returns true if the string ends with the specified Suffix/substring.
// Uses wide characters. Not case sensitive.
int StringEndsWithW(_In_ const wchar_t *Str, _In_ const wchar_t *Suffix)
{
	if (Str == NULL || Suffix == NULL)
	{
		return 0;
	}

	size_t str_len = wcslen(Str);
	size_t suffix_len = wcslen(Suffix);

	if (suffix_len > str_len)
	{
		return 0;
	}

	return 0 == _wcsnicmp(Str + str_len - suffix_len, Suffix, suffix_len);
}

// The WindowProc (callback) for WinMain's WindowClass.
// Basically the system tray does nothing except lets the user know that it's running.
// If the user clicks the tray icon it will ask if they want to exit the app.
LRESULT CALLBACK WindowClassCallback(_In_ HWND Window, _In_ UINT Message, _In_ WPARAM WParam, _In_ LPARAM LParam)
{
	LRESULT Result = 0;
	static BOOL QuitMessageBoxIsShowing = FALSE;

	switch (Message)
	{
		case WM_TRAYICON:
		{
			if (!QuitMessageBoxIsShowing && (LParam == WM_LBUTTONDOWN || LParam == WM_RBUTTONDOWN || LParam == WM_MBUTTONDOWN))
			{
				QuitMessageBoxIsShowing = TRUE;
				if (MessageBox(Window, L"Quit UniversalPauseButton?", L"Are you sure?", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL) == IDYES)
				{
					Shell_NotifyIcon(NIM_DELETE, &G_TrayNotifyIconData);
					PostQuitMessage(0);
				}
				else
				{
					QuitMessageBoxIsShowing = FALSE;
				}
			}
			break;
		}
		default:
		{
			Result = DefWindowProc(Window, Message, WParam, LParam);
			break;
		}
	}
	return(Result);
}

// Entry point.
int CALLBACK WinMain(_In_ HINSTANCE Instance, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int)
{
	G_Mutex = CreateMutex(NULL, FALSE, L"UniversalPauseButton");
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		MessageBox(NULL, L"An instance of the program is already running.", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(ERROR_ALREADY_EXISTS);
	}

	WNDCLASS SysTrayWindowClass = { 0 };

	SysTrayWindowClass.style         = CS_HREDRAW | CS_VREDRAW;
	SysTrayWindowClass.hInstance     = Instance;
	SysTrayWindowClass.lpszClassName = L"UniversalPauseButton_Systray_WindowClass";
	SysTrayWindowClass.hbrBackground = CreateSolidBrush(RGB(255, 0, 255));
	SysTrayWindowClass.lpfnWndProc   = WindowClassCallback;

	if (RegisterClass(&SysTrayWindowClass) == 0)
	{
		MessageBox(NULL, L"Failed to register WindowClass!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	HWND SystrayWindow = CreateWindowEx(
		WS_EX_TOOLWINDOW,
		SysTrayWindowClass.lpszClassName,
		L"UniversalPauseButton_Systray_Window",
		WS_ICONIC,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		0,
		0,
		Instance,
		0);

	if (SystrayWindow == 0)
	{
		MessageBox(NULL, L"Failed to create SystrayWindow!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	G_TrayNotifyIconData.cbSize           = sizeof(NOTIFYICONDATA);
	G_TrayNotifyIconData.hWnd             = SystrayWindow;
	G_TrayNotifyIconData.uID              = 1982;
	G_TrayNotifyIconData.uFlags           = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	G_TrayNotifyIconData.uCallbackMessage = WM_TRAYICON;

	wcscpy_s(G_TrayNotifyIconData.szTip, L"Universal Pause Button v1.0.2");

	G_TrayNotifyIconData.hIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, NULL);

	if (G_TrayNotifyIconData.hIcon == NULL)
	{
		MessageBox(NULL, L"Failed to load systray icon resource!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	if (Shell_NotifyIcon(NIM_ADD, &G_TrayNotifyIconData) == FALSE)
	{
		MessageBox(NULL, L"Failed to register systray icon!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
		return(E_FAIL);
	}

	MSG        SysTrayWindowMessage                 = { 0 };
	static int PauseKeyWasDown                      = 0;
	DWORD      PreviouslySuspendedProcessID         = 0;
	wchar_t    PreviouslySuspendedProcessText[256]  = { 0 };
	HANDLE     ProcessHandle                        = 0;

	while (SysTrayWindowMessage.message != WM_QUIT)
	{
		while (PeekMessage(&SysTrayWindowMessage, SystrayWindow, 0, 0, PM_REMOVE))
		{
			DispatchMessage(&SysTrayWindowMessage);
		}

		int PauseKeyIsDown = GetAsyncKeyState(VK_PAUSE);

		if (PauseKeyIsDown && !PauseKeyWasDown)
		{
			HWND ForegroundWindow = GetForegroundWindow();
			if (!ForegroundWindow)
			{
				MessageBox(NULL, L"Unable to detect foreground window!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				goto EndOfLoop;
			}

			DWORD ProcessID = 0;
			GetWindowThreadProcessId(ForegroundWindow, &ProcessID);
			if (ProcessID == 0)
			{
				MessageBox(NULL, L"Unable to get process ID of foreground window!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				goto EndOfLoop;
			}

			ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
			if (ProcessHandle == 0)
			{
				MessageBox(NULL, L"OpenProcess failed!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				goto EndOfLoop;
			}

			if (PreviouslySuspendedProcessID == 0)
			{
				// I won't let you pause your shell. Nothing good will come of that.
				// Later I will get the user's shell from the registry, just to cover the 0.001% case
				// that the user has a custom shell.
				wchar_t ImageFileName[MAX_PATH] = { 0 };
				GetProcessImageFileName(ProcessHandle, ImageFileName, sizeof(ImageFileName) / sizeof(wchar_t));
				if (!StringEndsWithW(ImageFileName, L"explorer.exe"))
				{
					NtSuspendProcess(ProcessHandle);
					PreviouslySuspendedProcessID = ProcessID;
					GetWindowText(ForegroundWindow, PreviouslySuspendedProcessText, sizeof(PreviouslySuspendedProcessText) / sizeof(wchar_t));
				}
				else
				{
					MessageBox(NULL, L"You cannot pause your shell.", L"UniversalPauseButton Error", MB_OK | MB_ICONWARNING | MB_SYSTEMMODAL);
				}
			}
			else if (PreviouslySuspendedProcessID == ProcessID)
			{
				NtResumeProcess(ProcessHandle);
				PreviouslySuspendedProcessID = 0;
				memset(PreviouslySuspendedProcessText, 0, sizeof(PreviouslySuspendedProcessText));
			}
			else
			{
				// The user pressed the pause button while focused on another process than what was
				// originally paused and the first process is still paused.
				DWORD AllProcesses[2048] = { 0 };
				DWORD BytesReturned = 0;
				BOOL PreviouslySuspendedProcessIsStillRunning = FALSE;

				if (EnumProcesses(AllProcesses, sizeof(AllProcesses), &BytesReturned) != 0)
				{
					for (DWORD Counter = 0; Counter < (BytesReturned / sizeof(DWORD)); Counter++)
					{
						if ((AllProcesses[Counter] != 0) && AllProcesses[Counter] == PreviouslySuspendedProcessID)
						{
							PreviouslySuspendedProcessIsStillRunning = TRUE;
						}
					}
					if (PreviouslySuspendedProcessIsStillRunning)
					{
						wchar_t MessageBoxBuffer[1024] = { 0 };
						_snwprintf_s(MessageBoxBuffer, sizeof(MessageBoxBuffer), L"You must first unpause %s (PID %d) before pausing another program.", PreviouslySuspendedProcessText, PreviouslySuspendedProcessID);
						MessageBox(ForegroundWindow, MessageBoxBuffer, L"Universal Pause Button", MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL);
					}
					else
					{
						// The paused process is no more, so reset.
						PreviouslySuspendedProcessID = 0;
						memset(PreviouslySuspendedProcessText, 0, sizeof(PreviouslySuspendedProcessText));
					}
				}
				else
				{
					MessageBox(NULL, L"EnumProcesses failed!", L"UniversalPauseButton Error", MB_OK | MB_ICONERROR);
				}
			}
		}

		// http://stackoverflow.com/questions/245742/examples-of-good-gotos-in-c-or-c

	EndOfLoop:
		if (ProcessHandle)
		{
			CloseHandle(ProcessHandle);
		}

		PauseKeyWasDown = PauseKeyIsDown;
		Sleep(10);
	}

	return(S_OK);
}<|endoftext|>


<commit_before>#include "Players/Genetic_AI.h"

#include <iostream>
#include <fstream>
#include <vector>

#include "Moves/Move.h"
#include "Game/Board.h"
#include "Game/Clock.h"

#include "Exceptions/Checkmate_Exception.h"
#include "Exceptions/Game_Ending_Exception.h"
#include "Exceptions/End_Of_File_Exception.h"

#include "Utility.h"

int Genetic_AI::next_id = 0;

Genetic_AI::Genetic_AI() :
    genome(),
    id(next_id++)
{
}

// Sexual reproduction
Genetic_AI::Genetic_AI(const Genetic_AI& A, const Genetic_AI& B) :
    genome(A.genome, B.genome),
    id(next_id++)
{
}

Genetic_AI::~Genetic_AI()
{
}

Genetic_AI::Genetic_AI(const std::string& file_name)
{
    std::ifstream ifs(file_name);
    if( ! ifs)
    {
        throw std::runtime_error("Could not read: " + file_name);
    }

    read_from(ifs);
}

Genetic_AI::Genetic_AI(std::istream& is)
{
    read_from(is);
}

Genetic_AI::Genetic_AI(const std::string& file_name, int id_in) : id(id_in)
{
    std::ifstream ifs(file_name);
    if( ! ifs)
    {
        throw std::runtime_error("Could not read: " + file_name);
    }

    std::string line;
    while(std::getline(ifs, line))
    {
        if( ! String::starts_with(line, "ID:"))
        {
            continue;
        }

        auto param_value = String::split(line, ":", 1);
        if(id_in == std::stoi(param_value[1]))
        {
            genome.read_from(ifs);
            return;
        }
    }

    throw std::runtime_error("Could not locate ID " + std::to_string(id_in) + " inside file " + file_name);
}

void Genetic_AI::read_from(std::istream& is)
{
    std::string line;
    id = -1;
    while(std::getline(is, line))
    {
        if(line.empty())
        {
            continue;
        }

        if(String::starts_with(line, "ID:"))
        {
            id = std::stoi(String::split(line)[1]);
            break;
        }
        else
        {
            throw std::runtime_error("Invalid Genetic_AI line: " + line);
        }
    }

    if( ! is)
    {
        if(id > -1)
        {
            throw std::runtime_error("Incomplete Genetic_AI spec in file for ID " + std::to_string(id));
        }
        else
        {
            throw End_Of_File_Exception();
        }
    }

    if(id >= next_id)
    {
        next_id = id + 1;
    }

    genome.read_from(is);
}

const Complete_Move Genetic_AI::choose_move(const Board& board, const Clock& clock) const
{
    const auto& legal_moves = board.all_legal_moves();
    if(legal_moves.size() == 1)
    {
        return legal_moves.front(); // If there's only one legal move, take it.
    }

    auto positions_to_examine = genome.positions_to_examine(board, clock);
    auto result = search_game_tree(board,
                                   positions_to_examine,
                                   clock,
                                   0);
    if(result.depth > 0)
    {
        board.add_commentary_to_next_move(result.commentary);
    }
    return result.move;
}

Game_Tree_Node_Result Genetic_AI::search_game_tree(const Board& board,
                                                   int& positions_to_examine,
                                                   const Clock& clock,
                                                   const int depth) const
{
    // Every call to search_game_tree() after the first
    // costs a position_to_examine.
    if(depth > 0 && positions_to_examine > 0)
    {
        --positions_to_examine;
    }

    auto perspective = board.whose_turn();

    std::vector<Game_Tree_Node_Result> results;

    // Moves worth examining further
    std::vector<std::tuple<Complete_Move, Board, double>> further_examine; // move, resulting board, score

    // Find moves worth examining
    for(const auto& move : board.all_legal_moves())
    {
        if(clock.time_left(clock.running_for()) < 0.0)
        {
            return {board.all_legal_moves().front(),
                    0.0,
                    perspective,
                    depth,
                    "Out of time"};
        }

        auto next_board = board;

        try
        {
            next_board.submit_move(move);

            if(next_board.all_legal_moves().size() > 1 &&
               (positions_to_examine <= 0
               || ! genome.good_enough_to_examine(board, next_board, perspective)))
            {
                // Record immediate result without looking ahead further
                results.push_back({move,
                                   genome.evaluate(next_board, perspective),
                                   perspective,
                                   depth,
                                   next_board.get_game_record().back()});
            }
            else
            {
                further_examine.push_back({move, next_board, genome.evaluate(next_board, perspective)});
            }
        }
        catch(const Checkmate_Exception&)
        {
            // Mate in one (try to pick the shortest path to checkmate)
            auto score = genome.evaluate(next_board, perspective);
            auto comment = next_board.get_game_record().back();

            return {move,
                    score,
                    perspective,
                    depth,
                    comment};
        }
        catch(const Game_Ending_Exception&)
        {
            // Draw
            results.push_back({move,
                               genome.evaluate(next_board, perspective),
                               perspective,
                               depth,
                               next_board.get_game_record().back()});
        }
    }

    // Sort moves by score so higher scores get more moves for examination
    std::sort(further_examine.begin(),
              further_examine.end(),
              [](const auto& x, const auto& y)
              {
                  return std::get<double>(x) < std::get<double>(y);
              });

    // Look ahead through moves found above
    for(size_t i = 0; i < further_examine.size(); ++i)
    {
        int moves_left = further_examine.size() - i;
        int positions_for_this_move = positions_to_examine/moves_left;

        if(positions_for_this_move > 0 || std::get<Board>(further_examine[i]).all_legal_moves().size() == 1)
        {
            positions_to_examine -= positions_for_this_move;

            results.push_back(search_game_tree(std::get<Board>(further_examine[i]),
                                               positions_for_this_move,
                                               clock,
                                               depth + 1));
            // Update last result with this game tree node's data
            results.back().move = std::get<Complete_Move>(further_examine[i]);
            results.back().commentary = std::get<Board>(further_examine[i]).get_game_record().back()
                                        + " "
                                        + results.back().commentary;

            positions_to_examine += positions_for_this_move;
        }
        else
        {
            results.push_back({std::get<Complete_Move>(further_examine[i]),
                               std::get<double>(further_examine[i]),
                               perspective,
                               depth,
                               std::get<Board>(further_examine[i]).get_game_record().back()});
        }
    }

    // Consider all results and return best
    auto best_score = -Math::infinity;
    auto best_move = board.all_legal_moves().front();
    auto best_depth = 0;
    std::string comments_on_best_move;

    for(const auto& result : results)
    {
        auto score = result.score;
        if(result.perspective != perspective)
        {
            score = -score;
        }

        // Prefer ...
        if(score > best_score // ... better score
           || (score ==  Math::infinity && result.depth < best_depth) // shortest path to victory
           || (best_score == -Math::infinity && result.depth > best_depth)) // longest path to defeat
        {
            best_score = score;
            best_move = result.move;
            best_depth = result.depth;
            comments_on_best_move = result.commentary;
        }
    }

    return {best_move,
            best_score,
            perspective,
            best_depth,
            comments_on_best_move};
}

void Genetic_AI::mutate()
{
    genome.mutate();
}

void Genetic_AI::print_genome(const std::string& file_name) const
{
    if(file_name.empty())
    {
        print_genome(std::cout);
    }
    else
    {
        auto dest = std::ofstream(file_name, std::ofstream::app);
        print_genome(dest);
    }
}

void Genetic_AI::print_genome(std::ostream& os) const
{
    os << "ID: " << get_id() << std::endl;
    genome.print(os);
    os << "END" << std::endl << std::endl;
}

std::string Genetic_AI::name() const
{
    return "Genetic AI " + std::to_string(get_id());
}

int Genetic_AI::get_id() const
{
    return id;
}

bool Genetic_AI::operator<(const Genetic_AI& other) const
{
    return get_id() < other.get_id();
}

bool Genetic_AI::operator==(const Genetic_AI& other) const
{
    return get_id() == other.get_id();
}
<commit_msg>Extra ellipsis in comment<commit_after>#include "Players/Genetic_AI.h"

#include <iostream>
#include <fstream>
#include <vector>

#include "Moves/Move.h"
#include "Game/Board.h"
#include "Game/Clock.h"

#include "Exceptions/Checkmate_Exception.h"
#include "Exceptions/Game_Ending_Exception.h"
#include "Exceptions/End_Of_File_Exception.h"

#include "Utility.h"

int Genetic_AI::next_id = 0;

Genetic_AI::Genetic_AI() :
    genome(),
    id(next_id++)
{
}

// Sexual reproduction
Genetic_AI::Genetic_AI(const Genetic_AI& A, const Genetic_AI& B) :
    genome(A.genome, B.genome),
    id(next_id++)
{
}

Genetic_AI::~Genetic_AI()
{
}

Genetic_AI::Genetic_AI(const std::string& file_name)
{
    std::ifstream ifs(file_name);
    if( ! ifs)
    {
        throw std::runtime_error("Could not read: " + file_name);
    }

    read_from(ifs);
}

Genetic_AI::Genetic_AI(std::istream& is)
{
    read_from(is);
}

Genetic_AI::Genetic_AI(const std::string& file_name, int id_in) : id(id_in)
{
    std::ifstream ifs(file_name);
    if( ! ifs)
    {
        throw std::runtime_error("Could not read: " + file_name);
    }

    std::string line;
    while(std::getline(ifs, line))
    {
        if( ! String::starts_with(line, "ID:"))
        {
            continue;
        }

        auto param_value = String::split(line, ":", 1);
        if(id_in == std::stoi(param_value[1]))
        {
            genome.read_from(ifs);
            return;
        }
    }

    throw std::runtime_error("Could not locate ID " + std::to_string(id_in) + " inside file " + file_name);
}

void Genetic_AI::read_from(std::istream& is)
{
    std::string line;
    id = -1;
    while(std::getline(is, line))
    {
        if(line.empty())
        {
            continue;
        }

        if(String::starts_with(line, "ID:"))
        {
            id = std::stoi(String::split(line)[1]);
            break;
        }
        else
        {
            throw std::runtime_error("Invalid Genetic_AI line: " + line);
        }
    }

    if( ! is)
    {
        if(id > -1)
        {
            throw std::runtime_error("Incomplete Genetic_AI spec in file for ID " + std::to_string(id));
        }
        else
        {
            throw End_Of_File_Exception();
        }
    }

    if(id >= next_id)
    {
        next_id = id + 1;
    }

    genome.read_from(is);
}

const Complete_Move Genetic_AI::choose_move(const Board& board, const Clock& clock) const
{
    const auto& legal_moves = board.all_legal_moves();
    if(legal_moves.size() == 1)
    {
        return legal_moves.front(); // If there's only one legal move, take it.
    }

    auto positions_to_examine = genome.positions_to_examine(board, clock);
    auto result = search_game_tree(board,
                                   positions_to_examine,
                                   clock,
                                   0);
    if(result.depth > 0)
    {
        board.add_commentary_to_next_move(result.commentary);
    }
    return result.move;
}

Game_Tree_Node_Result Genetic_AI::search_game_tree(const Board& board,
                                                   int& positions_to_examine,
                                                   const Clock& clock,
                                                   const int depth) const
{
    // Every call to search_game_tree() after the first
    // costs a position_to_examine.
    if(depth > 0 && positions_to_examine > 0)
    {
        --positions_to_examine;
    }

    auto perspective = board.whose_turn();

    std::vector<Game_Tree_Node_Result> results;

    // Moves worth examining further
    std::vector<std::tuple<Complete_Move, Board, double>> further_examine; // move, resulting board, score

    // Find moves worth examining
    for(const auto& move : board.all_legal_moves())
    {
        if(clock.time_left(clock.running_for()) < 0.0)
        {
            return {board.all_legal_moves().front(),
                    0.0,
                    perspective,
                    depth,
                    "Out of time"};
        }

        auto next_board = board;

        try
        {
            next_board.submit_move(move);

            if(next_board.all_legal_moves().size() > 1 &&
               (positions_to_examine <= 0
               || ! genome.good_enough_to_examine(board, next_board, perspective)))
            {
                // Record immediate result without looking ahead further
                results.push_back({move,
                                   genome.evaluate(next_board, perspective),
                                   perspective,
                                   depth,
                                   next_board.get_game_record().back()});
            }
            else
            {
                further_examine.push_back({move, next_board, genome.evaluate(next_board, perspective)});
            }
        }
        catch(const Checkmate_Exception&)
        {
            // Mate in one (try to pick the shortest path to checkmate)
            auto score = genome.evaluate(next_board, perspective);
            auto comment = next_board.get_game_record().back();

            return {move,
                    score,
                    perspective,
                    depth,
                    comment};
        }
        catch(const Game_Ending_Exception&)
        {
            // Draw
            results.push_back({move,
                               genome.evaluate(next_board, perspective),
                               perspective,
                               depth,
                               next_board.get_game_record().back()});
        }
    }

    // Sort moves by score so higher scores get more moves for examination
    std::sort(further_examine.begin(),
              further_examine.end(),
              [](const auto& x, const auto& y)
              {
                  return std::get<double>(x) < std::get<double>(y);
              });

    // Look ahead through moves found above
    for(size_t i = 0; i < further_examine.size(); ++i)
    {
        int moves_left = further_examine.size() - i;
        int positions_for_this_move = positions_to_examine/moves_left;

        if(positions_for_this_move > 0 || std::get<Board>(further_examine[i]).all_legal_moves().size() == 1)
        {
            positions_to_examine -= positions_for_this_move;

            results.push_back(search_game_tree(std::get<Board>(further_examine[i]),
                                               positions_for_this_move,
                                               clock,
                                               depth + 1));
            // Update last result with this game tree node's data
            results.back().move = std::get<Complete_Move>(further_examine[i]);
            results.back().commentary = std::get<Board>(further_examine[i]).get_game_record().back()
                                        + " "
                                        + results.back().commentary;

            positions_to_examine += positions_for_this_move;
        }
        else
        {
            results.push_back({std::get<Complete_Move>(further_examine[i]),
                               std::get<double>(further_examine[i]),
                               perspective,
                               depth,
                               std::get<Board>(further_examine[i]).get_game_record().back()});
        }
    }

    // Consider all results and return best
    auto best_score = -Math::infinity;
    auto best_move = board.all_legal_moves().front();
    auto best_depth = 0;
    std::string comments_on_best_move;

    for(const auto& result : results)
    {
        auto score = result.score;
        if(result.perspective != perspective)
        {
            score = -score;
        }

        // Prefer ...
        if(score > best_score // better score
           || (score ==  Math::infinity && result.depth < best_depth) // shortest path to victory
           || (best_score == -Math::infinity && result.depth > best_depth)) // longest path to defeat
        {
            best_score = score;
            best_move = result.move;
            best_depth = result.depth;
            comments_on_best_move = result.commentary;
        }
    }

    return {best_move,
            best_score,
            perspective,
            best_depth,
            comments_on_best_move};
}

void Genetic_AI::mutate()
{
    genome.mutate();
}

void Genetic_AI::print_genome(const std::string& file_name) const
{
    if(file_name.empty())
    {
        print_genome(std::cout);
    }
    else
    {
        auto dest = std::ofstream(file_name, std::ofstream::app);
        print_genome(dest);
    }
}

void Genetic_AI::print_genome(std::ostream& os) const
{
    os << "ID: " << get_id() << std::endl;
    genome.print(os);
    os << "END" << std::endl << std::endl;
}

std::string Genetic_AI::name() const
{
    return "Genetic AI " + std::to_string(get_id());
}

int Genetic_AI::get_id() const
{
    return id;
}

bool Genetic_AI::operator<(const Genetic_AI& other) const
{
    return get_id() < other.get_id();
}

bool Genetic_AI::operator==(const Genetic_AI& other) const
{
    return get_id() == other.get_id();
}
<|endoftext|>


<commit_before>#include "Players/Minimax_AI.h"

#include <iostream>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Game/Game_Result.h"
#include "Moves/Move.h"

#include "Utility.h"

const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.running_time_left();
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.running_time_left();

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 3 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back(principal_variation);

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    if(result.depth() > 1)
    {
        commentary.push_back(result.variation);
    }
    else
    {
        commentary.push_back({});
    }

    if(result.depth() > 2)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.running_time_left();
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    still_on_principal_variation = (still_on_principal_variation && principal_variation.size() > depth + 1);
    auto start_offset = 0;
    if(still_on_principal_variation)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        still_on_principal_variation = (move_iter != all_legal_moves.end());
        if(still_on_principal_variation)
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
            start_offset = 1;
        }
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), start_offset);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.running_time_left();
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.running_time_left());
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.running_time_left());

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 300)
        {
            recurse = false; // prevent stack overflow
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            // Record immediate result without looking ahead further
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(result.value(perspective) > best_result.value(perspective))
        {
            best_result = result;

            if(best_result.value(perspective) > alpha.value(perspective))
            {
                alpha = best_result;
                if(alpha.value(perspective) >= beta.value(perspective))
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 0.1)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.running_time_left();
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.running_time_left() < 0)
        {
            break;
        }

        if( ! recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += evaluate_start_time - clock.running_time_left();
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth() + 1;
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth() + 1);
    }

    auto time_so_far = clock_start_time - clock.running_time_left();
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.running_time_left();
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                const Game_Result& move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - depth,
            board.game_record().end()}};
}

void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
    // choose_move() keeps track of the time it takes and the number of positions
    // it sees, so this practice move will update the positions_per_second to a
    // more reasonable value.
}

double Minimax_AI::evaluate(const Board & board, const Game_Result& move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

void Minimax_AI::calculate_centipawn_value()
{
    auto board_with_pawns = Board("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1");
    auto board_with_no_white_pawns = Board("4k3/pppppppp/8/8/8/8/8/4K3 w - - 0 1");
    value_of_centipawn = std::abs(evaluate(board_with_pawns, {}, WHITE, 0) - evaluate(board_with_no_white_pawns, {}, WHITE, 0)) / 800;
}

std::string Minimax_AI::commentary_for_move(size_t move_number) const
{
    std::string result;
    if(move_number < commentary.size() && !commentary.at(move_number).empty())
    {
        result = commentary.at(move_number).front()->coordinate_move();
        for(size_t i = 1; i < commentary.at(move_number).size(); ++i)
        {
            result += " " + commentary.at(move_number).at(i)->coordinate_move();
        }
    }

    return result;
}
<commit_msg>Delete redundant comment<commit_after>#include "Players/Minimax_AI.h"

#include <iostream>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Game/Game_Result.h"
#include "Moves/Move.h"

#include "Utility.h"

const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.running_time_left();
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.running_time_left();

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 3 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back(principal_variation);

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    if(result.depth() > 1)
    {
        commentary.push_back(result.variation);
    }
    else
    {
        commentary.push_back({});
    }

    if(result.depth() > 2)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.running_time_left();
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    still_on_principal_variation = (still_on_principal_variation && principal_variation.size() > depth + 1);
    auto start_offset = 0;
    if(still_on_principal_variation)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        still_on_principal_variation = (move_iter != all_legal_moves.end());
        if(still_on_principal_variation)
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
            start_offset = 1;
        }
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), start_offset);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.running_time_left();
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.running_time_left());
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.running_time_left());

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 300)
        {
            recurse = false; // prevent stack overflow
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(result.value(perspective) > best_result.value(perspective))
        {
            best_result = result;

            if(best_result.value(perspective) > alpha.value(perspective))
            {
                alpha = best_result;
                if(alpha.value(perspective) >= beta.value(perspective))
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 0.1)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.running_time_left();
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.running_time_left() < 0)
        {
            break;
        }

        if( ! recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += evaluate_start_time - clock.running_time_left();
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth() + 1;
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth() + 1);
    }

    auto time_so_far = clock_start_time - clock.running_time_left();
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.running_time_left();
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                const Game_Result& move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - depth,
            board.game_record().end()}};
}

void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
    // choose_move() keeps track of the time it takes and the number of positions
    // it sees, so this practice move will update the positions_per_second to a
    // more reasonable value.
}

double Minimax_AI::evaluate(const Board & board, const Game_Result& move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

void Minimax_AI::calculate_centipawn_value()
{
    auto board_with_pawns = Board("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1");
    auto board_with_no_white_pawns = Board("4k3/pppppppp/8/8/8/8/8/4K3 w - - 0 1");
    value_of_centipawn = std::abs(evaluate(board_with_pawns, {}, WHITE, 0) - evaluate(board_with_no_white_pawns, {}, WHITE, 0)) / 800;
}

std::string Minimax_AI::commentary_for_move(size_t move_number) const
{
    std::string result;
    if(move_number < commentary.size() && !commentary.at(move_number).empty())
    {
        result = commentary.at(move_number).front()->coordinate_move();
        for(size_t i = 1; i < commentary.at(move_number).size(); ++i)
        {
            result += " " + commentary.at(move_number).at(i)->coordinate_move();
        }
    }

    return result;
}
<|endoftext|>