<commit_before>/*******************************************************
 * Copyright (c) 2015-2019, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <common.hpp>
#include <err_opengl.hpp>
#include <surface_impl.hpp>
#include <shader_headers/marker_fs.hpp>
#include <shader_headers/plot3_vs.hpp>
#include <shader_headers/plot3_fs.hpp>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <cmath>

using namespace gl;
using namespace std;

void generateGridIndices(unsigned short rows, unsigned short cols, unsigned short *indices)
{
    unsigned short idx = 0;

    for(unsigned short r = 0; r < rows-1; ++r) {
        for(unsigned short c = 0; c < cols*2; ++c) {
            unsigned short i = c + (r * (cols*2));

            if (c == cols * 2 - 1) {
                *indices++ = idx;
            } else {
                *indices++ = idx;
                if (i%2 == 0) {
                    idx += cols;
                } else {
                    idx -= (r%2 == 0) ? (cols-1) : (cols+1);
                }
            }
        }
    }
}

namespace forge
{
namespace opengl
{

void surface_impl::bindResources(const int pWindowId)
{
    if (mVAOMap.find(pWindowId) == mVAOMap.end()) {
        GLuint vao = 0;
        /* create a vertex array object
         * with appropriate bindings */
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        // attach plot vertices
        glEnableVertexAttribArray(mSurfPointIndex);
        glBindBuffer(GL_ARRAY_BUFFER, mVBO);
        glVertexAttribPointer(mSurfPointIndex, 3, mDataType, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(mSurfColorIndex);
        glBindBuffer(GL_ARRAY_BUFFER, mCBO);
        glVertexAttribPointer(mSurfColorIndex, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(mSurfAlphaIndex);
        glBindBuffer(GL_ARRAY_BUFFER, mABO);
        glVertexAttribPointer(mSurfAlphaIndex, 1, GL_FLOAT, GL_FALSE, 0, 0);
        //attach indices
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
        glBindVertexArray(0);
        /* store the vertex array object corresponding to
         * the window instance in the map */
        mVAOMap[pWindowId] = vao;
    }

    glBindVertexArray(mVAOMap[pWindowId]);
}

void surface_impl::unbindResources() const
{
    glBindVertexArray(0);
}

glm::mat4 surface_impl::computeTransformMat(const glm::mat4& pView, const glm::mat4& pOrient)
{
    static const glm::mat4 MODEL = glm::rotate(glm::mat4(1.0f), -glm::radians(90.f), glm::vec3(0,1,0)) *
                                   glm::rotate(glm::mat4(1.0f), -glm::radians(90.f), glm::vec3(1,0,0));

    float xRange = mRange[1] - mRange[0];
    float yRange = mRange[3] - mRange[2];
    float zRange = mRange[5] - mRange[4];
    // set scale to zero if input is constant array
    // otherwise compute scale factor by standard equation
    float xDataScale = std::abs(xRange) < 1.0e-3 ? 1.0f : 2/(xRange);
    float yDataScale = std::abs(yRange) < 1.0e-3 ? 1.0f : 2/(yRange);
    float zDataScale = std::abs(zRange) < 1.0e-3 ? 1.0f : 2/(zRange);

    float xDataOffset = (-mRange[0] * xDataScale);
    float yDataOffset = (-mRange[2] * yDataScale);
    float zDataOffset = (-mRange[4] * zDataScale);

    glm::vec3 scaleVector(xDataScale, yDataScale, zDataScale);

    glm::vec3 shiftVector = glm::vec3(-1 + xDataOffset, -1 + yDataOffset, -1 + zDataOffset);

    return pView * pOrient * MODEL * glm::scale(glm::translate(IDENTITY, shiftVector), scaleVector);
}

void surface_impl::renderGraph(const int pWindowId, const glm::mat4& transform)
{
    CheckGL("Begin surface_impl::renderGraph");

    mSurfProgram.bind();

    glUniformMatrix4fv(mSurfMatIndex, 1, GL_FALSE, glm::value_ptr(transform));
    glUniform2fv(mSurfRangeIndex, 3, mRange);
    glUniform1i(mSurfPVCIndex, mIsPVCOn);
    glUniform1i(mSurfPVAIndex, mIsPVAOn);

    bindResources(pWindowId);
    glDrawElements(GL_TRIANGLE_STRIP, mIBOSize, GL_UNSIGNED_SHORT, (void*)0 );
    unbindResources();
    mSurfProgram.unbind();

    if(mMarkerType != FG_MARKER_NONE) {
        glEnable(GL_PROGRAM_POINT_SIZE);
        mMarkerProgram.bind();

        glUniformMatrix4fv(mMarkerMatIndex, 1, GL_FALSE, glm::value_ptr(transform));
        glUniform1i(mMarkerPVCIndex, mIsPVCOn);
        glUniform1i(mMarkerPVAIndex, mIsPVAOn);
        glUniform1i(mMarkerTypeIndex, mMarkerType);
        glUniform4fv(mMarkerColIndex, 1, mColor);

        bindResources(pWindowId);
        glDrawElements(GL_POINTS, mIBOSize, GL_UNSIGNED_SHORT, (void*)0);
        unbindResources();

        mMarkerProgram.unbind();
        glDisable(GL_PROGRAM_POINT_SIZE);
    }
    CheckGL("End surface_impl::renderGraph");
}

surface_impl::surface_impl(unsigned pNumXPoints, unsigned pNumYPoints,
                           forge::dtype pDataType, forge::MarkerType pMarkerType)
    : mNumXPoints(pNumXPoints),mNumYPoints(pNumYPoints), mDataType(dtype2gl(pDataType)),
      mMarkerType(pMarkerType), mIBO(0), mIBOSize(0),
      mMarkerProgram(glsl::plot3_vs.c_str(), glsl::marker_fs.c_str()),
      mSurfProgram(glsl::plot3_vs.c_str(), glsl::plot3_fs.c_str()),
      mMarkerMatIndex(-1), mMarkerPointIndex(-1), mMarkerColorIndex(-1), mMarkerAlphaIndex(-1),
      mMarkerPVCIndex(-1), mMarkerPVAIndex(-1), mMarkerTypeIndex(-1), mMarkerColIndex(-1),
      mSurfMatIndex(-1), mSurfRangeIndex(-1), mSurfPointIndex(-1), mSurfColorIndex(-1),
      mSurfAlphaIndex(-1), mSurfPVCIndex(-1), mSurfPVAIndex(-1)
{
    CheckGL("Begin surface_impl::surface_impl");
    setColor(0.9, 0.5, 0.6, 1.0);

    mMarkerMatIndex  = mMarkerProgram.getUniformLocation("transform");
    mMarkerPVCIndex  = mMarkerProgram.getUniformLocation("isPVCOn");
    mMarkerPVAIndex  = mMarkerProgram.getUniformLocation("isPVAOn");
    mMarkerTypeIndex = mMarkerProgram.getUniformLocation("marker_type");
    mMarkerColIndex  = mMarkerProgram.getUniformLocation("marker_color");
    mMarkerPointIndex= mMarkerProgram.getAttributeLocation("point");
    mMarkerColorIndex= mMarkerProgram.getAttributeLocation("color");
    mMarkerAlphaIndex= mMarkerProgram.getAttributeLocation("alpha");

    mSurfMatIndex   = mSurfProgram.getUniformLocation("transform");
    mSurfRangeIndex = mSurfProgram.getUniformLocation("minmaxs");
    mSurfPVCIndex   = mSurfProgram.getUniformLocation("isPVCOn");
    mSurfPVAIndex   = mSurfProgram.getUniformLocation("isPVAOn");
    mSurfPointIndex = mSurfProgram.getAttributeLocation ("point");
    mSurfColorIndex = mSurfProgram.getAttributeLocation ("color");
    mSurfAlphaIndex = mSurfProgram.getAttributeLocation ("alpha");

    unsigned totalPoints = mNumXPoints * mNumYPoints;

    mVBOSize = 3*totalPoints;
    mCBOSize = 3*totalPoints;
    mABOSize = totalPoints;
#define SURF_CREATE_BUFFERS(type) \
    mVBO = createBuffer<type>(GL_ARRAY_BUFFER, mVBOSize, NULL, GL_DYNAMIC_DRAW);  \
    mCBO = createBuffer<float>(GL_ARRAY_BUFFER, mCBOSize, NULL, GL_DYNAMIC_DRAW); \
    mABO = createBuffer<float>(GL_ARRAY_BUFFER, mABOSize, NULL, GL_DYNAMIC_DRAW); \
    mVBOSize *= sizeof(type);   \
    mCBOSize *= sizeof(float);  \
    mABOSize *= sizeof(float);

    switch(mDataType) {
        case GL_FLOAT          : SURF_CREATE_BUFFERS(float) ; break;
        case GL_INT            : SURF_CREATE_BUFFERS(int)   ; break;
        case GL_UNSIGNED_INT   : SURF_CREATE_BUFFERS(uint)  ; break;
        case GL_SHORT          : SURF_CREATE_BUFFERS(short) ; break;
        case GL_UNSIGNED_SHORT : SURF_CREATE_BUFFERS(ushort); break;
        case GL_UNSIGNED_BYTE  : SURF_CREATE_BUFFERS(float) ; break;
        default                : TYPE_ERROR(1, pDataType);
    }

#undef SURF_CREATE_BUFFERS

    mIBOSize = (2 * mNumYPoints) * (mNumXPoints - 1);
    std::vector<ushort> indices(mIBOSize);
    generateGridIndices(mNumXPoints, mNumYPoints, indices.data());
    mIBO = createBuffer<ushort>(GL_ELEMENT_ARRAY_BUFFER, mIBOSize, indices.data(), GL_STATIC_DRAW);

    CheckGL("End surface_impl::surface_impl");
}

surface_impl::~surface_impl()
{
    CheckGL("Begin Plot::~Plot");
    for (auto it = mVAOMap.begin(); it!=mVAOMap.end(); ++it) {
        GLuint vao = it->second;
        glDeleteVertexArrays(1, &vao);
    }
    glDeleteBuffers(1, &mIBO);
    CheckGL("End Plot::~Plot");
}

void surface_impl::render(const int pWindowId,
                          const int pX, const int pY, const int pVPW, const int pVPH,
                          const glm::mat4& pView, const glm::mat4& pOrient)
{
    CheckGL("Begin surface_impl::render");
    // FIXME: even when per vertex alpha is enabled
    // primitives of transparent object should be sorted
    // from the furthest to closest primitive
    if (mIsPVAOn) {
        glDepthMask(GL_FALSE);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }

    renderGraph(pWindowId, computeTransformMat(pView, pOrient));

    if (mIsPVAOn) {
        glDisable(GL_BLEND);
        glDepthMask(GL_TRUE);
    }
    CheckGL("End surface_impl::render");
}

void scatter3_impl::renderGraph(const int pWindowId, const glm::mat4& transform)
{
    if(mMarkerType != FG_MARKER_NONE) {
        glEnable(GL_PROGRAM_POINT_SIZE);
        mMarkerProgram.bind();

        glUniformMatrix4fv(mMarkerMatIndex, 1, GL_FALSE, glm::value_ptr(transform));
        glUniform1i(mMarkerPVCIndex, mIsPVCOn);
        glUniform1i(mMarkerPVAIndex, mIsPVAOn);
        glUniform1i(mMarkerTypeIndex, mMarkerType);
        glUniform4fv(mMarkerColIndex, 1, mColor);

        bindResources(pWindowId);
        glDrawElements(GL_POINTS, mIBOSize, GL_UNSIGNED_SHORT, (void*)0);
        unbindResources();

        mMarkerProgram.unbind();
        glDisable(GL_PROGRAM_POINT_SIZE);
    }
}

}
}
<commit_msg>fix index buffer for surface's triangular strip<commit_after>/*******************************************************
 * Copyright (c) 2015-2019, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <common.hpp>
#include <err_opengl.hpp>
#include <surface_impl.hpp>
#include <shader_headers/marker_fs.hpp>
#include <shader_headers/plot3_vs.hpp>
#include <shader_headers/plot3_fs.hpp>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <cmath>

using namespace gl;
using namespace std;

void generateGridIndices(unsigned short rows, unsigned short cols,
                         std::vector<unsigned short>& indices)
{
    for (unsigned short r = 0; r < (rows-1); ++r) {
        if (r > 0) {
            // repeat first vertex for degenerate triangle
            indices.push_back(r*rows);
        }

        for (unsigned short c = 0; c < cols; ++c) {
            // One part of the strip
            indices.push_back(r*rows + c);
            indices.push_back((r+1)*rows + c);
        }

        if (r < (rows-2)) {
            // repeat last vertex for degenerate triangle
            indices.push_back(((r + 1) * rows) + (cols - 1));
        }
    }
}

namespace forge
{
namespace opengl
{

void surface_impl::bindResources(const int pWindowId)
{
    if (mVAOMap.find(pWindowId) == mVAOMap.end()) {
        GLuint vao = 0;
        /* create a vertex array object
         * with appropriate bindings */
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        // attach plot vertices
        glEnableVertexAttribArray(mSurfPointIndex);
        glBindBuffer(GL_ARRAY_BUFFER, mVBO);
        glVertexAttribPointer(mSurfPointIndex, 3, mDataType, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(mSurfColorIndex);
        glBindBuffer(GL_ARRAY_BUFFER, mCBO);
        glVertexAttribPointer(mSurfColorIndex, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(mSurfAlphaIndex);
        glBindBuffer(GL_ARRAY_BUFFER, mABO);
        glVertexAttribPointer(mSurfAlphaIndex, 1, GL_FLOAT, GL_FALSE, 0, 0);
        //attach indices
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
        glBindVertexArray(0);
        /* store the vertex array object corresponding to
         * the window instance in the map */
        mVAOMap[pWindowId] = vao;
    }

    glBindVertexArray(mVAOMap[pWindowId]);
}

void surface_impl::unbindResources() const
{
    glBindVertexArray(0);
}

glm::mat4 surface_impl::computeTransformMat(const glm::mat4& pView, const glm::mat4& pOrient)
{
    static const glm::mat4 MODEL = glm::rotate(glm::mat4(1.0f), -glm::radians(90.f), glm::vec3(0,1,0)) *
                                   glm::rotate(glm::mat4(1.0f), -glm::radians(90.f), glm::vec3(1,0,0));

    float xRange = mRange[1] - mRange[0];
    float yRange = mRange[3] - mRange[2];
    float zRange = mRange[5] - mRange[4];
    // set scale to zero if input is constant array
    // otherwise compute scale factor by standard equation
    float xDataScale = std::abs(xRange) < 1.0e-3 ? 1.0f : 2/(xRange);
    float yDataScale = std::abs(yRange) < 1.0e-3 ? 1.0f : 2/(yRange);
    float zDataScale = std::abs(zRange) < 1.0e-3 ? 1.0f : 2/(zRange);

    float xDataOffset = (-mRange[0] * xDataScale);
    float yDataOffset = (-mRange[2] * yDataScale);
    float zDataOffset = (-mRange[4] * zDataScale);

    glm::vec3 scaleVector(xDataScale, yDataScale, zDataScale);

    glm::vec3 shiftVector = glm::vec3(-1 + xDataOffset, -1 + yDataOffset, -1 + zDataOffset);

    return pView * pOrient * MODEL * glm::scale(glm::translate(IDENTITY, shiftVector), scaleVector);
}

void surface_impl::renderGraph(const int pWindowId, const glm::mat4& transform)
{
    CheckGL("Begin surface_impl::renderGraph");

    mSurfProgram.bind();

    glUniformMatrix4fv(mSurfMatIndex, 1, GL_FALSE, glm::value_ptr(transform));
    glUniform2fv(mSurfRangeIndex, 3, mRange);
    glUniform1i(mSurfPVCIndex, mIsPVCOn);
    glUniform1i(mSurfPVAIndex, mIsPVAOn);

    bindResources(pWindowId);
    glDrawElements(GL_TRIANGLE_STRIP, mIBOSize, GL_UNSIGNED_SHORT, (void*)0 );
    unbindResources();
    mSurfProgram.unbind();

    if(mMarkerType != FG_MARKER_NONE) {
        glEnable(GL_PROGRAM_POINT_SIZE);
        mMarkerProgram.bind();

        glUniformMatrix4fv(mMarkerMatIndex, 1, GL_FALSE, glm::value_ptr(transform));
        glUniform1i(mMarkerPVCIndex, mIsPVCOn);
        glUniform1i(mMarkerPVAIndex, mIsPVAOn);
        glUniform1i(mMarkerTypeIndex, mMarkerType);
        glUniform4fv(mMarkerColIndex, 1, mColor);

        bindResources(pWindowId);
        glDrawElements(GL_POINTS, mIBOSize, GL_UNSIGNED_SHORT, (void*)0);
        unbindResources();

        mMarkerProgram.unbind();
        glDisable(GL_PROGRAM_POINT_SIZE);
    }
    CheckGL("End surface_impl::renderGraph");
}

surface_impl::surface_impl(unsigned pNumXPoints, unsigned pNumYPoints,
                           forge::dtype pDataType, forge::MarkerType pMarkerType)
    : mNumXPoints(pNumXPoints),mNumYPoints(pNumYPoints), mDataType(dtype2gl(pDataType)),
      mMarkerType(pMarkerType), mIBO(0), mIBOSize(0),
      mMarkerProgram(glsl::plot3_vs.c_str(), glsl::marker_fs.c_str()),
      mSurfProgram(glsl::plot3_vs.c_str(), glsl::plot3_fs.c_str()),
      mMarkerMatIndex(-1), mMarkerPointIndex(-1), mMarkerColorIndex(-1), mMarkerAlphaIndex(-1),
      mMarkerPVCIndex(-1), mMarkerPVAIndex(-1), mMarkerTypeIndex(-1), mMarkerColIndex(-1),
      mSurfMatIndex(-1), mSurfRangeIndex(-1), mSurfPointIndex(-1), mSurfColorIndex(-1),
      mSurfAlphaIndex(-1), mSurfPVCIndex(-1), mSurfPVAIndex(-1)
{
    CheckGL("Begin surface_impl::surface_impl");
    setColor(0.9, 0.5, 0.6, 1.0);

    mMarkerMatIndex  = mMarkerProgram.getUniformLocation("transform");
    mMarkerPVCIndex  = mMarkerProgram.getUniformLocation("isPVCOn");
    mMarkerPVAIndex  = mMarkerProgram.getUniformLocation("isPVAOn");
    mMarkerTypeIndex = mMarkerProgram.getUniformLocation("marker_type");
    mMarkerColIndex  = mMarkerProgram.getUniformLocation("marker_color");
    mMarkerPointIndex= mMarkerProgram.getAttributeLocation("point");
    mMarkerColorIndex= mMarkerProgram.getAttributeLocation("color");
    mMarkerAlphaIndex= mMarkerProgram.getAttributeLocation("alpha");

    mSurfMatIndex   = mSurfProgram.getUniformLocation("transform");
    mSurfRangeIndex = mSurfProgram.getUniformLocation("minmaxs");
    mSurfPVCIndex   = mSurfProgram.getUniformLocation("isPVCOn");
    mSurfPVAIndex   = mSurfProgram.getUniformLocation("isPVAOn");
    mSurfPointIndex = mSurfProgram.getAttributeLocation ("point");
    mSurfColorIndex = mSurfProgram.getAttributeLocation ("color");
    mSurfAlphaIndex = mSurfProgram.getAttributeLocation ("alpha");

    unsigned totalPoints = mNumXPoints * mNumYPoints;

    mVBOSize = 3*totalPoints;
    mCBOSize = 3*totalPoints;
    mABOSize = totalPoints;
#define SURF_CREATE_BUFFERS(type) \
    mVBO = createBuffer<type>(GL_ARRAY_BUFFER, mVBOSize, NULL, GL_DYNAMIC_DRAW);  \
    mCBO = createBuffer<float>(GL_ARRAY_BUFFER, mCBOSize, NULL, GL_DYNAMIC_DRAW); \
    mABO = createBuffer<float>(GL_ARRAY_BUFFER, mABOSize, NULL, GL_DYNAMIC_DRAW); \
    mVBOSize *= sizeof(type);   \
    mCBOSize *= sizeof(float);  \
    mABOSize *= sizeof(float);

    switch(mDataType) {
        case GL_FLOAT          : SURF_CREATE_BUFFERS(float) ; break;
        case GL_INT            : SURF_CREATE_BUFFERS(int)   ; break;
        case GL_UNSIGNED_INT   : SURF_CREATE_BUFFERS(uint)  ; break;
        case GL_SHORT          : SURF_CREATE_BUFFERS(short) ; break;
        case GL_UNSIGNED_SHORT : SURF_CREATE_BUFFERS(ushort); break;
        case GL_UNSIGNED_BYTE  : SURF_CREATE_BUFFERS(float) ; break;
        default                : TYPE_ERROR(1, pDataType);
    }

#undef SURF_CREATE_BUFFERS

    std::vector<ushort> indices;

    generateGridIndices(mNumXPoints, mNumYPoints, indices);

    mIBOSize = indices.size();

    mIBO = createBuffer<ushort>(GL_ELEMENT_ARRAY_BUFFER, mIBOSize, indices.data(), GL_STATIC_DRAW);

    CheckGL("End surface_impl::surface_impl");
}

surface_impl::~surface_impl()
{
    CheckGL("Begin Plot::~Plot");
    for (auto it = mVAOMap.begin(); it!=mVAOMap.end(); ++it) {
        GLuint vao = it->second;
        glDeleteVertexArrays(1, &vao);
    }
    glDeleteBuffers(1, &mIBO);
    CheckGL("End Plot::~Plot");
}

void surface_impl::render(const int pWindowId,
                          const int pX, const int pY, const int pVPW, const int pVPH,
                          const glm::mat4& pView, const glm::mat4& pOrient)
{
    CheckGL("Begin surface_impl::render");
    // FIXME: even when per vertex alpha is enabled
    // primitives of transparent object should be sorted
    // from the furthest to closest primitive
    if (mIsPVAOn) {
        glDepthMask(GL_FALSE);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }

    renderGraph(pWindowId, computeTransformMat(pView, pOrient));

    if (mIsPVAOn) {
        glDisable(GL_BLEND);
        glDepthMask(GL_TRUE);
    }
    CheckGL("End surface_impl::render");
}

void scatter3_impl::renderGraph(const int pWindowId, const glm::mat4& transform)
{
    if(mMarkerType != FG_MARKER_NONE) {
        glEnable(GL_PROGRAM_POINT_SIZE);
        mMarkerProgram.bind();

        glUniformMatrix4fv(mMarkerMatIndex, 1, GL_FALSE, glm::value_ptr(transform));
        glUniform1i(mMarkerPVCIndex, mIsPVCOn);
        glUniform1i(mMarkerPVAIndex, mIsPVAOn);
        glUniform1i(mMarkerTypeIndex, mMarkerType);
        glUniform4fv(mMarkerColIndex, 1, mColor);

        bindResources(pWindowId);
        glDrawElements(GL_POINTS, mIBOSize, GL_UNSIGNED_SHORT, (void*)0);
        unbindResources();

        mMarkerProgram.unbind();
        glDisable(GL_PROGRAM_POINT_SIZE);
    }
}

}
}
<|endoftext|>


<commit_before>// BaseDisplay.cc for Fluxbox Window manager
// Copyright (c) 2001 - 2003 Henrik Kinnunen (fluxgen(at)linuxmail.org)
//
// BaseDisplay.cc for Blackbox - an X11 Window manager
// Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
//
// 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.

// $Id: BaseDisplay.cc,v 1.27 2003/04/28 00:39:34 fluxgen Exp $

#include "BaseDisplay.hh"
#include "i18n.hh"
#include "Timer.hh"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

// use GNU extensions
#ifndef	 _GNU_SOURCE
#define	 _GNU_SOURCE
#endif // _GNU_SOURCE

#include <X11/Xutil.h>

#ifdef	SHAPE
#include <X11/extensions/shape.h>
#endif // SHAPE

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H

#include <cstdio>
#include <cstdlib>
#include <cstring>

#ifdef	HAVE_UNISTD_H
#include <sys/types.h>
#include <unistd.h>
#endif // HAVE_UNISTD_H

#ifdef	HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif // HAVE_SYS_SELECT_H

#ifdef	HAVE_SIGNAL_H
#include <signal.h>
#endif // HAVE_SIGNAL_H

#ifndef	 SA_NODEFER
#ifdef	 SA_INTERRUPT
#define SA_NODEFER SA_INTERRUPT
#else // !SA_INTERRUPT
#define SA_NODEFER (0)
#endif // SA_INTERRUPT
#endif // SA_NODEFER

#ifdef HAVE_SYS_WAIT_H
#include <sys/types.h>
#include <sys/wait.h>
#endif // HAVE_SYS_WAIT_H

#if defined(HAVE_PROCESS_H) && defined(__EMX__)
#include <process.h>
#endif // HAVE_PROCESS_H  && __EMX__

#include <iostream>
using namespace std;

// X error handler to handle any and all X errors while the application is
// running
static Bool internal_error = False;
static Window last_bad_window = None;

static int handleXErrors(Display *d, XErrorEvent *e) {
#ifdef DEBUG
    char errtxt[128];

    XGetErrorText(d, e->error_code, errtxt, 128);
    fprintf(stderr,
            I18n::instance()->
            getMessage(
                       FBNLS::BaseDisplaySet, FBNLS::BaseDisplayXError,
                       "%s:	X error: %s(%d) opcodes %d/%d\n	resource 0x%lx\n"),
            BaseDisplay::instance()->getApplicationName(), errtxt, e->error_code,
            e->request_code, e->minor_code, e->resourceid);

#endif // !DEBUG

    if (e->error_code == BadWindow)
        last_bad_window = e->resourceid;

    if (internal_error)
        abort();

    return(False);
}

BaseDisplay *BaseDisplay::s_singleton = 0;

BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name):FbTk::App(dpy_name),
                                                                     m_startup(true), m_shutdown(false),
                                                                     m_display_name(XDisplayName(dpy_name)), m_app_name(app_name),
                                                                     m_server_grabs(0)
{
    if (s_singleton != 0)
        throw string("Can't create more than one instance of BaseDisplay!");

    s_singleton = this;

    last_bad_window = None;
    I18n *i18n = I18n::instance();

    if (display() == 0) {
        throw string(
                     i18n->
                     getMessage(
                                FBNLS::BaseDisplaySet, FBNLS::BaseDisplayXConnectFail,
                                "BaseDisplay::BaseDisplay: connection to X server failed."));

    } else if (fcntl(ConnectionNumber(display()), F_SETFD, 1) == -1) {
        throw string(
                     i18n->
                     getMessage(
                                FBNLS::BaseDisplaySet, FBNLS::BaseDisplayCloseOnExecFail,
                                "BaseDisplay::BaseDisplay: couldn't mark display connection "
                                "as close-on-exec"));

    }

    number_of_screens = ScreenCount(display());

#ifdef		SHAPE
    shape.extensions = XShapeQueryExtension(display(), &shape.event_basep,
                                            &shape.error_basep);
#else // !SHAPE
    shape.extensions = False;
#endif // SHAPE

    XSetErrorHandler((XErrorHandler) handleXErrors);

    for (int i = 0; i < number_of_screens; i++) {
        ScreenInfo *screeninfo = new ScreenInfo(i);
        screenInfoList.push_back(screeninfo);
    }
}

BaseDisplay::~BaseDisplay() {

    ScreenInfoList::iterator it = screenInfoList.begin();
    ScreenInfoList::iterator it_end = screenInfoList.end();
    for (; it != it_end; ++it) {
        delete (*it);
    }

    s_singleton = 0;
}

BaseDisplay *BaseDisplay::instance() {
    if (s_singleton == 0)
        throw string("BaseDisplay not created!");

    return s_singleton;
}

void BaseDisplay::eventLoop() {
    run();

    while ((! m_shutdown) && (! internal_error)) {
        if (XPending(display())) {
            XEvent e;
            XNextEvent(display(), &e);

            if (last_bad_window != None && e.xany.window == last_bad_window) {
#ifdef DEBUG
                fprintf(stderr,
                        I18n::instance()->
                        getMessage(
                                   FBNLS::BaseDisplaySet, FBNLS::BaseDisplayBadWindowRemove,
                                   "BaseDisplay::eventLoop(): removing bad window "
                                   "from event queue\n"));
#endif // DEBUG
            } else {
                last_bad_window = None;
                handleEvent(&e);
            }
        } else {
            FbTk::Timer::updateTimers(ConnectionNumber(display())); //handle all timers
        }
    }
}

bool BaseDisplay::validateWindow(Window window) {
    XEvent event;
    if (XCheckTypedWindowEvent(display(), window, DestroyNotify, &event)) {
        XPutBackEvent(display(), &event);
        return false;
    }

    return true;
}

void BaseDisplay::grab() {
    if (! m_server_grabs++)
        XGrabServer(display());
}

void BaseDisplay::ungrab() {
    if (! --m_server_grabs)
        XUngrabServer(display());
    if (m_server_grabs < 0)
        m_server_grabs = 0;
}

ScreenInfo::ScreenInfo(int num) {
    basedisplay = BaseDisplay::instance();
    Display * const disp = basedisplay->getXDisplay();
    screen_number = num;

    root_window = RootWindow(disp, screen_number);
    depth = DefaultDepth(disp, screen_number);

    width =
        WidthOfScreen(ScreenOfDisplay(disp, screen_number));
    height =
        HeightOfScreen(ScreenOfDisplay(disp, screen_number));

    // search for a TrueColor Visual... if we can't find one... we will use the
    // default visual for the screen
    XVisualInfo vinfo_template, *vinfo_return;
    int vinfo_nitems;

    vinfo_template.screen = screen_number;
    vinfo_template.c_class = TrueColor;

    visual = (Visual *) 0;

    if ((vinfo_return = XGetVisualInfo(disp,
                                       VisualScreenMask | VisualClassMask,
                                       &vinfo_template, &vinfo_nitems)) &&
        vinfo_nitems > 0) {

        for (int i = 0; i < vinfo_nitems; i++) {
            if (depth < (vinfo_return + i)->depth) {
                depth = (vinfo_return + i)->depth;
                visual = (vinfo_return + i)->visual;
            }
        }

        XFree(vinfo_return);
    }

    if (visual) {
        m_colormap = XCreateColormap(disp, root_window,
                                     visual, AllocNone);
    } else {
        visual = DefaultVisual(disp, screen_number);
        m_colormap = DefaultColormap(disp, screen_number);
    }

#ifdef XINERAMA
    // check if we have Xinerama extension enabled
    if (XineramaIsActive(disp)) {
        m_hasXinerama = true;
        xineramaLastHead = 0;
        xineramaInfos =
            XineramaQueryScreens(disp, &xineramaNumHeads);
    } else {
        m_hasXinerama = false;
        xineramaInfos = 0; // make sure we don't point anywhere we shouldn't
    }
#endif // XINERAMA
}

ScreenInfo::~ScreenInfo() {
#ifdef XINERAMA
    if (m_hasXinerama) { // only free if we first had it
        XFree(xineramaInfos);
        xineramaInfos = 0;
    }
#endif // XINERAMA
}

#ifdef XINERAMA

/**
   Searches for the head at the coordinates
   x,y. If it fails or Xinerama isn't
   activated it'll return head nr 0
*/
unsigned int ScreenInfo::getHead(int x, int y) const {

    // is Xinerama extensions enabled?
    if (hasXinerama()) {
        // check if last head is still active
        /*		if ((xineramaInfos[xineramaLastHead].x_org <= x) &&
                        ((xineramaInfos[xineramaLastHead].x_org +
                        xineramaInfos[xineramaLastHead].width) > x) &&
                        (xineramaInfos[xineramaLastHead].y_org <= y) &&
                        ((xineramaInfos[xineramaLastHead].y_org +
                        xineramaInfos[xineramaLastHead].height) > y)) {
                        return xineramaLastHead;
                        } else { */
        // go trough all the heads, and search
        for (int i = 0; (signed) i < xineramaNumHeads; i++) {
            if (xineramaInfos[i].x_org <= x &&
                (xineramaInfos[i].x_org + xineramaInfos[i].width) > x &&
                xineramaInfos[i].y_org <= y &&
                (xineramaInfos[i].y_org + xineramaInfos[i].height) > y)
                // return (xineramaLastHead = i);
                return i;
        }
        //	}
    }

    return 0;
}

/**
   Searches for the head that the pointer
   currently is on, if it isn't found
   the first one is returned
*/
unsigned int ScreenInfo::getCurrHead(void) const {

    // is Xinerama extensions enabled?
    if (hasXinerama()) {
        int x, y, wX, wY;
        unsigned int mask;
        Window rRoot, rChild;
        // get pointer cordinates
        if ( (XQueryPointer(basedisplay->getXDisplay(), root_window,
                            &rRoot, &rChild, &x, &y, &wX, &wY, &mask)) != 0 )  {
            return getHead(x, y);
        }
    }

    return 0;
}

//----------- getHeadWidth ------------
// Returns the width of head
//-------------------------------------
unsigned int ScreenInfo::getHeadWidth(unsigned int head) const {

    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[xineramaNumHeads - 1].width;
        } else
            return xineramaInfos[head].width;
    }

    return getWidth();

}

//----------- getHeadHeight ------------
// Returns the heigt of head
//--------------------------------------
unsigned int ScreenInfo::getHeadHeight(unsigned int head) const {

    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[xineramaNumHeads - 1].height;
        } else
            return xineramaInfos[head].height;
    }

    return getHeight();
}

//----------- getHeadX -----------------
// Returns the X start of head nr head
//--------------------------------------
int ScreenInfo::getHeadX(unsigned int head) const {
    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[head = xineramaNumHeads - 1].x_org;
        } else
            return xineramaInfos[head].x_org;
    }

    return 0;
}

//----------- getHeadY -----------------
// Returns the Y start of head
//--------------------------------------
int ScreenInfo::getHeadY(unsigned int head) const {
    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[xineramaNumHeads - 1].y_org;
        } else
            return xineramaInfos[head].y_org;
    }

    return 0;
}

#endif // XINERAMA
<commit_msg>better error msg<commit_after>// BaseDisplay.cc for Fluxbox Window manager
// Copyright (c) 2001 - 2003 Henrik Kinnunen (fluxgen(at)linuxmail.org)
//
// BaseDisplay.cc for Blackbox - an X11 Window manager
// Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
//
// 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.

// $Id: BaseDisplay.cc,v 1.28 2003/05/07 11:43:03 fluxgen Exp $

#include "BaseDisplay.hh"
#include "i18n.hh"
#include "Timer.hh"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

// use GNU extensions
#ifndef	 _GNU_SOURCE
#define	 _GNU_SOURCE
#endif // _GNU_SOURCE

#include <X11/Xutil.h>

#ifdef	SHAPE
#include <X11/extensions/shape.h>
#endif // SHAPE

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H

#include <cstdio>
#include <cstdlib>
#include <cstring>

#ifdef	HAVE_UNISTD_H
#include <sys/types.h>
#include <unistd.h>
#endif // HAVE_UNISTD_H

#ifdef	HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif // HAVE_SYS_SELECT_H

#ifdef	HAVE_SIGNAL_H
#include <signal.h>
#endif // HAVE_SIGNAL_H

#ifndef	 SA_NODEFER
#ifdef	 SA_INTERRUPT
#define SA_NODEFER SA_INTERRUPT
#else // !SA_INTERRUPT
#define SA_NODEFER (0)
#endif // SA_INTERRUPT
#endif // SA_NODEFER

#ifdef HAVE_SYS_WAIT_H
#include <sys/types.h>
#include <sys/wait.h>
#endif // HAVE_SYS_WAIT_H

#if defined(HAVE_PROCESS_H) && defined(__EMX__)
#include <process.h>
#endif // HAVE_PROCESS_H  && __EMX__

#include <iostream>
using namespace std;

// X error handler to handle any and all X errors while the application is
// running
static Bool internal_error = False;
static Window last_bad_window = None;

static int handleXErrors(Display *d, XErrorEvent *e) {
#ifdef DEBUG
    char errtxt[128];

    XGetErrorText(d, e->error_code, errtxt, 128);
    fprintf(stderr,
            I18n::instance()->
            getMessage(
                       FBNLS::BaseDisplaySet, FBNLS::BaseDisplayXError,
                       "%s:	X error: %s(%d) opcodes %d/%d\n	resource 0x%lx\n"),
            BaseDisplay::instance()->getApplicationName(), errtxt, e->error_code,
            e->request_code, e->minor_code, e->resourceid);

#endif // !DEBUG

    if (e->error_code == BadWindow)
        last_bad_window = e->resourceid;

    if (internal_error)
        abort();

    return(False);
}

BaseDisplay *BaseDisplay::s_singleton = 0;

BaseDisplay::BaseDisplay(const char *app_name, const char *dpy_name):FbTk::App(dpy_name),
                                                                     m_startup(true), m_shutdown(false),
                                                                     m_display_name(XDisplayName(dpy_name)), m_app_name(app_name),
                                                                     m_server_grabs(0)
{
    if (s_singleton != 0)
        throw string("Can't create more than one instance of BaseDisplay!");

    s_singleton = this;

    last_bad_window = None;
    I18n *i18n = I18n::instance();

    if (display() == 0) {
        throw string("Can not connect to X server."
                     "Make sure you started X before you start Fluxbox and"
                     "that you do not have any other window manager running at the same time.");
        /*
          i18n->
          getMessage(
          FBNLS::BaseDisplaySet, FBNLS::BaseDisplayXConnectFail,
          "BaseDisplay::BaseDisplay: connection to X server failed."));
        */
    } else if (fcntl(ConnectionNumber(display()), F_SETFD, 1) == -1) {
        throw string(
                     i18n->
                     getMessage(
                                FBNLS::BaseDisplaySet, FBNLS::BaseDisplayCloseOnExecFail,
                                "BaseDisplay::BaseDisplay: couldn't mark display connection "
                                "as close-on-exec"));

    }

    number_of_screens = ScreenCount(display());

#ifdef		SHAPE
    shape.extensions = XShapeQueryExtension(display(), &shape.event_basep,
                                            &shape.error_basep);
#else // !SHAPE
    shape.extensions = False;
#endif // SHAPE

    XSetErrorHandler((XErrorHandler) handleXErrors);

    for (int i = 0; i < number_of_screens; i++) {
        ScreenInfo *screeninfo = new ScreenInfo(i);
        screenInfoList.push_back(screeninfo);
    }
}

BaseDisplay::~BaseDisplay() {

    ScreenInfoList::iterator it = screenInfoList.begin();
    ScreenInfoList::iterator it_end = screenInfoList.end();
    for (; it != it_end; ++it) {
        delete (*it);
    }

    s_singleton = 0;
}

BaseDisplay *BaseDisplay::instance() {
    if (s_singleton == 0)
        throw string("BaseDisplay not created!");

    return s_singleton;
}

void BaseDisplay::eventLoop() {
    run();

    while ((! m_shutdown) && (! internal_error)) {
        if (XPending(display())) {
            XEvent e;
            XNextEvent(display(), &e);

            if (last_bad_window != None && e.xany.window == last_bad_window) {
#ifdef DEBUG
                fprintf(stderr,
                        I18n::instance()->
                        getMessage(
                                   FBNLS::BaseDisplaySet, FBNLS::BaseDisplayBadWindowRemove,
                                   "BaseDisplay::eventLoop(): removing bad window "
                                   "from event queue\n"));
#endif // DEBUG
            } else {
                last_bad_window = None;
                handleEvent(&e);
            }
        } else {
            FbTk::Timer::updateTimers(ConnectionNumber(display())); //handle all timers
        }
    }
}

bool BaseDisplay::validateWindow(Window window) {
    XEvent event;
    if (XCheckTypedWindowEvent(display(), window, DestroyNotify, &event)) {
        XPutBackEvent(display(), &event);
        return false;
    }

    return true;
}

void BaseDisplay::grab() {
    if (! m_server_grabs++)
        XGrabServer(display());
}

void BaseDisplay::ungrab() {
    if (! --m_server_grabs)
        XUngrabServer(display());
    if (m_server_grabs < 0)
        m_server_grabs = 0;
}

ScreenInfo::ScreenInfo(int num) {
    basedisplay = BaseDisplay::instance();
    Display * const disp = basedisplay->getXDisplay();
    screen_number = num;

    root_window = RootWindow(disp, screen_number);
    depth = DefaultDepth(disp, screen_number);

    width =
        WidthOfScreen(ScreenOfDisplay(disp, screen_number));
    height =
        HeightOfScreen(ScreenOfDisplay(disp, screen_number));

    // search for a TrueColor Visual... if we can't find one... we will use the
    // default visual for the screen
    XVisualInfo vinfo_template, *vinfo_return;
    int vinfo_nitems;

    vinfo_template.screen = screen_number;
    vinfo_template.c_class = TrueColor;

    visual = (Visual *) 0;

    if ((vinfo_return = XGetVisualInfo(disp,
                                       VisualScreenMask | VisualClassMask,
                                       &vinfo_template, &vinfo_nitems)) &&
        vinfo_nitems > 0) {

        for (int i = 0; i < vinfo_nitems; i++) {
            if (depth < (vinfo_return + i)->depth) {
                depth = (vinfo_return + i)->depth;
                visual = (vinfo_return + i)->visual;
            }
        }

        XFree(vinfo_return);
    }

    if (visual) {
        m_colormap = XCreateColormap(disp, root_window,
                                     visual, AllocNone);
    } else {
        visual = DefaultVisual(disp, screen_number);
        m_colormap = DefaultColormap(disp, screen_number);
    }

#ifdef XINERAMA
    // check if we have Xinerama extension enabled
    if (XineramaIsActive(disp)) {
        m_hasXinerama = true;
        xineramaLastHead = 0;
        xineramaInfos =
            XineramaQueryScreens(disp, &xineramaNumHeads);
    } else {
        m_hasXinerama = false;
        xineramaInfos = 0; // make sure we don't point anywhere we shouldn't
    }
#endif // XINERAMA
}

ScreenInfo::~ScreenInfo() {
#ifdef XINERAMA
    if (m_hasXinerama) { // only free if we first had it
        XFree(xineramaInfos);
        xineramaInfos = 0;
    }
#endif // XINERAMA
}

#ifdef XINERAMA

/**
   Searches for the head at the coordinates
   x,y. If it fails or Xinerama isn't
   activated it'll return head nr 0
*/
unsigned int ScreenInfo::getHead(int x, int y) const {

    // is Xinerama extensions enabled?
    if (hasXinerama()) {
        // check if last head is still active
        /*		if ((xineramaInfos[xineramaLastHead].x_org <= x) &&
                        ((xineramaInfos[xineramaLastHead].x_org +
                        xineramaInfos[xineramaLastHead].width) > x) &&
                        (xineramaInfos[xineramaLastHead].y_org <= y) &&
                        ((xineramaInfos[xineramaLastHead].y_org +
                        xineramaInfos[xineramaLastHead].height) > y)) {
                        return xineramaLastHead;
                        } else { */
        // go trough all the heads, and search
        for (int i = 0; (signed) i < xineramaNumHeads; i++) {
            if (xineramaInfos[i].x_org <= x &&
                (xineramaInfos[i].x_org + xineramaInfos[i].width) > x &&
                xineramaInfos[i].y_org <= y &&
                (xineramaInfos[i].y_org + xineramaInfos[i].height) > y)
                // return (xineramaLastHead = i);
                return i;
        }
        //	}
    }

    return 0;
}

/**
   Searches for the head that the pointer
   currently is on, if it isn't found
   the first one is returned
*/
unsigned int ScreenInfo::getCurrHead(void) const {

    // is Xinerama extensions enabled?
    if (hasXinerama()) {
        int x, y, wX, wY;
        unsigned int mask;
        Window rRoot, rChild;
        // get pointer cordinates
        if ( (XQueryPointer(basedisplay->getXDisplay(), root_window,
                            &rRoot, &rChild, &x, &y, &wX, &wY, &mask)) != 0 )  {
            return getHead(x, y);
        }
    }

    return 0;
}

/**
 @return the width of head
*/
unsigned int ScreenInfo::getHeadWidth(unsigned int head) const {

    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[xineramaNumHeads - 1].width;
        } else
            return xineramaInfos[head].width;
    }

    return getWidth();

}

/**
 @return the heigt of head
*/
unsigned int ScreenInfo::getHeadHeight(unsigned int head) const {

    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[xineramaNumHeads - 1].height;
        } else
            return xineramaInfos[head].height;
    }

    return getHeight();
}

/**
 @return the X start of head nr head
*/
int ScreenInfo::getHeadX(unsigned int head) const {
    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[head = xineramaNumHeads - 1].x_org;
        } else
            return xineramaInfos[head].x_org;
    }

    return 0;
}

/**
 @return the Y start of head
*/
int ScreenInfo::getHeadY(unsigned int head) const {
    if (hasXinerama()) {
        if ((signed) head >= xineramaNumHeads) {
#ifdef DEBUG
            cerr << __FILE__ << ":" <<__LINE__ << ": " <<
                "Head: " << head << " doesn't exist!" << endl;
#endif // DEBUG
            return xineramaInfos[xineramaNumHeads - 1].y_org;
        } else
            return xineramaInfos[head].y_org;
    }

    return 0;
}

#endif // XINERAMA
<|endoftext|>


<commit_before>#include <SCD/CD/CD_Pair.h>
#include <SCD/CD/CD_Simplex.h>
#include <SCD/CD/CD_SimplexEnhanced.h>

#include <iostream>

//#ifndef NOGLUT
//#define SHOW_LAST_SIMLPEX
//#endif

//#define CD_SAFE_VERSION //use when the scalar has a perfect precision
#define PENETRATION_DEPTH
//#define CD_PAIR_VERBOUS_MODE
//#define CD_ITERATION_LIMIT 50 //use when the real-time constraints are too high fo current performances while keeping the same global precision.
								//no theoretical guarantee on the precision nor the collision-safeness when used - Default value is 20

#define _EPSILON_ 1e-24
#define _PRECISION_ 1e-6

using namespace SCD;

inline Vector3 LinearSystem(Matrix3x3& A, Vector3& y)
{
	Matrix3x3 B;
	A.Inversion(B);
	return (B*y);
}

CD_Pair::CD_Pair(S_Object *obj1, S_Object *obj2):sObj1_(obj1),sObj2_(obj2),lastDirection_(1.0,0.0,0.0),
lastFeature1_(-1),lastFeature2_(-1),distance_(0),stamp1_(sObj1_->checkStamp()),stamp2_(sObj2_->checkStamp()),
precision_(_PRECISION_),epsilon_(_EPSILON_),witPointsAreComputed_(false),s1_(Point3()),s2_(Point3()),s_(Point3()),sp_(Point3()),depthPair(obj1,obj2)
{
	--stamp1_;
	--stamp2_;

	depthPair.setRelativePrecision(_PRECISION_);
	depthPair.setEpsilon(_EPSILON_);
}

CD_Pair::~CD_Pair(void)
{
}

Scalar CD_Pair::getDistance()
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		return distance_;
	}
	else
	{
		stamp1_=sObj1_->checkStamp();
		stamp2_=sObj2_->checkStamp();
		GJK();
		penetrationDepth();
		return distance_;
	}
}

Scalar CD_Pair::getDistanceWithoutPenetrationDepth()
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		if (distance_ > 0)
			return distance_;
		return 0;
	}
	else
	{
		GJK();
		if (collision_)
			return 0;
		return distance_;
	}
}

Scalar CD_Pair::reComputeClosestPoints(Point3& p1,Point3& p2)
{
	stamp1_=sObj1_->checkStamp();
	stamp2_=sObj2_->checkStamp();
	GJK();
	penetrationDepth();
	witPoints(p1,p2);
	return distance_;
}

void CD_Pair::setRelativePrecision(Scalar s)
{
	precision_=s*s;
	depthPair.setRelativePrecision(s);
}

void CD_Pair::setEpsilon(Scalar s)
{
	epsilon_=s;
	depthPair.setEpsilon(s);
}

Scalar CD_Pair::getClosestPoints(Point3 &p1, Point3 &p2)
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		if (!witPointsAreComputed_)
		{
			witPointsAreComputed_=true;
			witPoints(p1,p2);
		}
		p1=p1_;
		p2=p2_;
		return distance_;
	}
	else
	{
		stamp1_=sObj1_->checkStamp();
		stamp2_=sObj2_->checkStamp();
		GJK();
		penetrationDepth();
		witPoints(p1,p2);
		witPointsAreComputed_=true;
		return distance_;
	}
}

Scalar CD_Pair::penetrationDepth()
{
#ifdef PENETRATION_DEPTH
	if (collision_)//Objects are in collision
	{
		distance_=-depthPair.getPenetrationDepth(lastDirection_,p1_,p2_,sp_,s1_,s2_);
		if (distance_<0)
		{
			lastDirection_.Set(0,1,0);
		}

		return distance_;
	}
	else
	{
		return distance_;
	}
#else
	return distance_;
#endif
}

Scalar CD_Pair::GJK()
{
	Vector3& v=lastDirection_;

#ifdef CD_PAIR_VERBOUS_MODE
	std::cout<<"#####GJK START######"<<std::endl;
#endif

	witPointsAreComputed_=false;

	int& lf1=lastFeature1_;
	int& lf2=lastFeature2_;

	Point3 sup1=sObj1_->support(v,lf1);
	Point3 sup2=sObj2_->support(-v,lf2);

#ifdef CD_PAIR_VERBOUS_MODE
	std::cout<<"Last features"<<lf1<<" "<<lf2<<std::endl;
#endif

	Point3 sup(sup1);

	sup-=sup2;

	s1_=sup1;
	s2_=sup2;
	s_=sup;

	sp_=sup;

	CD_SimplexKeptPoints k;

	projectionComputed_=false;

	bool cont=true;

	Point3 proj;

	Vector3 S01;
	Vector3 S02;

	Scalar a1,a2,a3,a4,a5,a6;

#ifdef CD_ITERATION_LIMIT

	int	cnt=0;
#endif

	while (cont)
	{

#ifdef CD_ITERATION_LIMIT

		if (++cnt>CD_ITERATION_LIMIT)
			break;		//the iterations number limit has been reached
#endif

		switch (s_.getType())
		{

		case CD_Triangle:
			{
				S01=s_[1];
				S01-=s_[2];
				S02=s_[0];
				S02-=s_[2];
				a1=S01*s_[0],a2=S01*s_[1],a3=S01*s_[2],a4=S02*s_[0],a5=S02*s_[1],a6=S02*s_[2];

				lambda0_=a2*a6-a3*a5;
				lambda1_=a3*a4-a1*a6;
				lambda2_=a1*a5-a2*a4;
				det_=1/(lambda0_+lambda1_+lambda2_);
				lambda0_*=det_;
				lambda1_*=det_;
				lambda2_*=det_;

				proj=s_[0]*lambda0_+s_[1]*lambda1_+s_[2]*lambda2_;
				v=-proj;
				break;
			}

		case CD_Segment:
			{
				S01=s_[1];
				S01-=s_[0];

				lambda0_=S01*s_[1];
				lambda1_=-(S01*s_[0]);
				det_=1/(lambda0_+lambda1_);
				lambda0_*=det_;
				lambda1_*=det_;

				proj=s_[0]*lambda0_+s_[1]*lambda1_;
				v=-proj;
				break;
			}
		default:
			{
				proj=s_[0];
				v=-proj;
			}

		}
		Scalar dist=v.normsquared();

		if (distance_ <= dist) //the distance is not monotonous
		{
		    cont=false;
		}
        else if ( (distance_= dist)<=sp_.farthestPointDistance()*epsilon_)//v is considered zero
		{
			collision_=true;
			cont=false;
		}
		else
		{
			sup1=sObj1_->support(v,lf1);
			sup2=sObj2_->support(-v,lf2);

#ifdef CD_PAIR_VERBOUS_MODE

			std::cout<<"Last features"<<lf1<<" "<<lf2<<std::endl;
			std::cout<<"supports"<<sup1<<" "<<sup2<<std::endl;
#	ifdef CD_ITERATION_LIMIT
			std::cout<<"Iterations"<<cnt<<std::endl;
#	endif
#endif

			sup=sup1;
			sup-=sup2;

			if ((distance_-proj*sup)<(precision_*distance_))//precision reached
			{
				collision_=false;
				cont=false;
				projectionComputed_=true;
			}
			else
			{
				sp_+=sup;
				sp_.updateVectors();
#ifndef SAFE_VERSION
				if (sp_.isAffinelyDependent())
				{
					cont=false;
					collision_=false;
					projectionComputed_=true;
				}
				else
#endif
				{
					sp_.getClosestSubSimplexGJK(k);
					sp_.filter(k);
					s1_+=sup1;
					s2_+=sup2;

					if (sp_.getType()==CD_Tetrahedron)
					{
						cont=false;
						s1_+=sup1;
						s2_+=sup2;
						collision_=true;
					}
					else
					{
						s_=sp_;
						s1_.filter(k);
						s2_.filter(k);
					}
				}
			}
		}
	}

#ifdef CD_PAIR_VERBOUS_MODE
	std::cout<<"#####GJK END######"<<std::endl;
#endif

	return distance_;
}

void CD_Pair::setVector(const Vector3 &v)
{
	lastDirection_=v;
}

void CD_Pair::witPoints(Point3 &p1, Point3 &p2)
{
	Point3 proj;
	Vector3& v=lastDirection_;

	if (collision_)
	{
		p1=p1_;
		p2=p2_;
		return;

	}

	switch (s_.getType())
	{

	case CD_Triangle:
		{

			{
				if (!projectionComputed_)
				{
					Vector3 S01(s_[1]-s_[2]), S02(s_[0]-s_[2]);

					Scalar a1=S01*s_[0],a2=S01*s_[1],a3=S01*s_[2],a4=S02*s_[0],a5=S02*s_[1],a6=S02*s_[2];

					lambda0_=a2*a6-a3*a5;
					lambda1_=a3*a4-a1*a6;
					lambda2_=a1*a5-a2*a4;
					det_=1/(lambda0_+lambda1_+lambda2_);

					lambda0_*=det_;
					lambda1_*=det_;
					lambda2_*=det_;
					proj=s_[0]*lambda0_+s_[1]*lambda1_+s_[2]*lambda2_;
					v=-proj;
				}

				p1_=p1=s1_[0]*lambda0_+s1_[1]*lambda1_+s1_[2]*lambda2_;
				p2_=p2=s2_[0]*lambda0_+s2_[1]*lambda1_+s2_[2]*lambda2_;
			}

#ifdef SHOW_LAST_SIMLPEX
			glDisable(GL_DEPTH_TEST);
			glColor4d(0,0.5,1,0.5);
			glBegin(GL_TRIANGLES);
			glVertex3d(s1_[0][0],s1_[0][1],s1_[0][2]);
			glVertex3d(s1_[1][0],s1_[1][1],s1_[1][2]);
			glVertex3d(s1_[2][0],s1_[2][1],s1_[2][2]);

			glVertex3d(s2_[0][0],s2_[0][1],s2_[0][2]);
			glVertex3d(s2_[1][0],s2_[1][1],s2_[1][2]);
			glVertex3d(s2_[2][0],s2_[2][1],s2_[2][2]);

			glEnd();

			glEnable(GL_DEPTH_TEST);
#endif

			return;
		}

	case CD_Segment:
		{
			if (!projectionComputed_)
			{
				Vector3 S01(s_[1]-s_[0]);

				lambda1_=-(S01*s_[0]);
				lambda0_=S01*s_[1];

				det_=1/(lambda0_+lambda1_);

				lambda0_*=det_;
				lambda1_*=det_;

				proj=s_[0]*lambda0_+s_[1]*lambda1_;
				v=-proj;
			}

			p1_=p1=s1_[0]*lambda0_+s1_[1]*lambda1_;
			p2_=p2=s2_[0]*lambda0_+s2_[1]*lambda1_;

#ifdef SHOW_LAST_SIMLPEX

			glDisable(GL_DEPTH_TEST);
			glColor4d(0,0.5,1,0.5);
			glBegin(GL_LINES);
			glVertex3d(s1_[0][0],s1_[0][1],s1_[0][2]);
			glVertex3d(s1_[1][0],s1_[1][1],s1_[1][2]);

			glVertex3d(s2_[0][0],s2_[0][1],s2_[0][2]);
			glVertex3d(s2_[1][0],s2_[1][1],s2_[1][2]);

			glEnd();

			glEnable(GL_DEPTH_TEST);

#endif
			return ;
		}
	default:
		{
			p1_=p1=s1_[0];
			p2_=p2=s2_[0];

			return ;
		}
	}
}

bool CD_Pair::isInCollision()
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		return collision_;
	}
	else
	{
		Scalar prec=precision_;
		precision_=1;
		GJK();
		precision_=prec;
		return collision_;
	}
}

<commit_msg>Add the initialization of the distance to infinity in the beginning of GJK<commit_after>#include <SCD/CD/CD_Pair.h>
#include <SCD/CD/CD_Simplex.h>
#include <SCD/CD/CD_SimplexEnhanced.h>

#include <iostream>

//#ifndef NOGLUT
//#define SHOW_LAST_SIMLPEX
//#endif

//#define CD_SAFE_VERSION //use when the scalar has a perfect precision
#define PENETRATION_DEPTH
//#define CD_PAIR_VERBOUS_MODE
//#define CD_ITERATION_LIMIT 50 //use when the real-time constraints are too high fo current performances while keeping the same global precision.
								//no theoretical guarantee on the precision nor the collision-safeness when used - Default value is 20

#define _EPSILON_ 1e-24
#define _PRECISION_ 1e-6

using namespace SCD;

inline Vector3 LinearSystem(Matrix3x3& A, Vector3& y)
{
	Matrix3x3 B;
	A.Inversion(B);
	return (B*y);
}

CD_Pair::CD_Pair(S_Object *obj1, S_Object *obj2):sObj1_(obj1),sObj2_(obj2),lastDirection_(1.0,0.0,0.0),
lastFeature1_(-1),lastFeature2_(-1),distance_(0),stamp1_(sObj1_->checkStamp()),stamp2_(sObj2_->checkStamp()),
precision_(_PRECISION_),epsilon_(_EPSILON_),witPointsAreComputed_(false),s1_(Point3()),s2_(Point3()),s_(Point3()),sp_(Point3()),depthPair(obj1,obj2)
{
	--stamp1_;
	--stamp2_;

	depthPair.setRelativePrecision(_PRECISION_);
	depthPair.setEpsilon(_EPSILON_);
}

CD_Pair::~CD_Pair(void)
{
}

Scalar CD_Pair::getDistance()
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		return distance_;
	}
	else
	{
		stamp1_=sObj1_->checkStamp();
		stamp2_=sObj2_->checkStamp();
		GJK();
		penetrationDepth();
		return distance_;
	}
}

Scalar CD_Pair::getDistanceWithoutPenetrationDepth()
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		if (distance_ > 0)
			return distance_;
		return 0;
	}
	else
	{
		GJK();
		if (collision_)
			return 0;
		return distance_;
	}
}

Scalar CD_Pair::reComputeClosestPoints(Point3& p1,Point3& p2)
{
	stamp1_=sObj1_->checkStamp();
	stamp2_=sObj2_->checkStamp();
	GJK();
	penetrationDepth();
	witPoints(p1,p2);
	return distance_;
}

void CD_Pair::setRelativePrecision(Scalar s)
{
	precision_=s*s;
	depthPair.setRelativePrecision(s);
}

void CD_Pair::setEpsilon(Scalar s)
{
	epsilon_=s;
	depthPair.setEpsilon(s);
}

Scalar CD_Pair::getClosestPoints(Point3 &p1, Point3 &p2)
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		if (!witPointsAreComputed_)
		{
			witPointsAreComputed_=true;
			witPoints(p1,p2);
		}
		p1=p1_;
		p2=p2_;
		return distance_;
	}
	else
	{
		stamp1_=sObj1_->checkStamp();
		stamp2_=sObj2_->checkStamp();
		GJK();
		penetrationDepth();
		witPoints(p1,p2);
		witPointsAreComputed_=true;
		return distance_;
	}
}

Scalar CD_Pair::penetrationDepth()
{
#ifdef PENETRATION_DEPTH
	if (collision_)//Objects are in collision
	{
		distance_=-depthPair.getPenetrationDepth(lastDirection_,p1_,p2_,sp_,s1_,s2_);
		if (distance_<0)
		{
			lastDirection_.Set(0,1,0);
		}

		return distance_;
	}
	else
	{
		return distance_;
	}
#else
	return distance_;
#endif
}

Scalar CD_Pair::GJK()
{
	Vector3& v=lastDirection_;

#ifdef CD_PAIR_VERBOUS_MODE
	std::cout<<"#####GJK START######"<<std::endl;
#endif

	witPointsAreComputed_=false;

	int& lf1=lastFeature1_;
	int& lf2=lastFeature2_;

	Point3 sup1=sObj1_->support(v,lf1);
	Point3 sup2=sObj2_->support(-v,lf2);

#ifdef CD_PAIR_VERBOUS_MODE
	std::cout<<"Last features"<<lf1<<" "<<lf2<<std::endl;
#endif

	Point3 sup(sup1);

	sup-=sup2;

	s1_=sup1;
	s2_=sup2;
	s_=sup;

	sp_=sup;

	CD_SimplexKeptPoints k;

	projectionComputed_=false;

	bool cont=true;

	Point3 proj;

	Vector3 S01;
	Vector3 S02;

	Scalar a1,a2,a3,a4,a5,a6;

	distance_=infinity;

#ifdef CD_ITERATION_LIMIT

	int	cnt=0;
#endif

	while (cont)
	{

#ifdef CD_ITERATION_LIMIT

		if (++cnt>CD_ITERATION_LIMIT)
			break;		//the iterations number limit has been reached
#endif

		switch (s_.getType())
		{

		case CD_Triangle:
			{
				S01=s_[1];
				S01-=s_[2];
				S02=s_[0];
				S02-=s_[2];
				a1=S01*s_[0],a2=S01*s_[1],a3=S01*s_[2],a4=S02*s_[0],a5=S02*s_[1],a6=S02*s_[2];

				lambda0_=a2*a6-a3*a5;
				lambda1_=a3*a4-a1*a6;
				lambda2_=a1*a5-a2*a4;
				det_=1/(lambda0_+lambda1_+lambda2_);
				lambda0_*=det_;
				lambda1_*=det_;
				lambda2_*=det_;

				proj=s_[0]*lambda0_+s_[1]*lambda1_+s_[2]*lambda2_;
				v=-proj;
				break;
			}

		case CD_Segment:
			{
				S01=s_[1];
				S01-=s_[0];

				lambda0_=S01*s_[1];
				lambda1_=-(S01*s_[0]);
				det_=1/(lambda0_+lambda1_);
				lambda0_*=det_;
				lambda1_*=det_;

				proj=s_[0]*lambda0_+s_[1]*lambda1_;
				v=-proj;
				break;
			}
		default:
			{
				proj=s_[0];
				v=-proj;
			}

		}
		Scalar newdist=v.normsquared();

		if (distance_ <= newdist) //the distance is not monotonous
		{
		    cont=false;
		}
        else if ( (distance_= newdist)<=sp_.farthestPointDistance()*epsilon_)//v is considered zero
		{
			collision_=true;
			cont=false;
		}
		else
		{
			sup1=sObj1_->support(v,lf1);
			sup2=sObj2_->support(-v,lf2);

#ifdef CD_PAIR_VERBOUS_MODE

			std::cout<<"Last features"<<lf1<<" "<<lf2<<std::endl;
			std::cout<<"supports"<<sup1<<" "<<sup2<<std::endl;
#	ifdef CD_ITERATION_LIMIT
			std::cout<<"Iterations"<<cnt<<std::endl;
#	endif
#endif

			sup=sup1;
			sup-=sup2;

			if ((distance_-proj*sup)<(precision_*distance_))//precision reached
			{
				collision_=false;
				cont=false;
				projectionComputed_=true;
			}
			else
			{
				sp_+=sup;
				sp_.updateVectors();
#ifndef SAFE_VERSION
				if (sp_.isAffinelyDependent())
				{
					cont=false;
					collision_=false;
					projectionComputed_=true;
				}
				else
#endif
				{
					sp_.getClosestSubSimplexGJK(k);
					sp_.filter(k);
					s1_+=sup1;
					s2_+=sup2;

					if (sp_.getType()==CD_Tetrahedron)
					{
						cont=false;
						s1_+=sup1;
						s2_+=sup2;
						collision_=true;
					}
					else
					{
						s_=sp_;
						s1_.filter(k);
						s2_.filter(k);
					}
				}
			}
		}
	}

#ifdef CD_PAIR_VERBOUS_MODE
	std::cout<<"#####GJK END######"<<std::endl;
#endif

	return distance_;
}

void CD_Pair::setVector(const Vector3 &v)
{
	lastDirection_=v;
}

void CD_Pair::witPoints(Point3 &p1, Point3 &p2)
{
	Point3 proj;
	Vector3& v=lastDirection_;

	if (collision_)
	{
		p1=p1_;
		p2=p2_;
		return;

	}

	switch (s_.getType())
	{

	case CD_Triangle:
		{

			{
				if (!projectionComputed_)
				{
					Vector3 S01(s_[1]-s_[2]), S02(s_[0]-s_[2]);

					Scalar a1=S01*s_[0],a2=S01*s_[1],a3=S01*s_[2],a4=S02*s_[0],a5=S02*s_[1],a6=S02*s_[2];

					lambda0_=a2*a6-a3*a5;
					lambda1_=a3*a4-a1*a6;
					lambda2_=a1*a5-a2*a4;
					det_=1/(lambda0_+lambda1_+lambda2_);

					lambda0_*=det_;
					lambda1_*=det_;
					lambda2_*=det_;
					proj=s_[0]*lambda0_+s_[1]*lambda1_+s_[2]*lambda2_;
					v=-proj;
				}

				p1_=p1=s1_[0]*lambda0_+s1_[1]*lambda1_+s1_[2]*lambda2_;
				p2_=p2=s2_[0]*lambda0_+s2_[1]*lambda1_+s2_[2]*lambda2_;
			}

#ifdef SHOW_LAST_SIMLPEX
			glDisable(GL_DEPTH_TEST);
			glColor4d(0,0.5,1,0.5);
			glBegin(GL_TRIANGLES);
			glVertex3d(s1_[0][0],s1_[0][1],s1_[0][2]);
			glVertex3d(s1_[1][0],s1_[1][1],s1_[1][2]);
			glVertex3d(s1_[2][0],s1_[2][1],s1_[2][2]);

			glVertex3d(s2_[0][0],s2_[0][1],s2_[0][2]);
			glVertex3d(s2_[1][0],s2_[1][1],s2_[1][2]);
			glVertex3d(s2_[2][0],s2_[2][1],s2_[2][2]);

			glEnd();

			glEnable(GL_DEPTH_TEST);
#endif

			return;
		}

	case CD_Segment:
		{
			if (!projectionComputed_)
			{
				Vector3 S01(s_[1]-s_[0]);

				lambda1_=-(S01*s_[0]);
				lambda0_=S01*s_[1];

				det_=1/(lambda0_+lambda1_);

				lambda0_*=det_;
				lambda1_*=det_;

				proj=s_[0]*lambda0_+s_[1]*lambda1_;
				v=-proj;
			}

			p1_=p1=s1_[0]*lambda0_+s1_[1]*lambda1_;
			p2_=p2=s2_[0]*lambda0_+s2_[1]*lambda1_;

#ifdef SHOW_LAST_SIMLPEX

			glDisable(GL_DEPTH_TEST);
			glColor4d(0,0.5,1,0.5);
			glBegin(GL_LINES);
			glVertex3d(s1_[0][0],s1_[0][1],s1_[0][2]);
			glVertex3d(s1_[1][0],s1_[1][1],s1_[1][2]);

			glVertex3d(s2_[0][0],s2_[0][1],s2_[0][2]);
			glVertex3d(s2_[1][0],s2_[1][1],s2_[1][2]);

			glEnd();

			glEnable(GL_DEPTH_TEST);

#endif
			return ;
		}
	default:
		{
			p1_=p1=s1_[0];
			p2_=p2=s2_[0];

			return ;
		}
	}
}

bool CD_Pair::isInCollision()
{
	if ((stamp1_==sObj1_->checkStamp())&&(stamp2_==sObj2_->checkStamp()))
	{
		return collision_;
	}
	else
	{
		Scalar prec=precision_;
		precision_=1;
		GJK();
		precision_=prec;
		return collision_;
	}
}

<|endoftext|>


<commit_before>#pragma once

#include "logger.hpp"
#include "logger/wrapper.hpp"

namespace blackhole {

class scoped_attributes_t : public scoped_attributes_concept_t {
    // Attributes provided by this guard.
    mutable log::attributes_t m_guard_attributes;
    // Merged attributes are provided by this guard and all the parent guards.
    // This value is computed lazily.
    mutable log::attributes_t m_merged_attributes;

public:
    scoped_attributes_t(logger_base_t& logger, log::attributes_t&& attributes) :
        scoped_attributes_concept_t(logger),
        m_guard_attributes(std::move(attributes))
    {}

    template<class Wrapper>
    scoped_attributes_t(Wrapper& wrapper, log::attributes_t&& attributes) :
        scoped_attributes_concept_t(wrapper.log()),
        m_guard_attributes(std::move(attributes))
    {}

    virtual
    const log::attributes_t&
    attributes() const {
        if (m_merged_attributes.empty()) {
            m_merged_attributes = std::move(m_guard_attributes);
            if (has_parent()) {
                const auto& parent_attributes = parent().attributes();
                m_merged_attributes.insert(parent_attributes.begin(), parent_attributes.end());
            }
        }
        return m_merged_attributes;
    }
};

} // namespace blackhole
<commit_msg>[Tasks] One task more.<commit_after>#pragma once

#include "logger.hpp"
#include "logger/wrapper.hpp"

namespace blackhole {

class scoped_attributes_t : public scoped_attributes_concept_t {
    // Attributes provided by this guard.
    mutable log::attributes_t m_guard_attributes;
    // Merged attributes are provided by this guard and all the parent guards.
    // This value is computed lazily.
    mutable log::attributes_t m_merged_attributes;

public:
    scoped_attributes_t(logger_base_t& logger, log::attributes_t&& attributes) :
        scoped_attributes_concept_t(logger),
        m_guard_attributes(std::move(attributes))
    {}

    //!@todo: Make logger concept.
    template<class Wrapper>
    scoped_attributes_t(Wrapper& wrapper, log::attributes_t&& attributes) :
        scoped_attributes_concept_t(wrapper.log()),
        m_guard_attributes(std::move(attributes))
    {}

    virtual
    const log::attributes_t&
    attributes() const {
        if (m_merged_attributes.empty()) {
            m_merged_attributes = std::move(m_guard_attributes);
            if (has_parent()) {
                const auto& parent_attributes = parent().attributes();
                m_merged_attributes.insert(parent_attributes.begin(), parent_attributes.end());
            }
        }
        return m_merged_attributes;
    }
};

} // namespace blackhole
<|endoftext|>


<commit_before>#include "remote_access.h"
#include "parameters.h"
#include "slab_allocator.h"
#include "disk_read_thread.h"
#include "file_mapper.h"

const int DEFAULT_MAX_PENDING_IOS = 128;

/**
 * The maximal number of pending IOs is decided by the maximal pending IOs
 * allowed on each I/O thread divided by the number of remote_disk_access.
 * Thus, the performance isn't decided by the number of remote_disk_access.
 */
int remote_disk_access::get_max_num_pending_ios() const
{
	return DEFAULT_MAX_PENDING_IOS * io_threads.size() / num_ios.get();
}

void remote_disk_access::notify_completion(io_request *reqs[], int num)
{
	// There are a few cases for the incoming requests.
	//	the requests issued by the upper layer IO;
	//	the requests split by the current IO;
	//	the requests issued by an application.
	io_request *from_upper[num];
	io_request *from_app[num];
	io_interface *upper_io = NULL;
	int num_from_upper = 0;
	int num_from_app = 0;
	int num_part_reqs = 0;
	std::vector<io_request *> completes;
	for (int i = 0; i < num; i++) {
		// The requests issued by the upper layer IO.
		if (reqs[i]->get_io() != this) {
			if (upper_io == NULL)
				upper_io = reqs[i]->get_io();
			else
				// They should be from the same upper layer IO.
				assert(upper_io == reqs[i]->get_io());
			from_upper[num_from_upper++] = reqs[i];
			continue;
		}
		if (reqs[i]->get_io() == this && !reqs[i]->is_extended_req()) {
			from_app[num_from_app++] = reqs[i];
			continue;
		}

		io_request *orig = reqs[i]->get_orig();
		io_request *req = reqs[i];
		orig->inc_complete_count();
		if (orig->complete_size(req->get_size()))
			completes.push_back(orig);
		else {
			orig->dec_complete_count();
			num_part_reqs++;
		}
	}
	if (num_from_upper > 0)
		upper_io->notify_completion(from_upper, num_from_upper);
	if (num_from_app > 0 && this->get_callback())
		this->get_callback()->invoke(from_app, num_from_app);

	for (unsigned i = 0; i < completes.size(); i++) {
		io_request *orig = completes[i];
		assert(orig->is_extended_req());
		io_interface *io = orig->get_io();
		// It's from an application.
		if (io == this) {
			if (io->get_callback())
				io->get_callback()->invoke(&orig, 1);
		}
		else
			io->notify_completion(&orig, 1);
		orig->dec_complete_count();
		orig->wait4unref();
		// Now we can delete it.
		delete orig;
	}

	num_completed_reqs.inc(num - num_part_reqs);
	wakeup_waiting_thread();
}

remote_disk_access::remote_disk_access(const std::vector<disk_read_thread *> &remotes,
		aio_complete_thread *complete_thread, file_mapper *mapper, int node_id,
		int max_reqs): io_interface(node_id), max_disk_cached_reqs(max_reqs)
{
	num_ios.inc(1);
	this->io_threads = remotes;
	if (complete_thread == NULL)
		this->complete_queue = NULL;
	else
		this->complete_queue = complete_thread->get_queue();
	// TODO I need to deallocate it later.
	msg_allocator = new slab_allocator(IO_MSG_SIZE * sizeof(io_request),
			IO_MSG_SIZE * sizeof(io_request) * 1024, INT_MAX, node_id);
	senders.resize(remotes.size());
	low_prio_senders.resize(remotes.size());
	// create a msg sender for each disk read thread.
	for (unsigned i = 0; i < remotes.size(); i++) {
		senders[i] = request_sender::create(node_id, msg_allocator,
				remotes[i]->get_queue());
		low_prio_senders[i] = request_sender::create(node_id, msg_allocator,
				remotes[i]->get_low_prio_queue());
	}
	cb = NULL;
	this->block_mapper = mapper;
}

remote_disk_access::~remote_disk_access()
{
	assert(senders.size() == low_prio_senders.size());
	int num_senders = senders.size();
	for (int i = 0; i < num_senders; i++) {
		request_sender::destroy(senders[i]);
		request_sender::destroy(low_prio_senders[i]);
	}
}

io_interface *remote_disk_access::clone() const
{
	num_ios.inc(1);
	remote_disk_access *copy = new remote_disk_access(this->get_node_id(),
			this->max_disk_cached_reqs);
	copy->io_threads = this->io_threads;
	copy->senders.resize(this->senders.size());
	copy->low_prio_senders.resize(this->low_prio_senders.size());
	assert(copy->senders.size() == copy->low_prio_senders.size());
	for (unsigned i = 0; i < copy->senders.size(); i++) {
		copy->senders[i] = request_sender::create(this->get_node_id(),
				msg_allocator, this->senders[i]->get_queue());
		copy->low_prio_senders[i] = request_sender::create(this->get_node_id(),
				msg_allocator, this->low_prio_senders[i]->get_queue());
	}
	copy->cb = this->cb;
	copy->block_mapper = this->block_mapper;
	copy->complete_queue = this->complete_queue;
	copy->msg_allocator = this->msg_allocator;
	return copy;
}

void remote_disk_access::cleanup()
{
	num_ios.dec(1);
	for (unsigned i = 0; i < senders.size(); i++) {
		senders[i]->flush_all();
		low_prio_senders[i]->flush_all();
	}
	int num;
	do {
		num = 0;
		assert(senders.size() == low_prio_senders.size());
		for (unsigned i = 0; i < senders.size(); i++) {
			num += senders[i]->get_queue()->get_num_entries();
			num += low_prio_senders[i]->get_queue()->get_num_entries();
		}
		/*
		 * if there are still messages in the queue, wait.
		 * this might be the best I can do right now
		 * unless the queues can notify me when they are
		 * empty.
		 */
		if (num > 0) {
			// Let's wake up all IO threads if there are still
			// some low-priority requests that need to be processed.
			for (unsigned i = 0; i < senders.size(); i++) {
				senders[i]->get_queue()->wakeup();
			}
			usleep(100000);
		}
	} while (num > 0);

	for (unsigned i = 0; i < io_threads.size(); i++)
		io_threads[i]->flush_requests();
}

void remote_disk_access::access(io_request *requests, int num,
		io_status *status)
{
	num_issued_reqs.inc(num);
	// It marks whether a low-priority sender gets a request.
	bool has_msgs[low_prio_senders.size()];
	memset(has_msgs, 0, sizeof(has_msgs[0]) * low_prio_senders.size());

	bool syncd = false;
	for (int i = 0; i < num; i++) {
		assert(requests[i].get_size() >= MIN_BLOCK_SIZE);

		if (requests[i].is_flush()) {
			syncd = true;
			continue;
		}
		else if (requests[i].is_sync()) {
			syncd = true;
		}

		// If the request accesses one RAID block, it's simple.
		if (inside_RAID_block(requests[i])) {
			off_t pg_off = requests[i].get_offset() / PAGE_SIZE;
			int idx = block_mapper->map2file(pg_off);
			// The cache inside a sender is extensible, so it can absorb
			// all requests.
			int ret;
			if (requests[i].is_high_prio())
				ret = senders[idx]->send_cached(&requests[i]);
			else {
				has_msgs[idx] = true;
				ret = low_prio_senders[idx]->send_cached(&requests[i]);
			}
			assert(ret == 1);
		}
		else {
			// If the request accesses multiple RAID blocks, we have to
			// split the request.
			// I still use the default memory allocator, but since it is used
			// when the request size is large, it should normally be OK.
			// TODO I can use slab allocators later.
			io_request *orig = new io_request(true);
			// global_cached_io doesn't issue requests across a block boundary.
			// It can only be application issued requst, so it shouldn't have
			// extension.
			assert(!requests[i].is_extended_req());
			orig->init(requests[i]);
			off_t end = orig->get_offset() + orig->get_size();
			const off_t RAID_block_size = params.get_RAID_block_size() * PAGE_SIZE;
			for (off_t begin = orig->get_offset(); begin < end;
					begin = ROUND(begin + RAID_block_size, RAID_block_size)) {
				io_request req(true);
				int size = ROUND(begin + RAID_block_size, RAID_block_size) - begin;
				size = min(size, end - begin);
				// It only supports to extract a specified request from
				// a single-buffer request.
				extract_pages(*orig, begin, size / PAGE_SIZE, req);
				req.set_orig(orig);
				req.set_io(this);
				assert(inside_RAID_block(req));

				// Send a request.
				off_t pg_off = req.get_offset() / PAGE_SIZE;
				int idx = block_mapper->map2file(pg_off);
				// The cache inside a sender is extensible, so it can absorb
				// all requests.
				int ret;
				if (req.is_high_prio())
					ret = senders[idx]->send_cached(&req);
				else {
					has_msgs[idx] = true;
					ret = low_prio_senders[idx]->send_cached(&req);
				}
				assert(ret == 1);
			}
		}
	}

	int num_remaining = 0;
	assert(senders.size() == low_prio_senders.size());
	for (unsigned i = 0; i < senders.size(); i++) {
		num_remaining += senders[i]->get_num_remaining();
		num_remaining += low_prio_senders[i]->get_num_remaining();
	}
	if (num_remaining > MAX_DISK_CACHED_REQS) {
		flush_requests(MAX_DISK_CACHED_REQS);
	}
	if (syncd)
		flush_requests();

	if (status)
		for (int i = 0; i < num; i++)
			status[i] = IO_PENDING;

	// The IO threads are never blocked by the low-priority queues,
	// but they may be blocked by the regular message queues.
	// If so, we need to explicitly wake up the IO threads.
	for (unsigned i = 0; i < senders.size(); i++) {
		if (has_msgs[i])
			senders[i]->get_queue()->wakeup();
	}

}

void remote_disk_access::flush_requests()
{
	flush_requests(0);
}

void remote_disk_access::flush_requests(int max_cached)
{
	if (complete_queue)
		complete_queue->process(1000, false);
	int num_high_prio_remaining = 0;
	int num_low_prio_remaining = 0;
	// Now let's flush requests to the queues, but we first try to
	// flush requests non-blockingly.
	assert(senders.size() == low_prio_senders.size());
	int num_senders = senders.size();
	for (int i = 0; i < num_senders; i++) {
		senders[i]->flush(false);
		low_prio_senders[i]->flush(false);
		num_high_prio_remaining += senders[i]->get_num_remaining();
		num_low_prio_remaining += low_prio_senders[i]->get_num_remaining();
	}
	// If all requests have been flushed successfully, return immediately.
	if (num_high_prio_remaining + num_low_prio_remaining == 0)
		return;

	int base_idx;
	if (num_senders == 1)
		base_idx = 0;
	else
		base_idx = random() % num_senders;
	int i = 0;
	// We only allow cache that many requests. If we have more than
	// we want, continue flushing, but try harder this time.
	while (num_high_prio_remaining + num_low_prio_remaining > max_cached
			&& num_high_prio_remaining > 0) {
		int idx = (base_idx + i) % num_senders;
		int orig_remaining = senders[idx]->get_num_remaining();
		senders[idx]->flush(true);
		int num_flushed = orig_remaining - senders[idx]->get_num_remaining();
		num_high_prio_remaining -= num_flushed;
		i++;
	}
	// When we reach this point, it means either that the total number of
	// remaining requests is lower than max_cached or there aren't high-
	// priority requests left.
	// In this way, we can make sure high-priority requests have been moved
	// to the IO threads before low-priority requests are moved.
	i = 0;
	while (num_low_prio_remaining > max_cached) {
		int idx = (base_idx + i) % num_senders;
		int orig_remaining = low_prio_senders[idx]->get_num_remaining();
		low_prio_senders[idx]->flush(true);
		int num_flushed = orig_remaining
			- low_prio_senders[idx]->get_num_remaining();
		num_low_prio_remaining -= num_flushed;
		i++;
	}
}

int remote_disk_access::get_file_id() const
{
	return block_mapper->get_file_id();
}

atomic_integer remote_disk_access::num_ios;
<commit_msg>Use 32 as default IO depth for remote access.<commit_after>#include "remote_access.h"
#include "parameters.h"
#include "slab_allocator.h"
#include "disk_read_thread.h"
#include "file_mapper.h"

const int DEFAULT_MAX_PENDING_IOS = 32;

/**
 * The maximal number of pending IOs is decided by the maximal pending IOs
 * allowed on each I/O thread divided by the number of remote_disk_access.
 * Thus, the performance isn't decided by the number of remote_disk_access.
 */
int remote_disk_access::get_max_num_pending_ios() const
{
	return DEFAULT_MAX_PENDING_IOS * io_threads.size() / num_ios.get();
}

void remote_disk_access::notify_completion(io_request *reqs[], int num)
{
	// There are a few cases for the incoming requests.
	//	the requests issued by the upper layer IO;
	//	the requests split by the current IO;
	//	the requests issued by an application.
	io_request *from_upper[num];
	io_request *from_app[num];
	io_interface *upper_io = NULL;
	int num_from_upper = 0;
	int num_from_app = 0;
	int num_part_reqs = 0;
	std::vector<io_request *> completes;
	for (int i = 0; i < num; i++) {
		// The requests issued by the upper layer IO.
		if (reqs[i]->get_io() != this) {
			if (upper_io == NULL)
				upper_io = reqs[i]->get_io();
			else
				// They should be from the same upper layer IO.
				assert(upper_io == reqs[i]->get_io());
			from_upper[num_from_upper++] = reqs[i];
			continue;
		}
		if (reqs[i]->get_io() == this && !reqs[i]->is_extended_req()) {
			from_app[num_from_app++] = reqs[i];
			continue;
		}

		io_request *orig = reqs[i]->get_orig();
		io_request *req = reqs[i];
		orig->inc_complete_count();
		if (orig->complete_size(req->get_size()))
			completes.push_back(orig);
		else {
			orig->dec_complete_count();
			num_part_reqs++;
		}
	}
	if (num_from_upper > 0)
		upper_io->notify_completion(from_upper, num_from_upper);
	if (num_from_app > 0 && this->get_callback())
		this->get_callback()->invoke(from_app, num_from_app);

	for (unsigned i = 0; i < completes.size(); i++) {
		io_request *orig = completes[i];
		assert(orig->is_extended_req());
		io_interface *io = orig->get_io();
		// It's from an application.
		if (io == this) {
			if (io->get_callback())
				io->get_callback()->invoke(&orig, 1);
		}
		else
			io->notify_completion(&orig, 1);
		orig->dec_complete_count();
		orig->wait4unref();
		// Now we can delete it.
		delete orig;
	}

	num_completed_reqs.inc(num - num_part_reqs);
	wakeup_waiting_thread();
}

remote_disk_access::remote_disk_access(const std::vector<disk_read_thread *> &remotes,
		aio_complete_thread *complete_thread, file_mapper *mapper, int node_id,
		int max_reqs): io_interface(node_id), max_disk_cached_reqs(max_reqs)
{
	num_ios.inc(1);
	this->io_threads = remotes;
	if (complete_thread == NULL)
		this->complete_queue = NULL;
	else
		this->complete_queue = complete_thread->get_queue();
	// TODO I need to deallocate it later.
	msg_allocator = new slab_allocator(IO_MSG_SIZE * sizeof(io_request),
			IO_MSG_SIZE * sizeof(io_request) * 1024, INT_MAX, node_id);
	senders.resize(remotes.size());
	low_prio_senders.resize(remotes.size());
	// create a msg sender for each disk read thread.
	for (unsigned i = 0; i < remotes.size(); i++) {
		senders[i] = request_sender::create(node_id, msg_allocator,
				remotes[i]->get_queue());
		low_prio_senders[i] = request_sender::create(node_id, msg_allocator,
				remotes[i]->get_low_prio_queue());
	}
	cb = NULL;
	this->block_mapper = mapper;
}

remote_disk_access::~remote_disk_access()
{
	assert(senders.size() == low_prio_senders.size());
	int num_senders = senders.size();
	for (int i = 0; i < num_senders; i++) {
		request_sender::destroy(senders[i]);
		request_sender::destroy(low_prio_senders[i]);
	}
}

io_interface *remote_disk_access::clone() const
{
	num_ios.inc(1);
	remote_disk_access *copy = new remote_disk_access(this->get_node_id(),
			this->max_disk_cached_reqs);
	copy->io_threads = this->io_threads;
	copy->senders.resize(this->senders.size());
	copy->low_prio_senders.resize(this->low_prio_senders.size());
	assert(copy->senders.size() == copy->low_prio_senders.size());
	for (unsigned i = 0; i < copy->senders.size(); i++) {
		copy->senders[i] = request_sender::create(this->get_node_id(),
				msg_allocator, this->senders[i]->get_queue());
		copy->low_prio_senders[i] = request_sender::create(this->get_node_id(),
				msg_allocator, this->low_prio_senders[i]->get_queue());
	}
	copy->cb = this->cb;
	copy->block_mapper = this->block_mapper;
	copy->complete_queue = this->complete_queue;
	copy->msg_allocator = this->msg_allocator;
	return copy;
}

void remote_disk_access::cleanup()
{
	num_ios.dec(1);
	for (unsigned i = 0; i < senders.size(); i++) {
		senders[i]->flush_all();
		low_prio_senders[i]->flush_all();
	}
	int num;
	do {
		num = 0;
		assert(senders.size() == low_prio_senders.size());
		for (unsigned i = 0; i < senders.size(); i++) {
			num += senders[i]->get_queue()->get_num_entries();
			num += low_prio_senders[i]->get_queue()->get_num_entries();
		}
		/*
		 * if there are still messages in the queue, wait.
		 * this might be the best I can do right now
		 * unless the queues can notify me when they are
		 * empty.
		 */
		if (num > 0) {
			// Let's wake up all IO threads if there are still
			// some low-priority requests that need to be processed.
			for (unsigned i = 0; i < senders.size(); i++) {
				senders[i]->get_queue()->wakeup();
			}
			usleep(100000);
		}
	} while (num > 0);

	for (unsigned i = 0; i < io_threads.size(); i++)
		io_threads[i]->flush_requests();
}

void remote_disk_access::access(io_request *requests, int num,
		io_status *status)
{
	num_issued_reqs.inc(num);
	// It marks whether a low-priority sender gets a request.
	bool has_msgs[low_prio_senders.size()];
	memset(has_msgs, 0, sizeof(has_msgs[0]) * low_prio_senders.size());

	bool syncd = false;
	for (int i = 0; i < num; i++) {
		assert(requests[i].get_size() >= MIN_BLOCK_SIZE);

		if (requests[i].is_flush()) {
			syncd = true;
			continue;
		}
		else if (requests[i].is_sync()) {
			syncd = true;
		}

		// If the request accesses one RAID block, it's simple.
		if (inside_RAID_block(requests[i])) {
			off_t pg_off = requests[i].get_offset() / PAGE_SIZE;
			int idx = block_mapper->map2file(pg_off);
			// The cache inside a sender is extensible, so it can absorb
			// all requests.
			int ret;
			if (requests[i].is_high_prio())
				ret = senders[idx]->send_cached(&requests[i]);
			else {
				has_msgs[idx] = true;
				ret = low_prio_senders[idx]->send_cached(&requests[i]);
			}
			assert(ret == 1);
		}
		else {
			// If the request accesses multiple RAID blocks, we have to
			// split the request.
			// I still use the default memory allocator, but since it is used
			// when the request size is large, it should normally be OK.
			// TODO I can use slab allocators later.
			io_request *orig = new io_request(true);
			// global_cached_io doesn't issue requests across a block boundary.
			// It can only be application issued requst, so it shouldn't have
			// extension.
			assert(!requests[i].is_extended_req());
			orig->init(requests[i]);
			off_t end = orig->get_offset() + orig->get_size();
			const off_t RAID_block_size = params.get_RAID_block_size() * PAGE_SIZE;
			for (off_t begin = orig->get_offset(); begin < end;
					begin = ROUND(begin + RAID_block_size, RAID_block_size)) {
				io_request req(true);
				int size = ROUND(begin + RAID_block_size, RAID_block_size) - begin;
				size = min(size, end - begin);
				// It only supports to extract a specified request from
				// a single-buffer request.
				extract_pages(*orig, begin, size / PAGE_SIZE, req);
				req.set_orig(orig);
				req.set_io(this);
				assert(inside_RAID_block(req));

				// Send a request.
				off_t pg_off = req.get_offset() / PAGE_SIZE;
				int idx = block_mapper->map2file(pg_off);
				// The cache inside a sender is extensible, so it can absorb
				// all requests.
				int ret;
				if (req.is_high_prio())
					ret = senders[idx]->send_cached(&req);
				else {
					has_msgs[idx] = true;
					ret = low_prio_senders[idx]->send_cached(&req);
				}
				assert(ret == 1);
			}
		}
	}

	int num_remaining = 0;
	assert(senders.size() == low_prio_senders.size());
	for (unsigned i = 0; i < senders.size(); i++) {
		num_remaining += senders[i]->get_num_remaining();
		num_remaining += low_prio_senders[i]->get_num_remaining();
	}
	if (num_remaining > MAX_DISK_CACHED_REQS) {
		flush_requests(MAX_DISK_CACHED_REQS);
	}
	if (syncd)
		flush_requests();

	if (status)
		for (int i = 0; i < num; i++)
			status[i] = IO_PENDING;

	// The IO threads are never blocked by the low-priority queues,
	// but they may be blocked by the regular message queues.
	// If so, we need to explicitly wake up the IO threads.
	for (unsigned i = 0; i < senders.size(); i++) {
		if (has_msgs[i])
			senders[i]->get_queue()->wakeup();
	}

}

void remote_disk_access::flush_requests()
{
	flush_requests(0);
}

void remote_disk_access::flush_requests(int max_cached)
{
	if (complete_queue)
		complete_queue->process(1000, false);
	int num_high_prio_remaining = 0;
	int num_low_prio_remaining = 0;
	// Now let's flush requests to the queues, but we first try to
	// flush requests non-blockingly.
	assert(senders.size() == low_prio_senders.size());
	int num_senders = senders.size();
	for (int i = 0; i < num_senders; i++) {
		senders[i]->flush(false);
		low_prio_senders[i]->flush(false);
		num_high_prio_remaining += senders[i]->get_num_remaining();
		num_low_prio_remaining += low_prio_senders[i]->get_num_remaining();
	}
	// If all requests have been flushed successfully, return immediately.
	if (num_high_prio_remaining + num_low_prio_remaining == 0)
		return;

	int base_idx;
	if (num_senders == 1)
		base_idx = 0;
	else
		base_idx = random() % num_senders;
	int i = 0;
	// We only allow cache that many requests. If we have more than
	// we want, continue flushing, but try harder this time.
	while (num_high_prio_remaining + num_low_prio_remaining > max_cached
			&& num_high_prio_remaining > 0) {
		int idx = (base_idx + i) % num_senders;
		int orig_remaining = senders[idx]->get_num_remaining();
		senders[idx]->flush(true);
		int num_flushed = orig_remaining - senders[idx]->get_num_remaining();
		num_high_prio_remaining -= num_flushed;
		i++;
	}
	// When we reach this point, it means either that the total number of
	// remaining requests is lower than max_cached or there aren't high-
	// priority requests left.
	// In this way, we can make sure high-priority requests have been moved
	// to the IO threads before low-priority requests are moved.
	i = 0;
	while (num_low_prio_remaining > max_cached) {
		int idx = (base_idx + i) % num_senders;
		int orig_remaining = low_prio_senders[idx]->get_num_remaining();
		low_prio_senders[idx]->flush(true);
		int num_flushed = orig_remaining
			- low_prio_senders[idx]->get_num_remaining();
		num_low_prio_remaining -= num_flushed;
		i++;
	}
}

int remote_disk_access::get_file_id() const
{
	return block_mapper->get_file_id();
}

atomic_integer remote_disk_access::num_ios;
<|endoftext|>