<commit_before>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <cassert>
#include <algorithm>
#include "Camera.h"
#include "core/Engine.h"
#include "graphics/Renderer.h"
#include "Layer.h"
#include "graphics/Texture.h"
#include "graphics/RenderTarget.h"
#include "math/Matrix4.h"
#include "utils/Utils.h"

namespace ouzel
{
    namespace scene
    {
        Camera::Camera()
        {
        }

        Camera::~Camera()
        {
        }

        void Camera::recalculateProjection()
        {
            Size2 screenSize = sharedEngine->getRenderer()->getSize();

            if (layer)
            {
                if (const graphics::RenderTargetPtr& renderTarget = layer->getRenderTarget())
                {
                    screenSize = renderTarget->getTexture()->getSize();
                }
            }

            assert(screenSize.width > 0.0f && screenSize.height > 0.0f);

            if (targetContentSize.width > 0.0f && targetContentSize.height > 0.0f)
            {
                contentScale.x = screenSize.width / targetContentSize.width;
                contentScale.y = screenSize.height / targetContentSize.height;

                switch (scaleMode)
                {
                    case ScaleMode::NONE:
                    {
                        break;
                    }
                    case ScaleMode::EXACT_FIT:
                    {
                        contentScale.x = 1.0f;
                        contentScale.y = 1.0f;
                        break;
                    }
                    case ScaleMode::NO_BORDER:
                    {
                        contentScale.x = contentScale.y = std::max(contentScale.x, contentScale.y);
                        break;
                    }
                    case ScaleMode::SHOW_ALL:
                    {
                        contentScale.x = contentScale.y = std::min(contentScale.x, contentScale.y);
                        break;
                    }
                }

                contentSize = screenSize / contentScale;
                contentPosition = Vector2((contentSize.width - targetContentSize.width) / 2.0f,
                                          (contentSize.height - targetContentSize.height) / 2.0f);
            }
            else
            {
                contentSize = screenSize;
                contentScale = Vector2(1.0f, 1.0f);
            }

            Matrix4::createOrthographic(screenSize.width / contentScale.x, screenSize.height / contentScale.y, -1.0f, 1.0f, projection);

            inverseProjection = projection;
            inverseProjection.invert();

            viewProjectionDirty = true;
        }

        const Matrix4& Camera::getViewProjection() const
        {
            if (viewProjectionDirty || transformDirty)
            {
                viewProjection = projection * getTransform();
            }

            return viewProjection;
        }

        void Camera::calculateLocalTransform() const
        {
            Matrix4 translationMatrix = Matrix4::IDENTITY;
            translationMatrix.translate(-position.x, -position.y, 0.0f);

            Matrix4 rotationMatrix = Matrix4::IDENTITY;
            rotationMatrix.rotate(Vector3(0.0f, 0.0f, -1.0f), -rotation);

            Matrix4 scaleMatrix = Matrix4::IDENTITY;
            Vector3 inverseScale(0.0f, 0.0f, 1.0f);
            if (scale.x != 0.0f) inverseScale.x = 1.0f / scale.x;
            if (scale.y != 0.0f) inverseScale.y = 1.0f / scale.y;
            scaleMatrix.scale(inverseScale);

            localTransform = scaleMatrix * rotationMatrix * translationMatrix;

            localTransformDirty = false;
        }

        Vector2 Camera::convertScreenToWorld(const Vector2& position)
        {
            Matrix4 projViewMatrix = projection * getTransform();
            Matrix4 inverseViewMatrix = projViewMatrix;
            inverseViewMatrix.invert();

            Vector3 result = Vector3(position.x, position.y, 0.0f);
            inverseViewMatrix.transformPoint(result);

            return Vector2(result.x, result.y);
        }

        Vector2 Camera::convertWorldToScreen(const Vector2& position)
        {
            Matrix4 projViewMatrix = projection * getTransform();

            Vector3 result = Vector3(position.x, position.y, 0.0f);
            projViewMatrix.transformPoint(result);

            return Vector2(result.x, result.y);
        }

        bool Camera::checkVisibility(const Matrix4& boxTransform, const AABB2& boundingBox)
        {
            if (scale.x == 0.0f || scale.y == 0.0f) return false;

            Size2 size = sharedEngine->getRenderer()->getSize();
            Rectangle visibleRect(0.0f, 0.0f, size.width, size.height);

            // transform center point to screen space
            Vector2 diff = boundingBox.max - boundingBox.min;

            Vector3 v3p(boundingBox.min.x + diff.x / 2.0f, boundingBox.min.y + diff.y / 2.0f, 0.0f);
            diff.x *= contentScale.x / scale.x;
            diff.y *= contentScale.y / scale.y;

            boxTransform.transformPoint(v3p);

            Vector2 v2p = projectPoint(v3p);

            Size2 halfSize(diff.x / 2.0f, diff.y / 2.0f);

            // convert content size to world coordinates
            Size2 halfWorldSize;

            if (transformDirty)
            {
                calculateTransform();
            }

            halfWorldSize.width = std::max(fabsf(halfSize.width * transform.m[0] + halfSize.height * transform.m[4]), fabsf(halfSize.width * transform.m[0] - halfSize.height * transform.m[4]));
            halfWorldSize.height = std::max(fabsf(halfSize.width * transform.m[1] + halfSize.height * transform.m[5]), fabsf(halfSize.width * transform.m[1] - halfSize.height * transform.m[5]));

            // enlarge visible rect half size in screen coord
            visibleRect.x -= halfWorldSize.width;
            visibleRect.y -= halfWorldSize.height;
            visibleRect.width += halfWorldSize.width * 2.0f;
            visibleRect.height += halfWorldSize.height * 2.0f;

            return visibleRect.containsPoint(v2p);
        }

        Vector2 Camera::projectPoint(const Vector3& src) const
        {
            Vector2 screenPos;

            auto viewport = sharedEngine->getRenderer()->getSize();
            Vector4 clipPos;

            getViewProjection().transformVector(Vector4(src.x, src.y, src.z, 1.0f), clipPos);

            assert(clipPos.w != 0.0f);
            Vector2 ndc(clipPos.x / clipPos.w, clipPos.y / clipPos.w);

            screenPos.x = (ndc.x + 1.0f) * 0.5f * viewport.width;
            screenPos.y = (ndc.y + 1.0f) * 0.5f * viewport.height;

            return screenPos;
        }

        void Camera::setScaleMode(ScaleMode newScaleMode)
        {
            scaleMode = newScaleMode;
            recalculateProjection();
        }

        void Camera::setTargetContentSize(const Size2& newTargetContentSize)
        {
            targetContentSize = newTargetContentSize;
            recalculateProjection();
        }

        void Camera::addToLayer(Layer* newLayer)
        {
            layer = newLayer;
        }

        void Camera::removeFromLayer()
        {
            layer = nullptr;
        }
    } // namespace scene
} // namespace ouzel
<commit_msg>Don't recalculate viewProjection in convertScreenToWorld and convertWorldToScreen<commit_after>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <cassert>
#include <algorithm>
#include "Camera.h"
#include "core/Engine.h"
#include "graphics/Renderer.h"
#include "Layer.h"
#include "graphics/Texture.h"
#include "graphics/RenderTarget.h"
#include "math/Matrix4.h"
#include "utils/Utils.h"

namespace ouzel
{
    namespace scene
    {
        Camera::Camera()
        {
        }

        Camera::~Camera()
        {
        }

        void Camera::recalculateProjection()
        {
            Size2 screenSize = sharedEngine->getRenderer()->getSize();

            if (layer)
            {
                if (const graphics::RenderTargetPtr& renderTarget = layer->getRenderTarget())
                {
                    screenSize = renderTarget->getTexture()->getSize();
                }
            }

            assert(screenSize.width > 0.0f && screenSize.height > 0.0f);

            if (targetContentSize.width > 0.0f && targetContentSize.height > 0.0f)
            {
                contentScale.x = screenSize.width / targetContentSize.width;
                contentScale.y = screenSize.height / targetContentSize.height;

                switch (scaleMode)
                {
                    case ScaleMode::NONE:
                    {
                        break;
                    }
                    case ScaleMode::EXACT_FIT:
                    {
                        contentScale.x = 1.0f;
                        contentScale.y = 1.0f;
                        break;
                    }
                    case ScaleMode::NO_BORDER:
                    {
                        contentScale.x = contentScale.y = std::max(contentScale.x, contentScale.y);
                        break;
                    }
                    case ScaleMode::SHOW_ALL:
                    {
                        contentScale.x = contentScale.y = std::min(contentScale.x, contentScale.y);
                        break;
                    }
                }

                contentSize = screenSize / contentScale;
                contentPosition = Vector2((contentSize.width - targetContentSize.width) / 2.0f,
                                          (contentSize.height - targetContentSize.height) / 2.0f);
            }
            else
            {
                contentSize = screenSize;
                contentScale = Vector2(1.0f, 1.0f);
            }

            Matrix4::createOrthographic(screenSize.width / contentScale.x, screenSize.height / contentScale.y, -1.0f, 1.0f, projection);

            inverseProjection = projection;
            inverseProjection.invert();

            viewProjectionDirty = true;
        }

        const Matrix4& Camera::getViewProjection() const
        {
            if (viewProjectionDirty || transformDirty)
            {
                viewProjection = projection * getTransform();
            }

            return viewProjection;
        }

        void Camera::calculateLocalTransform() const
        {
            Matrix4 translationMatrix = Matrix4::IDENTITY;
            translationMatrix.translate(-position.x, -position.y, 0.0f);

            Matrix4 rotationMatrix = Matrix4::IDENTITY;
            rotationMatrix.rotate(Vector3(0.0f, 0.0f, -1.0f), -rotation);

            Matrix4 scaleMatrix = Matrix4::IDENTITY;
            Vector3 inverseScale(0.0f, 0.0f, 1.0f);
            if (scale.x != 0.0f) inverseScale.x = 1.0f / scale.x;
            if (scale.y != 0.0f) inverseScale.y = 1.0f / scale.y;
            scaleMatrix.scale(inverseScale);

            localTransform = scaleMatrix * rotationMatrix * translationMatrix;

            localTransformDirty = false;
        }

        Vector2 Camera::convertScreenToWorld(const Vector2& position)
        {
            Matrix4 projViewMatrix = getViewProjection();
            Matrix4 inverseViewMatrix = projViewMatrix;
            inverseViewMatrix.invert();

            Vector3 result = Vector3(position.x, position.y, 0.0f);
            inverseViewMatrix.transformPoint(result);

            return Vector2(result.x, result.y);
        }

        Vector2 Camera::convertWorldToScreen(const Vector2& position)
        {
            Matrix4 projViewMatrix = getViewProjection();

            Vector3 result = Vector3(position.x, position.y, 0.0f);
            projViewMatrix.transformPoint(result);

            return Vector2(result.x, result.y);
        }

        bool Camera::checkVisibility(const Matrix4& boxTransform, const AABB2& boundingBox)
        {
            if (scale.x == 0.0f || scale.y == 0.0f) return false;

            Size2 size = sharedEngine->getRenderer()->getSize();
            Rectangle visibleRect(0.0f, 0.0f, size.width, size.height);

            // transform center point to screen space
            Vector2 diff = boundingBox.max - boundingBox.min;

            Vector3 v3p(boundingBox.min.x + diff.x / 2.0f, boundingBox.min.y + diff.y / 2.0f, 0.0f);
            diff.x *= contentScale.x / scale.x;
            diff.y *= contentScale.y / scale.y;

            boxTransform.transformPoint(v3p);

            Vector2 v2p = projectPoint(v3p);

            Size2 halfSize(diff.x / 2.0f, diff.y / 2.0f);

            // convert content size to world coordinates
            Size2 halfWorldSize;

            if (transformDirty)
            {
                calculateTransform();
            }

            halfWorldSize.width = std::max(fabsf(halfSize.width * transform.m[0] + halfSize.height * transform.m[4]), fabsf(halfSize.width * transform.m[0] - halfSize.height * transform.m[4]));
            halfWorldSize.height = std::max(fabsf(halfSize.width * transform.m[1] + halfSize.height * transform.m[5]), fabsf(halfSize.width * transform.m[1] - halfSize.height * transform.m[5]));

            // enlarge visible rect half size in screen coord
            visibleRect.x -= halfWorldSize.width;
            visibleRect.y -= halfWorldSize.height;
            visibleRect.width += halfWorldSize.width * 2.0f;
            visibleRect.height += halfWorldSize.height * 2.0f;

            return visibleRect.containsPoint(v2p);
        }

        Vector2 Camera::projectPoint(const Vector3& src) const
        {
            Vector2 screenPos;

            auto viewport = sharedEngine->getRenderer()->getSize();
            Vector4 clipPos;

            getViewProjection().transformVector(Vector4(src.x, src.y, src.z, 1.0f), clipPos);

            assert(clipPos.w != 0.0f);
            Vector2 ndc(clipPos.x / clipPos.w, clipPos.y / clipPos.w);

            screenPos.x = (ndc.x + 1.0f) * 0.5f * viewport.width;
            screenPos.y = (ndc.y + 1.0f) * 0.5f * viewport.height;

            return screenPos;
        }

        void Camera::setScaleMode(ScaleMode newScaleMode)
        {
            scaleMode = newScaleMode;
            recalculateProjection();
        }

        void Camera::setTargetContentSize(const Size2& newTargetContentSize)
        {
            targetContentSize = newTargetContentSize;
            recalculateProjection();
        }

        void Camera::addToLayer(Layer* newLayer)
        {
            layer = newLayer;
        }

        void Camera::removeFromLayer()
        {
            layer = nullptr;
        }
    } // namespace scene
} // namespace ouzel
<|endoftext|>


<commit_before>// HoldTheFlag.cpp : bzfs plugin for Hold-The-flag game mode
//

#include "bzfsAPI.h"
#include <map>
#include <stdio.h>
#include <stdarg.h>

#define HOLDTHEFLAG_VER "1.00.02"
#define DO_FLAG_RESET 1

#define MAX_PLAYERID 255

typedef struct {
  bool isValid;
  int  score;
  char callsign[22];
  int capNum;
} HtfPlayer;

HtfPlayer Players[MAX_PLAYERID+1];
std::map<std::string, HtfPlayer> leftDuringMatch;
bool matchActive = false;
bool htfEnabled = true;
bz_eTeamType htfTeam = eNoTeam;
int nextCapNum = 0;
int NumPlayers=0;
int Leader;

class HTFscore : public bz_Plugin, public bz_CustomSlashCommandHandler
{
public:
	virtual const char* Name() {return "Hold the Flag";}
	virtual void Init ( const char* config );
	virtual void Cleanup ( void );
  virtual void Event ( bz_EventData *eventData );
  virtual bool SlashCommand ( int playerID, bz_ApiString, bz_ApiString, bz_APIStringList*);
  bz_eTeamType colorNameToDef (const char *color);
  const char *colorDefToName (bz_eTeamType team);

protected:

private:
};

BZ_PLUGIN(HTFscore)

HTFscore *htfScore = NULL;

bz_eTeamType HTFscore::colorNameToDef (const char *color)
{
  if (!strcasecmp (color, "green"))
    return eGreenTeam;
  if (!strcasecmp (color, "red"))
    return eRedTeam;
  if (!strcasecmp (color, "purple"))
    return ePurpleTeam;
  if (!strcasecmp (color, "blue"))
    return eBlueTeam;
  if (!strcasecmp (color, "rogue"))
    return eRogueTeam;
  if (!strcasecmp (color, "observer"))
    return eObservers;
  return eNoTeam;
}

const char *HTFscore::colorDefToName (bz_eTeamType team)
{
  switch (team){
    case eGreenTeam:
      return ("Green");
    case eBlueTeam:
      return ("Blue");
    case eRedTeam:
      return ("Red");
    case ePurpleTeam:
      return ("Purple");
    case eObservers:
      return ("Observer");
    case eRogueTeam:
      return ("Rogue");
    case eRabbitTeam:
      return ("Rabbit");
    case eHunterTeam:
      return ("Hunters");
    case eAdministrators:
      return ("Administrators");
    default:
      return ("No Team");
  }
}

bool listAdd (int playerID, const char *callsign)
{
  if (playerID>MAX_PLAYERID || playerID<0)
    return false;
  Players[playerID].score = 0;
  Players[playerID].isValid = true;
  Players[playerID].capNum = -1;
  strncpy (Players[playerID].callsign, callsign, 20);
  ++NumPlayers;
  return true;
}

bool listDel (int playerID){
  if (playerID>MAX_PLAYERID || playerID<0 || !Players[playerID].isValid)
    return false;
  Players[playerID].isValid = false;
  --NumPlayers;
  return true;
}

int sort_compare (const void *_p1, const void *_p2){
  const int p1 = *(const int *)_p1;
  const int p2 = *(const int *)_p2;

  if (Players[p1].score != Players[p2].score)
    return Players[p2].score - Players[p1].score;
  return Players[p2].capNum - Players[p1].capNum;
  return 0;
}

void dispScores (int who)
{
  int sortList[MAX_PLAYERID+1];	 // do HtfPlayer *   !!
  int playerLastCapped = -1;
  int lastCapnum = -1;
  int x = 0;

  if (!htfEnabled)
    return;
  bz_sendTextMessage(BZ_SERVER, who, "**** HTF  Scoreboard ****");
  Leader = -1;
  if (NumPlayers<1)
    return;

  for (int i=0; i<MAX_PLAYERID; i++){
    if (Players[i].isValid) {
      if (Players[i].capNum > lastCapnum){
	playerLastCapped = i;
	lastCapnum = Players[i].capNum;
      }
      sortList[x++] = i;
    }
  }
  qsort (sortList, NumPlayers, sizeof(int), sort_compare);
  if (x != NumPlayers)
    bz_debugMessage(1, "++++++++++++++++++++++++ HTF INTERNAL ERROR: player count mismatch!");
  for (int i=0; i<NumPlayers; i++){
    x = sortList[i];
    bz_sendTextMessagef(BZ_SERVER, who, "%20.20s :%3d %c", Players[x].callsign, Players[x].score,
			x == playerLastCapped ? '*' : ' ');
  }
  Leader = sortList[0];
}

void resetScores (void)
{
  for (int i=0; i<MAX_PLAYERID; i++){
    Players[i].score = 0;
    Players[i].capNum = -1;
  }
  nextCapNum = 0;
}

void htfCapture (int who)
{
  if (!htfEnabled)
    return;

#if DO_FLAG_RESET
  bz_resetFlags ( false );
#endif

  bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "HTF FLAG CAPTURED by %s", Players[who].callsign);
  ++Players[who].score;
  Players[who].capNum = nextCapNum++;
  dispScores(BZ_ALLUSERS);
}

void htfStartGame (void)
{
  if (!htfEnabled)
    return;

// TODO: clear leftDuringMatch
  resetScores();
  matchActive = true;
  bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, "HTF MATCH has begun, good luck!");
}

void htfEndGame (void)
{
  if (htfEnabled && matchActive){
    dispScores(BZ_ALLUSERS);
    bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, "HTF MATCH has ended.");
    if (Leader >= 0)
      bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "%s is the WINNER !", Players[Leader].callsign);
  }

  matchActive = false;

// TODO: clear leftDuringMatch

}

void sendHelp (int who)
{
  bz_sendTextMessage(BZ_SERVER, who, "HTF commands: reset, off, on, stats");
}

/************************** (SUB)COMMAND Implementations ... **************************/

void htfStats (int who)
{
  bz_sendTextMessagef(BZ_SERVER, who, "HTF plugin version %s", HOLDTHEFLAG_VER);
  bz_sendTextMessagef(BZ_SERVER, who,  "  Team: %s", htfScore->colorDefToName(htfTeam));
  bz_sendTextMessagef(BZ_SERVER, who,  "  Flag Reset: %s" , DO_FLAG_RESET ? "ENabled":"DISabled");
}

void htfReset (int who)
{
  resetScores();
  bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "*** HTF scores reset by %s", Players[who].callsign);
}

void htfEnable (bool onoff, int who)
{
  char msg[255];
  if (onoff == htfEnabled){
    bz_sendTextMessage(BZ_SERVER, who, "HTF mode is already that way.");
    return;
  }
  htfEnabled = onoff;
  sprintf (msg, "*** HTF mode %s by %s", onoff?"ENabled":"DISabled", Players[who].callsign);
  bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, msg);
}

// handle events
void HTFscore::Event ( bz_EventData *eventData )
{
  // player JOIN
  if (eventData->eventType == bz_ePlayerJoinEvent) {
    bz_PlayerJoinPartEventData_V1 *joinData = (bz_PlayerJoinPartEventData_V1*)eventData;
bz_debugMessagef(3, "++++++ HTFscore: Player JOINED (ID:%d, TEAM:%d, CALLSIGN:%s)", joinData->playerID, joinData->record->team, joinData->record->callsign.c_str()); fflush (stdout);
    if (htfTeam!=eNoTeam && joinData->record->team!=htfTeam && joinData->record->team != eObservers){
      char msg[255];
      sprintf (msg, "HTF mode enabled, you must join the %s team to play", htfScore->colorDefToName(htfTeam));
      bz_kickUser (joinData->playerID, msg, true);
      return;
    }
    if (joinData->record->team == htfTeam)
      listAdd (joinData->playerID, joinData->record->callsign.c_str());

  // player PART
  } else if (eventData->eventType == bz_ePlayerPartEvent) {
    bz_PlayerJoinPartEventData_V1 *joinData = (bz_PlayerJoinPartEventData_V1*)eventData;
bz_debugMessagef(3, "++++++ HTFscore: Player PARTED (ID:%d, TEAM:%d, CALLSIGN:%s)", joinData->playerID, joinData->record->team, joinData->record->callsign.c_str()); fflush (stdout);

    if (joinData->record->team== htfTeam)
      listDel (joinData->playerID);

  // flag CAPTURE
  } else if (eventData->eventType == bz_eCaptureEvent) {
    bz_CTFCaptureEventData_V1 *capData = (bz_CTFCaptureEventData_V1*)eventData;
    htfCapture (capData->playerCapping);

  // game START
  } else if (eventData->eventType == bz_eGameStartEvent) {
    bz_GameStartEndEventData_V1 *msgData = (bz_GameStartEndEventData_V1*)eventData;
bz_debugMessagef(2, "++++++ HTFscore: Game START (%f, %f)", msgData->eventTime, msgData->duration); fflush (stdout);
    htfStartGame ();

  // game END
  } else if (eventData->eventType == bz_eGameEndEvent) {
    bz_GameStartEndEventData_V1 *msgData = (bz_GameStartEndEventData_V1*)eventData;
bz_debugMessagef(2, "++++++ HTFscore: Game END (%f, %f)", msgData->eventTime, msgData->duration); fflush (stdout);
    htfEndGame ();
  }
}

bool checkPerms (int playerID, const char *htfCmd, const char *permName)
{
  if (bz_hasPerm (playerID, permName))
    return true;
  bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "you need \"%s\" permission to do /htf %s", permName, htfCmd);
  return false;
}

// handle /htf command
bool HTFscore::SlashCommand ( int playerID, bz_ApiString cmd, bz_ApiString, bz_APIStringList* cmdParams )
{
  char subCmd[6];
  if (strcasecmp (cmd.c_str(), "htf"))   // is it for me ?
    return false;
  if (cmdParams->get(0).c_str()[0] == '\0'){
    dispScores (playerID);
    return true;
  }

  strncpy (subCmd, cmdParams->get(0).c_str(), 5);
  subCmd[4] = '\0';
  if (strcasecmp (subCmd, "rese") == 0){
    if (checkPerms (playerID, "reset", "COUNTDOWN"))
      htfReset (playerID);
  } else if (strcasecmp (subCmd, "off") == 0){
    if (checkPerms (playerID, "off", "HTFONOFF"))
      htfEnable (false, playerID);
  } else if (strcasecmp (subCmd, "on") == 0){
    if (checkPerms (playerID, "off", "HTFONOFF"))
      htfEnable (true, playerID);
  } else if (strcasecmp (subCmd, "stat") == 0)
    htfStats (playerID);
  else
    sendHelp (playerID);
  return true;
}

bool commandLineHelp (void){
  const char *help[] = {
    "Command line args:  PLUGINNAME,team=color",
    NULL
  };
  bz_debugMessage (0, "+++ HoldTheFlag plugin command-line error");
  for (int x=0; help[x]!=NULL; x++)
    bz_debugMessage (0, help[x]);
  return true;
}

bool parseCommandLine (const char *cmdLine)
{
  if (cmdLine==NULL || *cmdLine=='\0')
    return false;
  htfTeam = eGreenTeam;
  if (strcasecmp (cmdLine, "team=") == 0){
    if ((htfTeam = htfScore->colorNameToDef(cmdLine+5)) == eNoTeam) {
      return commandLineHelp ();
    }
  }
  return false;
}

void HTFscore::Init(const char* cmdLine)
{
  htfScore = this;

  if (parseCommandLine (cmdLine))
    return;

  // get current list of player indices ...
  bz_APIIntList *playerList = bz_newIntList();
  bz_getPlayerIndexList (playerList);
  for (unsigned int i = 0; i < playerList->size(); i++){
    bz_BasePlayerRecord *playerRecord = bz_getPlayerByIndex (playerList->get(i));
    if (playerRecord != NULL){
      listAdd (playerList->get(i), playerRecord->callsign.c_str());
    }
    bz_freePlayerRecord (playerRecord);
  }
  bz_deleteIntList (playerList);

  bz_registerCustomSlashCommand ("htf", this);
  Register(bz_ePlayerJoinEvent);
  Register(bz_ePlayerPartEvent);
  Register(bz_eCaptureEvent);
  Register(bz_eGameStartEvent);
  Register(bz_eGameEndEvent);
}

void HTFscore::Cleanup(void)
{
	htfScore = NULL;
	Flush();
  bz_removeCustomSlashCommand ("htf");
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Remove unnecessary return statement.<commit_after>// HoldTheFlag.cpp : bzfs plugin for Hold-The-flag game mode
//

#include "bzfsAPI.h"
#include <map>
#include <stdio.h>
#include <stdarg.h>

#define HOLDTHEFLAG_VER "1.00.02"
#define DO_FLAG_RESET 1

#define MAX_PLAYERID 255

typedef struct {
  bool isValid;
  int  score;
  char callsign[22];
  int capNum;
} HtfPlayer;

HtfPlayer Players[MAX_PLAYERID+1];
std::map<std::string, HtfPlayer> leftDuringMatch;
bool matchActive = false;
bool htfEnabled = true;
bz_eTeamType htfTeam = eNoTeam;
int nextCapNum = 0;
int NumPlayers=0;
int Leader;

class HTFscore : public bz_Plugin, public bz_CustomSlashCommandHandler
{
public:
  virtual const char* Name() {return "Hold the Flag";}
  virtual void Init ( const char* config );
  virtual void Cleanup ( void );
  virtual void Event ( bz_EventData *eventData );
  virtual bool SlashCommand ( int playerID, bz_ApiString, bz_ApiString, bz_APIStringList*);
  bz_eTeamType colorNameToDef (const char *color);
  const char *colorDefToName (bz_eTeamType team);
};

BZ_PLUGIN(HTFscore)

HTFscore *htfScore = NULL;

bz_eTeamType HTFscore::colorNameToDef (const char *color)
{
  if (!strcasecmp (color, "green"))
    return eGreenTeam;
  if (!strcasecmp (color, "red"))
    return eRedTeam;
  if (!strcasecmp (color, "purple"))
    return ePurpleTeam;
  if (!strcasecmp (color, "blue"))
    return eBlueTeam;
  if (!strcasecmp (color, "rogue"))
    return eRogueTeam;
  if (!strcasecmp (color, "observer"))
    return eObservers;
  return eNoTeam;
}

const char *HTFscore::colorDefToName (bz_eTeamType team)
{
  switch (team){
    case eGreenTeam:
      return ("Green");
    case eBlueTeam:
      return ("Blue");
    case eRedTeam:
      return ("Red");
    case ePurpleTeam:
      return ("Purple");
    case eObservers:
      return ("Observer");
    case eRogueTeam:
      return ("Rogue");
    case eRabbitTeam:
      return ("Rabbit");
    case eHunterTeam:
      return ("Hunters");
    case eAdministrators:
      return ("Administrators");
    default:
      return ("No Team");
  }
}

bool listAdd (int playerID, const char *callsign)
{
  if (playerID>MAX_PLAYERID || playerID<0)
    return false;
  Players[playerID].score = 0;
  Players[playerID].isValid = true;
  Players[playerID].capNum = -1;
  strncpy (Players[playerID].callsign, callsign, 20);
  ++NumPlayers;
  return true;
}

bool listDel (int playerID){
  if (playerID>MAX_PLAYERID || playerID<0 || !Players[playerID].isValid)
    return false;
  Players[playerID].isValid = false;
  --NumPlayers;
  return true;
}

int sort_compare (const void *_p1, const void *_p2){
  const int p1 = *(const int *)_p1;
  const int p2 = *(const int *)_p2;

  if (Players[p1].score != Players[p2].score)
    return Players[p2].score - Players[p1].score;
  return Players[p2].capNum - Players[p1].capNum;
}

void dispScores (int who)
{
  int sortList[MAX_PLAYERID+1];	 // do HtfPlayer *   !!
  int playerLastCapped = -1;
  int lastCapnum = -1;
  int x = 0;

  if (!htfEnabled)
    return;
  bz_sendTextMessage(BZ_SERVER, who, "**** HTF  Scoreboard ****");
  Leader = -1;
  if (NumPlayers<1)
    return;

  for (int i=0; i<MAX_PLAYERID; i++){
    if (Players[i].isValid) {
      if (Players[i].capNum > lastCapnum){
	playerLastCapped = i;
	lastCapnum = Players[i].capNum;
      }
      sortList[x++] = i;
    }
  }
  qsort (sortList, NumPlayers, sizeof(int), sort_compare);
  if (x != NumPlayers)
    bz_debugMessage(1, "++++++++++++++++++++++++ HTF INTERNAL ERROR: player count mismatch!");
  for (int i=0; i<NumPlayers; i++){
    x = sortList[i];
    bz_sendTextMessagef(BZ_SERVER, who, "%20.20s :%3d %c", Players[x].callsign, Players[x].score,
			x == playerLastCapped ? '*' : ' ');
  }
  Leader = sortList[0];
}

void resetScores (void)
{
  for (int i=0; i<MAX_PLAYERID; i++){
    Players[i].score = 0;
    Players[i].capNum = -1;
  }
  nextCapNum = 0;
}

void htfCapture (int who)
{
  if (!htfEnabled)
    return;

#if DO_FLAG_RESET
  bz_resetFlags ( false );
#endif

  bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "HTF FLAG CAPTURED by %s", Players[who].callsign);
  ++Players[who].score;
  Players[who].capNum = nextCapNum++;
  dispScores(BZ_ALLUSERS);
}

void htfStartGame (void)
{
  if (!htfEnabled)
    return;

// TODO: clear leftDuringMatch
  resetScores();
  matchActive = true;
  bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, "HTF MATCH has begun, good luck!");
}

void htfEndGame (void)
{
  if (htfEnabled && matchActive){
    dispScores(BZ_ALLUSERS);
    bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, "HTF MATCH has ended.");
    if (Leader >= 0)
      bz_sendTextMessagef(BZ_SERVER, BZ_ALLUSERS, "%s is the WINNER !", Players[Leader].callsign);
  }

  matchActive = false;

// TODO: clear leftDuringMatch

}

void sendHelp (int who)
{
  bz_sendTextMessage(BZ_SERVER, who, "HTF commands: reset, off, on, stats");
}

/************************** (SUB)COMMAND Implementations ... **************************/

void htfStats (int who)
{
  bz_sendTextMessagef(BZ_SERVER, who, "HTF plugin version %s", HOLDTHEFLAG_VER);
  bz_sendTextMessagef(BZ_SERVER, who,  "  Team: %s", htfScore->colorDefToName(htfTeam));
  bz_sendTextMessagef(BZ_SERVER, who,  "  Flag Reset: %s" , DO_FLAG_RESET ? "ENabled":"DISabled");
}

void htfReset (int who)
{
  resetScores();
  bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "*** HTF scores reset by %s", Players[who].callsign);
}

void htfEnable (bool onoff, int who)
{
  char msg[255];
  if (onoff == htfEnabled){
    bz_sendTextMessage(BZ_SERVER, who, "HTF mode is already that way.");
    return;
  }
  htfEnabled = onoff;
  sprintf (msg, "*** HTF mode %s by %s", onoff?"ENabled":"DISabled", Players[who].callsign);
  bz_sendTextMessage(BZ_SERVER, BZ_ALLUSERS, msg);
}

// handle events
void HTFscore::Event ( bz_EventData *eventData )
{
  // player JOIN
  if (eventData->eventType == bz_ePlayerJoinEvent) {
    bz_PlayerJoinPartEventData_V1 *joinData = (bz_PlayerJoinPartEventData_V1*)eventData;
bz_debugMessagef(3, "++++++ HTFscore: Player JOINED (ID:%d, TEAM:%d, CALLSIGN:%s)", joinData->playerID, joinData->record->team, joinData->record->callsign.c_str()); fflush (stdout);
    if (htfTeam!=eNoTeam && joinData->record->team!=htfTeam && joinData->record->team != eObservers){
      char msg[255];
      sprintf (msg, "HTF mode enabled, you must join the %s team to play", htfScore->colorDefToName(htfTeam));
      bz_kickUser (joinData->playerID, msg, true);
      return;
    }
    if (joinData->record->team == htfTeam)
      listAdd (joinData->playerID, joinData->record->callsign.c_str());

  // player PART
  } else if (eventData->eventType == bz_ePlayerPartEvent) {
    bz_PlayerJoinPartEventData_V1 *joinData = (bz_PlayerJoinPartEventData_V1*)eventData;
bz_debugMessagef(3, "++++++ HTFscore: Player PARTED (ID:%d, TEAM:%d, CALLSIGN:%s)", joinData->playerID, joinData->record->team, joinData->record->callsign.c_str()); fflush (stdout);

    if (joinData->record->team== htfTeam)
      listDel (joinData->playerID);

  // flag CAPTURE
  } else if (eventData->eventType == bz_eCaptureEvent) {
    bz_CTFCaptureEventData_V1 *capData = (bz_CTFCaptureEventData_V1*)eventData;
    htfCapture (capData->playerCapping);

  // game START
  } else if (eventData->eventType == bz_eGameStartEvent) {
    bz_GameStartEndEventData_V1 *msgData = (bz_GameStartEndEventData_V1*)eventData;
bz_debugMessagef(2, "++++++ HTFscore: Game START (%f, %f)", msgData->eventTime, msgData->duration); fflush (stdout);
    htfStartGame ();

  // game END
  } else if (eventData->eventType == bz_eGameEndEvent) {
    bz_GameStartEndEventData_V1 *msgData = (bz_GameStartEndEventData_V1*)eventData;
bz_debugMessagef(2, "++++++ HTFscore: Game END (%f, %f)", msgData->eventTime, msgData->duration); fflush (stdout);
    htfEndGame ();
  }
}

bool checkPerms (int playerID, const char *htfCmd, const char *permName)
{
  if (bz_hasPerm (playerID, permName))
    return true;
  bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "you need \"%s\" permission to do /htf %s", permName, htfCmd);
  return false;
}

// handle /htf command
bool HTFscore::SlashCommand ( int playerID, bz_ApiString cmd, bz_ApiString, bz_APIStringList* cmdParams )
{
  char subCmd[6];
  if (strcasecmp (cmd.c_str(), "htf"))   // is it for me ?
    return false;
  if (cmdParams->get(0).c_str()[0] == '\0'){
    dispScores (playerID);
    return true;
  }

  strncpy (subCmd, cmdParams->get(0).c_str(), 5);
  subCmd[4] = '\0';
  if (strcasecmp (subCmd, "rese") == 0){
    if (checkPerms (playerID, "reset", "COUNTDOWN"))
      htfReset (playerID);
  } else if (strcasecmp (subCmd, "off") == 0){
    if (checkPerms (playerID, "off", "HTFONOFF"))
      htfEnable (false, playerID);
  } else if (strcasecmp (subCmd, "on") == 0){
    if (checkPerms (playerID, "off", "HTFONOFF"))
      htfEnable (true, playerID);
  } else if (strcasecmp (subCmd, "stat") == 0)
    htfStats (playerID);
  else
    sendHelp (playerID);
  return true;
}

bool commandLineHelp (void){
  const char *help[] = {
    "Command line args:  PLUGINNAME,team=color",
    NULL
  };
  bz_debugMessage (0, "+++ HoldTheFlag plugin command-line error");
  for (int x=0; help[x]!=NULL; x++)
    bz_debugMessage (0, help[x]);
  return true;
}

bool parseCommandLine (const char *cmdLine)
{
  if (cmdLine==NULL || *cmdLine=='\0')
    return false;
  htfTeam = eGreenTeam;
  if (strcasecmp (cmdLine, "team=") == 0){
    if ((htfTeam = htfScore->colorNameToDef(cmdLine+5)) == eNoTeam) {
      return commandLineHelp ();
    }
  }
  return false;
}

void HTFscore::Init(const char* cmdLine)
{
  htfScore = this;

  if (parseCommandLine (cmdLine))
    return;

  // get current list of player indices ...
  bz_APIIntList *playerList = bz_newIntList();
  bz_getPlayerIndexList (playerList);
  for (unsigned int i = 0; i < playerList->size(); i++){
    bz_BasePlayerRecord *playerRecord = bz_getPlayerByIndex (playerList->get(i));
    if (playerRecord != NULL){
      listAdd (playerList->get(i), playerRecord->callsign.c_str());
    }
    bz_freePlayerRecord (playerRecord);
  }
  bz_deleteIntList (playerList);

  bz_registerCustomSlashCommand ("htf", this);
  Register(bz_ePlayerJoinEvent);
  Register(bz_ePlayerPartEvent);
  Register(bz_eCaptureEvent);
  Register(bz_eGameStartEvent);
  Register(bz_eGameEndEvent);
}

void HTFscore::Cleanup(void)
{
	htfScore = NULL;
	Flush();
  bz_removeCustomSlashCommand ("htf");
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/*
  Q Light Controller Plus
  artnetplugin.cpp

  Copyright (c) Massimo Callegari

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include "artnetplugin.h"
#include "configureartnet.h"

#include <QSettings>
#include <QDebug>
#include <QMessageBox>

ArtNetPlugin::~ArtNetPlugin()
{
}

void ArtNetPlugin::init()
{
    foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces())
    {
        foreach (QNetworkAddressEntry entry, interface.addressEntries())
        {
            QHostAddress addr = entry.ip();
            if (addr.protocol() != QAbstractSocket::IPv6Protocol)
            {
                ArtNetIO tmpIO;
                tmpIO.interface = interface;
                tmpIO.address = entry;
                tmpIO.controller = NULL;

                bool alreadyInList = false;
                for(int j = 0; j < m_IOmapping.count(); j++)
                {
                    if (m_IOmapping.at(j).address == tmpIO.address)
                    {
                        alreadyInList = true;
                        break;
                    }
                }
                if (alreadyInList == false)
                {
                    m_IOmapping.append(tmpIO);
                }
            }
        }
    }
}

QString ArtNetPlugin::name()
{
    return QString("ArtNet");
}

int ArtNetPlugin::capabilities() const
{
    return QLCIOPlugin::Output | QLCIOPlugin::Input | QLCIOPlugin::Infinite;
}

QString ArtNetPlugin::pluginInfo()
{
    QString str;

    str += QString("");
    str += QString("");
    str += QString("<TITLE>%1</TITLE>").arg(name());
    str += QString("");
    str += QString("");

    str += QString("
");
    str += QString("
%1
").arg(name());
    str += tr("This plugin provides DMX output for devices supporting the ArtNet communication protocol.");
    str += QString("
");

    return str;
}

/*********************************************************************
 * Outputs
 *********************************************************************/
QStringList ArtNetPlugin::outputs()
{
    QStringList list;
    int j = 0;

    init();

    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString("%1: %2").arg(j + 1).arg(line.address.ip().toString());
        j++;
    }
    return list;
}

QString ArtNetPlugin::outputInfo(quint32 output)
{
    if (output >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Output")).arg(outputs()[output]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(output).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Input)
        str += tr("Status: Not open");
    else
    {
        str += tr("Status: Open");
        str += QString("
");

        QString boundString;
        if (!ctrl->socketBound())
            boundString = QString("%1").arg(tr("No"));
        else
           boundString = QString("%1").arg(tr("Yes"));
        str += QString("%1: %2").arg(tr("Can receive node informations")).arg(boundString);
        str += QString("
");

        str += tr("Nodes discovered: ");
        str += QString("%1").arg(ctrl->getNodesList().size());
        str += QString("
");
        str += tr("Packets sent: ");
        str += QString("%1").arg(ctrl->getPacketSentNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

bool ArtNetPlugin::openOutput(quint32 output, quint32 universe)
{
    init();

    if (output >= (quint32)m_IOmapping.length())
        return false;

    qDebug() << "[ArtNet] Open output on address :" << m_IOmapping.at(output).address.ip().toString();

    // if the controller doesn't exist, create it
    if (m_IOmapping[output].controller == NULL)
    {
        ArtNetController *controller = new ArtNetController(m_IOmapping.at(output).interface,
                                                            m_IOmapping.at(output).address,
                                                            getUdpSocket(),
                                                            output, this);
        connect(controller, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)),
                this, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)));
        m_IOmapping[output].controller = controller;
    }

    m_IOmapping[output].controller->addUniverse(universe, ArtNetController::Output);
    addToMap(universe, output, Output);

    return true;
}

void ArtNetPlugin::closeOutput(quint32 output, quint32 universe)
{
    if (output >= (quint32)m_IOmapping.length())
        return;

    removeFromMap(output, universe, Output);
    ArtNetController *controller = m_IOmapping.at(output).controller;
    if (controller != NULL)
    {
        controller->removeUniverse(universe, ArtNetController::Output);
        if (controller->universesList().count() == 0)
        {
            delete m_IOmapping[output].controller;
            m_IOmapping[output].controller = NULL;
        }
    }
}

void ArtNetPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data)
{
    if (output >= (quint32)m_IOmapping.count())
        return;

    ArtNetController *controller = m_IOmapping.at(output).controller;
    if (controller != NULL)
        controller->sendDmx(universe, data);
}

/*************************************************************************
  * Inputs
  *************************************************************************/
QStringList ArtNetPlugin::inputs()
{
    QStringList list;
    int j = 0;

    init();

    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString("%1: %2").arg(j + 1).arg(line.address.ip().toString());
        j++;
    }
    return list;
}

bool ArtNetPlugin::openInput(quint32 input, quint32 universe)
{
    init();

    if (input >= (quint32)m_IOmapping.length())
        return false;

    // if the controller doesn't exist, create it.
    // We need to have only one input controller.
    if (m_IOmapping[input].controller == NULL)
    {
        ArtNetController *controller = new ArtNetController(m_IOmapping.at(input).interface,
                                                            m_IOmapping.at(input).address,
                                                            getUdpSocket(),
                                                            input, this);
        connect(controller, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)),
                this, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)));
        m_IOmapping[input].controller = controller;
    }

    m_IOmapping[input].controller->addUniverse(universe, ArtNetController::Input);
    addToMap(universe, input, Input);

    return true;
}

void ArtNetPlugin::closeInput(quint32 input, quint32 universe)
{
    if (input >= (quint32)m_IOmapping.length())
        return;

    removeFromMap(input, universe, Input);
    ArtNetController *controller = m_IOmapping.at(input).controller;
    if (controller != NULL)
    {
        controller->removeUniverse(universe, ArtNetController::Input);
        if (controller->universesList().count() == 0)
        {
            delete m_IOmapping[input].controller;
            m_IOmapping[input].controller = NULL;
        }
    }
}

QString ArtNetPlugin::inputInfo(quint32 input)
{
    if (input >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Input")).arg(inputs()[input]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(input).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Output)
        str += tr("Status: Not open");
    else
    {
        QString boundString;
        if (!ctrl->socketBound())
            boundString = QString("%1").arg(tr("Bind failed"));
        else
           boundString = QString("%1").arg(tr("Open"));
        str += QString("%1: %2").arg(tr("Status")).arg(boundString);
        str += QString("
");

        str += tr("Packets received: ");
        str += QString("%1").arg(ctrl->getPacketReceivedNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

/*********************************************************************
 * Configuration
 *********************************************************************/
void ArtNetPlugin::configure()
{
    ConfigureArtNet conf(this);
    conf.exec();
}

bool ArtNetPlugin::canConfigure()
{
    return true;
}

void ArtNetPlugin::setParameter(quint32 universe, quint32 line, Capability type,
                                QString name, QVariant value)
{
    if (line >= (quint32)m_IOmapping.length())
        return;

    ArtNetController *controller = m_IOmapping.at(line).controller;
    if (controller == NULL)
        return;

    // If the Controller parameter is restored to its default value,
    // unset the corresponding plugin parameter
    bool unset;

    if (type == Input)
    {
        if (name == ARTNET_INPUTUNI)
            unset = controller->setInputUniverse(universe, value.toUInt());
        else
        {
            qWarning() << Q_FUNC_INFO << name << "is not a valid ArtNet input parameter";
            return;
        }
    }
    else // if (type == Output)
    {
        if (name == ARTNET_OUTPUTIP)
            unset = controller->setOutputIPAddress(universe, value.toString());
        else if (name == ARTNET_OUTPUTUNI)
            unset = controller->setOutputUniverse(universe, value.toUInt());
        else if (name == ARTNET_TRANSMITMODE)
            unset = controller->setTransmissionMode(universe, ArtNetController::stringToTransmissionMode(value.toString()));
        else
        {
            qWarning() << Q_FUNC_INFO << name << "is not a valid ArtNet output parameter";
            return;
        }
    }

    if (unset)
        QLCIOPlugin::unSetParameter(universe, line, type, name);
    else
        QLCIOPlugin::setParameter(universe, line, type, name, value);
}

QList<ArtNetIO> ArtNetPlugin::getIOMapping()
{
    return m_IOmapping;
}

/*********************************************************************
 * ArtNet socket
 *********************************************************************/

QSharedPointer<QUdpSocket> ArtNetPlugin::getUdpSocket()
{
    // Is the socket already present ?
    QSharedPointer<QUdpSocket> udpSocket(m_udpSocket);
    if (udpSocket)
        return udpSocket;

    // Create a new socket
    udpSocket = QSharedPointer<QUdpSocket>(new QUdpSocket());
    m_udpSocket = udpSocket.toWeakRef();

    if (udpSocket->bind(ARTNET_PORT, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint))
    {
        connect(udpSocket.data(), SIGNAL(readyRead()),
                this, SLOT(slotReadyRead()));
    }
    else
    {
        qWarning() << "ArtNet: could not bind socket to address" << QString("0:%2").arg(ARTNET_PORT);
    }
    return udpSocket;
}

void ArtNetPlugin::slotReadyRead()
{
    QUdpSocket* udpSocket = qobject_cast<QUdpSocket*>(sender());
    Q_ASSERT(udpSocket != NULL);

    QByteArray datagram;
    QHostAddress senderAddress;
    while (udpSocket->hasPendingDatagrams())
    {
        datagram.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress);
        handlePacket(datagram, senderAddress);
    }
}

void ArtNetPlugin::handlePacket(QByteArray const& datagram, QHostAddress const& senderAddress)
{
    // A firts filter: look for a controller on the same subnet as the sender.
    // This allows having the same ArtNet Universe on 2 different network interfaces.
    foreach(ArtNetIO io, m_IOmapping)
    {
        if (senderAddress.isInSubnet(io.address.ip(), io.address.prefixLength()))
        {
            if (io.controller != NULL)
                io.controller->handlePacket(datagram, senderAddress);
            return;
        }
    }
    // Packet comming from another subnet. This is an unusual case.
    // We stop at the first controller that handles this packet.
    foreach(ArtNetIO io, m_IOmapping)
    {
        if (io.controller != NULL)
        {
            if (io.controller->handlePacket(datagram, senderAddress))
                break;
        }
    }
}

/*****************************************************************************
 * Plugin export
 ****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(artnetplugin, ArtNetPlugin)
#endif
<commit_msg>plugins/artnet: "information" is an uncountable word<commit_after>/*
  Q Light Controller Plus
  artnetplugin.cpp

  Copyright (c) Massimo Callegari

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include "artnetplugin.h"
#include "configureartnet.h"

#include <QSettings>
#include <QDebug>
#include <QMessageBox>

ArtNetPlugin::~ArtNetPlugin()
{
}

void ArtNetPlugin::init()
{
    foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces())
    {
        foreach (QNetworkAddressEntry entry, interface.addressEntries())
        {
            QHostAddress addr = entry.ip();
            if (addr.protocol() != QAbstractSocket::IPv6Protocol)
            {
                ArtNetIO tmpIO;
                tmpIO.interface = interface;
                tmpIO.address = entry;
                tmpIO.controller = NULL;

                bool alreadyInList = false;
                for(int j = 0; j < m_IOmapping.count(); j++)
                {
                    if (m_IOmapping.at(j).address == tmpIO.address)
                    {
                        alreadyInList = true;
                        break;
                    }
                }
                if (alreadyInList == false)
                {
                    m_IOmapping.append(tmpIO);
                }
            }
        }
    }
}

QString ArtNetPlugin::name()
{
    return QString("ArtNet");
}

int ArtNetPlugin::capabilities() const
{
    return QLCIOPlugin::Output | QLCIOPlugin::Input | QLCIOPlugin::Infinite;
}

QString ArtNetPlugin::pluginInfo()
{
    QString str;

    str += QString("");
    str += QString("");
    str += QString("<TITLE>%1</TITLE>").arg(name());
    str += QString("");
    str += QString("");

    str += QString("
");
    str += QString("
%1
").arg(name());
    str += tr("This plugin provides DMX output for devices supporting the ArtNet communication protocol.");
    str += QString("
");

    return str;
}

/*********************************************************************
 * Outputs
 *********************************************************************/
QStringList ArtNetPlugin::outputs()
{
    QStringList list;
    int j = 0;

    init();

    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString("%1: %2").arg(j + 1).arg(line.address.ip().toString());
        j++;
    }
    return list;
}

QString ArtNetPlugin::outputInfo(quint32 output)
{
    if (output >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Output")).arg(outputs()[output]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(output).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Input)
        str += tr("Status: Not open");
    else
    {
        str += tr("Status: Open");
        str += QString("
");

        QString boundString;
        if (!ctrl->socketBound())
            boundString = QString("%1").arg(tr("No"));
        else
           boundString = QString("%1").arg(tr("Yes"));
        str += QString("%1: %2").arg(tr("Can receive nodes information")).arg(boundString);
        str += QString("
");

        str += tr("Nodes discovered: ");
        str += QString("%1").arg(ctrl->getNodesList().size());
        str += QString("
");
        str += tr("Packets sent: ");
        str += QString("%1").arg(ctrl->getPacketSentNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

bool ArtNetPlugin::openOutput(quint32 output, quint32 universe)
{
    init();

    if (output >= (quint32)m_IOmapping.length())
        return false;

    qDebug() << "[ArtNet] Open output on address :" << m_IOmapping.at(output).address.ip().toString();

    // if the controller doesn't exist, create it
    if (m_IOmapping[output].controller == NULL)
    {
        ArtNetController *controller = new ArtNetController(m_IOmapping.at(output).interface,
                                                            m_IOmapping.at(output).address,
                                                            getUdpSocket(),
                                                            output, this);
        connect(controller, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)),
                this, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)));
        m_IOmapping[output].controller = controller;
    }

    m_IOmapping[output].controller->addUniverse(universe, ArtNetController::Output);
    addToMap(universe, output, Output);

    return true;
}

void ArtNetPlugin::closeOutput(quint32 output, quint32 universe)
{
    if (output >= (quint32)m_IOmapping.length())
        return;

    removeFromMap(output, universe, Output);
    ArtNetController *controller = m_IOmapping.at(output).controller;
    if (controller != NULL)
    {
        controller->removeUniverse(universe, ArtNetController::Output);
        if (controller->universesList().count() == 0)
        {
            delete m_IOmapping[output].controller;
            m_IOmapping[output].controller = NULL;
        }
    }
}

void ArtNetPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data)
{
    if (output >= (quint32)m_IOmapping.count())
        return;

    ArtNetController *controller = m_IOmapping.at(output).controller;
    if (controller != NULL)
        controller->sendDmx(universe, data);
}

/*************************************************************************
  * Inputs
  *************************************************************************/
QStringList ArtNetPlugin::inputs()
{
    QStringList list;
    int j = 0;

    init();

    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString("%1: %2").arg(j + 1).arg(line.address.ip().toString());
        j++;
    }
    return list;
}

bool ArtNetPlugin::openInput(quint32 input, quint32 universe)
{
    init();

    if (input >= (quint32)m_IOmapping.length())
        return false;

    // if the controller doesn't exist, create it.
    // We need to have only one input controller.
    if (m_IOmapping[input].controller == NULL)
    {
        ArtNetController *controller = new ArtNetController(m_IOmapping.at(input).interface,
                                                            m_IOmapping.at(input).address,
                                                            getUdpSocket(),
                                                            input, this);
        connect(controller, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)),
                this, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)));
        m_IOmapping[input].controller = controller;
    }

    m_IOmapping[input].controller->addUniverse(universe, ArtNetController::Input);
    addToMap(universe, input, Input);

    return true;
}

void ArtNetPlugin::closeInput(quint32 input, quint32 universe)
{
    if (input >= (quint32)m_IOmapping.length())
        return;

    removeFromMap(input, universe, Input);
    ArtNetController *controller = m_IOmapping.at(input).controller;
    if (controller != NULL)
    {
        controller->removeUniverse(universe, ArtNetController::Input);
        if (controller->universesList().count() == 0)
        {
            delete m_IOmapping[input].controller;
            m_IOmapping[input].controller = NULL;
        }
    }
}

QString ArtNetPlugin::inputInfo(quint32 input)
{
    if (input >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Input")).arg(inputs()[input]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(input).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Output)
        str += tr("Status: Not open");
    else
    {
        QString boundString;
        if (!ctrl->socketBound())
            boundString = QString("%1").arg(tr("Bind failed"));
        else
           boundString = QString("%1").arg(tr("Open"));
        str += QString("%1: %2").arg(tr("Status")).arg(boundString);
        str += QString("
");

        str += tr("Packets received: ");
        str += QString("%1").arg(ctrl->getPacketReceivedNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

/*********************************************************************
 * Configuration
 *********************************************************************/
void ArtNetPlugin::configure()
{
    ConfigureArtNet conf(this);
    conf.exec();
}

bool ArtNetPlugin::canConfigure()
{
    return true;
}

void ArtNetPlugin::setParameter(quint32 universe, quint32 line, Capability type,
                                QString name, QVariant value)
{
    if (line >= (quint32)m_IOmapping.length())
        return;

    ArtNetController *controller = m_IOmapping.at(line).controller;
    if (controller == NULL)
        return;

    // If the Controller parameter is restored to its default value,
    // unset the corresponding plugin parameter
    bool unset;

    if (type == Input)
    {
        if (name == ARTNET_INPUTUNI)
            unset = controller->setInputUniverse(universe, value.toUInt());
        else
        {
            qWarning() << Q_FUNC_INFO << name << "is not a valid ArtNet input parameter";
            return;
        }
    }
    else // if (type == Output)
    {
        if (name == ARTNET_OUTPUTIP)
            unset = controller->setOutputIPAddress(universe, value.toString());
        else if (name == ARTNET_OUTPUTUNI)
            unset = controller->setOutputUniverse(universe, value.toUInt());
        else if (name == ARTNET_TRANSMITMODE)
            unset = controller->setTransmissionMode(universe, ArtNetController::stringToTransmissionMode(value.toString()));
        else
        {
            qWarning() << Q_FUNC_INFO << name << "is not a valid ArtNet output parameter";
            return;
        }
    }

    if (unset)
        QLCIOPlugin::unSetParameter(universe, line, type, name);
    else
        QLCIOPlugin::setParameter(universe, line, type, name, value);
}

QList<ArtNetIO> ArtNetPlugin::getIOMapping()
{
    return m_IOmapping;
}

/*********************************************************************
 * ArtNet socket
 *********************************************************************/

QSharedPointer<QUdpSocket> ArtNetPlugin::getUdpSocket()
{
    // Is the socket already present ?
    QSharedPointer<QUdpSocket> udpSocket(m_udpSocket);
    if (udpSocket)
        return udpSocket;

    // Create a new socket
    udpSocket = QSharedPointer<QUdpSocket>(new QUdpSocket());
    m_udpSocket = udpSocket.toWeakRef();

    if (udpSocket->bind(ARTNET_PORT, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint))
    {
        connect(udpSocket.data(), SIGNAL(readyRead()),
                this, SLOT(slotReadyRead()));
    }
    else
    {
        qWarning() << "ArtNet: could not bind socket to address" << QString("0:%2").arg(ARTNET_PORT);
    }
    return udpSocket;
}

void ArtNetPlugin::slotReadyRead()
{
    QUdpSocket* udpSocket = qobject_cast<QUdpSocket*>(sender());
    Q_ASSERT(udpSocket != NULL);

    QByteArray datagram;
    QHostAddress senderAddress;
    while (udpSocket->hasPendingDatagrams())
    {
        datagram.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress);
        handlePacket(datagram, senderAddress);
    }
}

void ArtNetPlugin::handlePacket(QByteArray const& datagram, QHostAddress const& senderAddress)
{
    // A firts filter: look for a controller on the same subnet as the sender.
    // This allows having the same ArtNet Universe on 2 different network interfaces.
    foreach(ArtNetIO io, m_IOmapping)
    {
        if (senderAddress.isInSubnet(io.address.ip(), io.address.prefixLength()))
        {
            if (io.controller != NULL)
                io.controller->handlePacket(datagram, senderAddress);
            return;
        }
    }
    // Packet comming from another subnet. This is an unusual case.
    // We stop at the first controller that handles this packet.
    foreach(ArtNetIO io, m_IOmapping)
    {
        if (io.controller != NULL)
        {
            if (io.controller->handlePacket(datagram, senderAddress))
                break;
        }
    }
}

/*****************************************************************************
 * Plugin export
 ****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(artnetplugin, ArtNetPlugin)
#endif
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2006 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//$Id: postgisfs.cc 34 2005-04-04 13:27:23Z pavlenko $

#include <boost/lexical_cast.hpp>
#include <boost/scoped_array.hpp>
#include <boost/algorithm/string.hpp>
#include <mapnik/global.hpp>
#include <mapnik/wkb.hpp>
#include <mapnik/unicode.hpp>
#include "postgis.hpp"
#include <sstream>

using boost::lexical_cast;
using boost::bad_lexical_cast;
using boost::trim_copy;
using std::string;
using mapnik::Feature;
using mapnik::geometry2d;
using mapnik::byte;
using mapnik::geometry_utils;

std::string numeric2string(const char* buf)
{
   int16_t ndigits = int2net(buf);
   int16_t weight  = int2net(buf+2);
   int16_t sign    = int2net(buf+4);
   int16_t dscale  = int2net(buf+6);

   boost::scoped_array<int16_t> digits(new int16_t[ndigits]);
   for (int n=0; n < ndigits ;++n)
   {
      digits[n] = int2net(buf+8+n*2);
   }

   std::ostringstream ss;

   if (sign == 0x4000) ss << "-";

   int i = std::max(weight,int16_t(0));
   int d = 0;
   while ( i >= 0)
   {
      if (i <= weight && d < ndigits)
         ss <<  digits[d++];
      else
         ss <<  '0';
      i--;
   }
   if (dscale > 0)
   {
      ss << '.';
      while ( i >= -dscale)
      {
         if (i <= weight && d < ndigits)
            ss <<  digits[d++];
         i--;
      }
   }
   return ss.str();
}

postgis_featureset::postgis_featureset(boost::shared_ptr<IResultSet> const& rs,
                                       std::string const& encoding,
                                       bool multiple_geometries,
                                       unsigned num_attrs=0)
    : rs_(rs),
      multiple_geometries_(multiple_geometries),
      num_attrs_(num_attrs),
      tr_(new transcoder(encoding)),
      totalGeomSize_(0),
      count_(0)  {}

feature_ptr postgis_featureset::next()
{
    if (rs_->next())
    {
        feature_ptr feature(new Feature(count_));
        int size=rs_->getFieldLength(0);
        const char *data = rs_->getValue(0);
        geometry_utils::from_wkb(*feature,data,size,multiple_geometries_);
        totalGeomSize_+=size;

        for (unsigned pos=1;pos<num_attrs_+1;++pos)
        {
           std::string name = rs_->getFieldName(pos);

           if (!rs_->isNull(pos))
           {
              const char* buf=rs_->getValue(pos);
              int oid = rs_->getTypeOID(pos);

              if (oid==16) //bool
              {
                 boost::put(*feature,name,buf[0] != 0);
              }
              else if (oid==23) //int4
              {
                 int val = int4net(buf);
                 boost::put(*feature,name,val);
              }
              else if (oid==21) //int2
              {
                 int val = int2net(buf);
                 boost::put(*feature,name,val);
              }
              else if (oid==20) //int8/BigInt
              {
                 int val = int8net(buf);
                 boost::put(*feature,name,val);
              }
              else if (oid == 700) // float4
              {
                 float val;
                 float4net(val,buf);
                 boost::put(*feature,name,val);
              }
              else if (oid == 701) // float8
              {
                 double val;
                 float8net(val,buf);
                 boost::put(*feature,name,val);
              }
              else if (oid==25 || oid==1043) // text or varchar
              {
                 UnicodeString ustr = tr_->transcode(buf);
                 boost::put(*feature,name,ustr);
              }
              else if (oid==1042)
              {
                 UnicodeString ustr = tr_->transcode(trim_copy(string(buf)).c_str()); // bpchar
                 boost::put(*feature,name,ustr);
              }
              else if (oid == 1700) // numeric
              {
                 std::string str = numeric2string(buf);
                 try
                 {
                    double val = boost::lexical_cast<double>(str);
                    boost::put(*feature,name,val);
                 }
                 catch (boost::bad_lexical_cast & ex)
                 {
                    std::clog << ex.what() << "\n";
                 }
              }
              else
              {
#ifdef MAPNIK_DEBUG
                 std::clog << "uknown OID = " << oid << " FIXME \n";
#endif
              }
           }
        }
        ++count_;
        return feature;
    }
    else
    {
        rs_->close();
        return feature_ptr();
    }
}

postgis_featureset::~postgis_featureset()
{
    rs_->close();
}
<commit_msg>Fix numeric2string() to insert the correct number of 0's when building the string. This problem was reported as: [Mapnik-users] unusual problem with Mapnik output.<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2006 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//$Id: postgisfs.cc 34 2005-04-04 13:27:23Z pavlenko $

#include <boost/lexical_cast.hpp>
#include <boost/scoped_array.hpp>
#include <boost/algorithm/string.hpp>
#include <mapnik/global.hpp>
#include <mapnik/wkb.hpp>
#include <mapnik/unicode.hpp>
#include "postgis.hpp"
#include <sstream>

using boost::lexical_cast;
using boost::bad_lexical_cast;
using boost::trim_copy;
using std::string;
using mapnik::Feature;
using mapnik::geometry2d;
using mapnik::byte;
using mapnik::geometry_utils;

std::string numeric2string(const char* buf)
{
   int16_t ndigits = int2net(buf);
   int16_t weight  = int2net(buf+2);
   int16_t sign    = int2net(buf+4);
   int16_t dscale  = int2net(buf+6);

   boost::scoped_array<int16_t> digits(new int16_t[ndigits]);
   for (int n=0; n < ndigits ;++n)
   {
      digits[n] = int2net(buf+8+n*2);
   }

   std::ostringstream ss;

   if (sign == 0x4000) ss << "-";

   int i = std::max(weight,int16_t(0));
   int d = 0;

   // Each numeric "digit" is actually a value between 0000 and 9999 stored in a 16 bit field.
   // For example, the number 1234567809990001 is stored as four digits: [1234] [5678] [999] [1].
   // Note that the last two digits show that the leading 0's are lost when the number is split.
   // We must be careful to re-insert these 0's when building the string.

   while ( i >= 0)
   {
      if (i <= weight && d < ndigits) {
         // All digits after the first must be padded to make the field 4 characters long
         if (d != 0) {
            switch(digits[d]) {
            case 0 ... 9:
               ss << "000"; // 0000 - 0009
               break;
            case 10 ... 99:
               ss << "00";  // 0010 - 0099
               break;
            case 100 ... 999:
               ss << "0";   // 0100 - 0999
               break;
            }
         }
         ss <<  digits[d++];
     } else
        if (d == 0)
           ss <<  "0";
        else
           ss <<  "0000";
       i--;
   }
   if (dscale > 0)
   {
      ss << '.';
      while ( i >= -dscale)
      {
         if (i <= weight && d < ndigits) {
            // All the digits following the decimal point must be padded
            switch(digits[d]) {
            case 0 ... 9:
               ss << "000";
               break;
            case 10 ... 99:
               ss << "00";
               break;
            case 100 ... 999:
               ss << "0";
               break;
            }
            ss <<  digits[d++];
         }
         i--;
      }
   }
   return ss.str();
}

postgis_featureset::postgis_featureset(boost::shared_ptr<IResultSet> const& rs,
                                       std::string const& encoding,
                                       bool multiple_geometries,
                                       unsigned num_attrs=0)
    : rs_(rs),
      multiple_geometries_(multiple_geometries),
      num_attrs_(num_attrs),
      tr_(new transcoder(encoding)),
      totalGeomSize_(0),
      count_(0)  {}

feature_ptr postgis_featureset::next()
{
    if (rs_->next())
    {
        feature_ptr feature(new Feature(count_));
        int size=rs_->getFieldLength(0);
        const char *data = rs_->getValue(0);
        geometry_utils::from_wkb(*feature,data,size,multiple_geometries_);
        totalGeomSize_+=size;

        for (unsigned pos=1;pos<num_attrs_+1;++pos)
        {
           std::string name = rs_->getFieldName(pos);

           if (!rs_->isNull(pos))
           {
              const char* buf=rs_->getValue(pos);
              int oid = rs_->getTypeOID(pos);

              if (oid==16) //bool
              {
                 boost::put(*feature,name,buf[0] != 0);
              }
              else if (oid==23) //int4
              {
                 int val = int4net(buf);
                 boost::put(*feature,name,val);
              }
              else if (oid==21) //int2
              {
                 int val = int2net(buf);
                 boost::put(*feature,name,val);
              }
              else if (oid==20) //int8/BigInt
              {
                 int val = int8net(buf);
                 boost::put(*feature,name,val);
              }
              else if (oid == 700) // float4
              {
                 float val;
                 float4net(val,buf);
                 boost::put(*feature,name,val);
              }
              else if (oid == 701) // float8
              {
                 double val;
                 float8net(val,buf);
                 boost::put(*feature,name,val);
              }
              else if (oid==25 || oid==1043) // text or varchar
              {
                 UnicodeString ustr = tr_->transcode(buf);
                 boost::put(*feature,name,ustr);
              }
              else if (oid==1042)
              {
                 UnicodeString ustr = tr_->transcode(trim_copy(string(buf)).c_str()); // bpchar
                 boost::put(*feature,name,ustr);
              }
              else if (oid == 1700) // numeric
              {
                 std::string str = numeric2string(buf);
                 try
                 {
                    double val = boost::lexical_cast<double>(str);
                    boost::put(*feature,name,val);
                 }
                 catch (boost::bad_lexical_cast & ex)
                 {
                    std::clog << ex.what() << "\n";
                 }
              }
              else
              {
#ifdef MAPNIK_DEBUG
                 std::clog << "uknown OID = " << oid << " FIXME \n";
#endif
              }
           }
        }
        ++count_;
        return feature;
    }
    else
    {
        rs_->close();
        return feature_ptr();
    }
}

postgis_featureset::~postgis_featureset()
{
    rs_->close();
}
<|endoftext|>


<commit_before>// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
#include "scrollwidgets.hh"
#include "window.hh"
#include "factory.hh"

namespace Rapicorn {

// == ScrollAreaImpl ==
ScrollAreaImpl::ScrollAreaImpl() :
  hadjustment_ (NULL), vadjustment_ (NULL)
{}

Adjustment&
ScrollAreaImpl::hadjustment ()
{
  if (!hadjustment_)
    hadjustment_ = Adjustment::create (0, 0, 1, 0.01, 0.2);
  return *hadjustment_;
}

Adjustment&
ScrollAreaImpl::vadjustment ()
{
  if (!vadjustment_)
    vadjustment_ = Adjustment::create (0, 0, 1, 0.01, 0.2);
  return *vadjustment_;
}

Adjustment*
ScrollAreaImpl::get_adjustment (AdjustmentSourceType adj_source, const String &name)
{
  switch (adj_source)
    {
    case AdjustmentSourceType::ANCESTRY_HORIZONTAL:
      return &hadjustment();
    case AdjustmentSourceType::ANCESTRY_VERTICAL:
      return &vadjustment();
    default:
      return NULL;
    }
}

double
ScrollAreaImpl::x_offset ()
{
  return round (hadjustment().value());
}

double
ScrollAreaImpl::y_offset ()
{
  return round (vadjustment().value());
}

void
ScrollAreaImpl::scroll_to (double x, double y)
{
  hadjustment().freeze();
  vadjustment().freeze();
  hadjustment_->value (round (x));
  vadjustment_->value (round (y));
  hadjustment_->constrain();
  vadjustment_->constrain();
  hadjustment_->thaw();
  vadjustment_->thaw();
}

static const WidgetFactory<ScrollAreaImpl> scroll_area_factory ("Rapicorn::ScrollArea");

// == ScrollPortImpl ==
ScrollPortImpl::ScrollPortImpl() :
  hadjustment_ (NULL), vadjustment_ (NULL), conid_hadjustment_ (0), conid_vadjustment_ (0), fix_id_ (0)
{}

void
ScrollPortImpl::hierarchy_changed (WidgetImpl *old_toplevel)
{
  if (hadjustment_ && conid_hadjustment_)
    hadjustment_->sig_value_changed() -= conid_hadjustment_;
  hadjustment_ = NULL;
  conid_hadjustment_ = 0;
  if (vadjustment_ && conid_vadjustment_)
    vadjustment_->sig_value_changed() -= conid_vadjustment_;
  vadjustment_ = NULL;
  conid_vadjustment_ = 0;
  this->ViewportImpl::hierarchy_changed (old_toplevel);
  if (anchored())
    {
      find_adjustments (AdjustmentSourceType::ANCESTRY_HORIZONTAL, &hadjustment_,
                        AdjustmentSourceType::ANCESTRY_VERTICAL, &vadjustment_);
      if (hadjustment_)
        conid_hadjustment_ = hadjustment_->sig_value_changed() += Aida::slot (*this, &ScrollPortImpl::adjustment_changed);
      if (vadjustment_)
        conid_vadjustment_ = vadjustment_->sig_value_changed() += Aida::slot (*this, &ScrollPortImpl::adjustment_changed);
    }
}

void
ScrollPortImpl::fix_adjustments ()
{
  assert_return (fix_id_ != 0);
  fix_id_ = 0;
  if (!has_visible_child())
    return;
  /* size allocation negotiation may re-allocate the widget several times with new tentative sizes.
   * since some combinations could lead to the adjustments contraining the current scroll values
   * undesirably and loosing the last scroll position, we fix up the adjustments once the dust settled.
   */
  WidgetImpl &child = get_child();
  const Allocation area = child.allocation();
  const double small_step = 10;
  if (hadjustment_)
    {
      hadjustment_->freeze();
      hadjustment_->lower (0);
      hadjustment_->upper (area.width);
      double d, w = allocation().width, l = min (w, 1);
      hadjustment_->page (w);
      hadjustment_->step_increment (min (small_step, round (hadjustment_->page() / 10)));
      hadjustment_->page_increment (max (small_step, round (hadjustment_->page() / 3)));
      w = MIN (hadjustment_->page(), area.width);
      d = hadjustment_->step_increment ();
      hadjustment_->step_increment (CLAMP (d, l, w));
      d = hadjustment_->page_increment ();
      hadjustment_->page_increment (CLAMP (d, l, w));
    }
  if (vadjustment_)
    {
      vadjustment_->freeze();
      vadjustment_->lower (0);
      vadjustment_->upper (area.height);
      double d, h = allocation().height, l = min (h, 1);
      vadjustment_->page (h);
      vadjustment_->step_increment (min (small_step, round (vadjustment_->page() / 10)));
      vadjustment_->page_increment (max (small_step, round (vadjustment_->page() / 3)));
      h = MIN (vadjustment_->page(), area.height);
      d = vadjustment_->step_increment ();
      vadjustment_->step_increment (CLAMP (d, l, h));
      d = vadjustment_->page_increment ();
      vadjustment_->page_increment (CLAMP (d, l, h));
    }
  if (hadjustment_)
    hadjustment_->constrain();
  if (vadjustment_)
    vadjustment_->constrain();
  const int xoffset = hadjustment_ ? iround (hadjustment_->value()) : 0;
  const int yoffset = vadjustment_ ? iround (vadjustment_->value()) : 0;
  scroll_offsets (xoffset, yoffset); // syncronize offsets, before adjustment_changed() kicks in
  if (hadjustment_)
    hadjustment_->thaw();
  if (vadjustment_)
    vadjustment_->thaw();
}

void
ScrollPortImpl::size_allocate (Allocation area, bool changed)
{
  if (!has_visible_child())
    return;
  WidgetImpl &child = get_child();
  const Requisition rq = child.requisition();
  Allocation child_area;
  child_area.x = 0; // 0-offset
  child_area.y = 0; // 0-offset
  if (child.hexpand())
    child_area.width = max (rq.width, area.width);
  else
    child_area.width = rq.width;
  if (child.vexpand())
    child_area.height = max (rq.height, area.height);
  else
    child_area.height = rq.height;
  const Allocation clip_area = child_area;
  child_area = layout_child (child, child_area);
  child.set_allocation (child_area, &clip_area); // clipping is done in render instead
  if (!fix_id_)
    {
      WindowImpl *window = get_window();
      EventLoop *loop = window ? window->get_loop() : NULL;
      if (loop)
        fix_id_ = loop->exec_callback (Aida::slot (*this, &ScrollPortImpl::fix_adjustments), EventLoop::PRIORITY_NOW);
    }
}

void
ScrollPortImpl::adjustment_changed()
{
  const int xoffset = hadjustment_ ? iround (hadjustment_->value()) : 0;
  const int yoffset = vadjustment_ ? iround (vadjustment_->value()) : 0;
  scroll_offsets (xoffset, yoffset);
}

void
ScrollPortImpl::set_focus_child (WidgetImpl *widget)
{
  ViewportImpl::set_focus_child (widget);
  WidgetImpl *fchild = get_focus_child();
  if (!fchild)
    return;
  WindowImpl *rt = get_window();
  if (!rt)
    return;
  WidgetImpl *const rfwidget = rt->get_focus();
  WidgetImpl *fwidget = rfwidget;
  if (!fwidget)
    return;
  /* list focus widgets between focus_widget and our immediate child */
  std::list<WidgetImpl*> fwidgets;
  while (fwidget)
    {
      fwidgets.push_back (fwidget);
      if (fwidget == fchild)
        break;
      fwidget = fwidget->parent();
    }
  /* find the first focus descendant that fits the scroll area */
  fwidget = rfwidget; /* fallback to innermost focus widget */
  const Rect area = allocation();
  for (WidgetImpl *widget : fwidgets)
    {
      Rect a = widget->allocation();
      if (!translate_from (*widget, 1, &a))
        {
          fwidget = NULL; // no geographic descendant
          break;
        }
      if (a.width <= area.width && a.height <= area.height)
        {
          fwidget = widget;
          break;
        }
    }
  if (!fwidget)
    return;
  scroll_to_child (*fwidget);
}

void
ScrollPortImpl::scroll_to_child (WidgetImpl &widget)
{
  const Rect area = allocation();
  /* adjust scroll area to widget's area */
  Rect farea = widget.allocation();
  if (!translate_from (widget, 1, &farea))
    return;           // not geographic descendant
  if (0)
    printerr ("scroll-focus: area=%s farea=%s child=%p (%s)\n",
              area.string().c_str(), farea.string().c_str(), &widget,
              widget.allocation().string().c_str());
  /* calc new scroll position, giving precedence to lower left */
  double deltax = 0, deltay = 0;
  if (farea.upper_x() > area.upper_x() + deltax)
    deltax += farea.upper_x() - (area.upper_x() + deltax);
  if (farea.x < area.x + deltax)
    deltax += farea.x - (area.x + deltax);
  if (farea.upper_y() > area.upper_y() + deltay)
    deltay += farea.upper_y() - (area.upper_y() + deltay);
  if (farea.y < area.y + deltay)
    deltay += farea.y - (area.y + deltay);
  /* scroll to new position */
  hadjustment_->freeze();
  vadjustment_->freeze();
  if (deltax)
    hadjustment_->value (hadjustment_->value() + deltax);
  if (deltay)
    vadjustment_->value (vadjustment_->value() + deltay);
  hadjustment_->constrain();
  vadjustment_->constrain();
  hadjustment_->thaw();
  vadjustment_->thaw();
}

bool
ScrollPortImpl::scroll (EventType scroll_dir)
{
  switch (scroll_dir)
    {
    case SCROLL_UP:           return vadjustment_ ? vadjustment_->move (MOVE_STEP_BACKWARD) : false;
    case SCROLL_LEFT:         return hadjustment_ ? hadjustment_->move (MOVE_STEP_BACKWARD) : false;
    case SCROLL_DOWN:         return vadjustment_ ? vadjustment_->move (MOVE_STEP_FORWARD) : false;
    case SCROLL_RIGHT:        return hadjustment_ ? hadjustment_->move (MOVE_STEP_FORWARD) : false;
    default:                  ;
    }
  return false;
}

const CommandList&
ScrollPortImpl::list_commands ()
{
  static CommandP commands[] = {
    MakeNamedCommand (ScrollPortImpl, "scroll-up",    _("Scroll upwards"),    scroll, SCROLL_UP),
    MakeNamedCommand (ScrollPortImpl, "scroll-left",  _("Scroll leftwards"),  scroll, SCROLL_LEFT),
    MakeNamedCommand (ScrollPortImpl, "scroll-down",  _("Scroll downwards"),  scroll, SCROLL_DOWN),
    MakeNamedCommand (ScrollPortImpl, "scroll-right", _("Scroll rightwards"), scroll, SCROLL_RIGHT),
  };
  static const CommandList command_list (commands, ViewportImpl::list_commands());
  return command_list;
}

bool
ScrollPortImpl::handle_event (const Event &event)
{
  bool handled = false;
  switch (event.type)
    {
    case SCROLL_UP:           handled = exec_command ("scroll-up");           break;
    case SCROLL_LEFT:         handled = exec_command ("scroll-left");         break;
    case SCROLL_DOWN:         handled = exec_command ("scroll-down");         break;
    case SCROLL_RIGHT:        handled = exec_command ("scroll-right");        break;
    default:                  break;
    }
  return handled;
}

void
ScrollPortImpl::reset (ResetMode mode)
{}

ScrollPortImpl::~ScrollPortImpl()
{
  clear_exec (&fix_id_);
}

static const WidgetFactory<ScrollPortImpl> scroll_port_factory ("Rapicorn::ScrollPort");

} // Rapicorn
<commit_msg>UI: avoid scroll adjustment jumps during size negotiation<commit_after>// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
#include "scrollwidgets.hh"
#include "window.hh"
#include "factory.hh"

namespace Rapicorn {

// == ScrollAreaImpl ==
ScrollAreaImpl::ScrollAreaImpl() :
  hadjustment_ (NULL), vadjustment_ (NULL)
{}

Adjustment&
ScrollAreaImpl::hadjustment ()
{
  if (!hadjustment_)
    hadjustment_ = Adjustment::create (0, 0, 1, 0.01, 0.2);
  return *hadjustment_;
}

Adjustment&
ScrollAreaImpl::vadjustment ()
{
  if (!vadjustment_)
    vadjustment_ = Adjustment::create (0, 0, 1, 0.01, 0.2);
  return *vadjustment_;
}

Adjustment*
ScrollAreaImpl::get_adjustment (AdjustmentSourceType adj_source, const String &name)
{
  switch (adj_source)
    {
    case AdjustmentSourceType::ANCESTRY_HORIZONTAL:
      return &hadjustment();
    case AdjustmentSourceType::ANCESTRY_VERTICAL:
      return &vadjustment();
    default:
      return NULL;
    }
}

double
ScrollAreaImpl::x_offset ()
{
  return round (hadjustment().value());
}

double
ScrollAreaImpl::y_offset ()
{
  return round (vadjustment().value());
}

void
ScrollAreaImpl::scroll_to (double x, double y)
{
  hadjustment().freeze();
  vadjustment().freeze();
  hadjustment_->value (round (x));
  vadjustment_->value (round (y));
  hadjustment_->constrain();
  vadjustment_->constrain();
  hadjustment_->thaw();
  vadjustment_->thaw();
}

static const WidgetFactory<ScrollAreaImpl> scroll_area_factory ("Rapicorn::ScrollArea");

// == ScrollPortImpl ==
ScrollPortImpl::ScrollPortImpl() :
  hadjustment_ (NULL), vadjustment_ (NULL), conid_hadjustment_ (0), conid_vadjustment_ (0), fix_id_ (0)
{}

void
ScrollPortImpl::hierarchy_changed (WidgetImpl *old_toplevel)
{
  if (hadjustment_ && conid_hadjustment_)
    hadjustment_->sig_value_changed() -= conid_hadjustment_;
  hadjustment_ = NULL;
  conid_hadjustment_ = 0;
  if (vadjustment_ && conid_vadjustment_)
    vadjustment_->sig_value_changed() -= conid_vadjustment_;
  vadjustment_ = NULL;
  conid_vadjustment_ = 0;
  this->ViewportImpl::hierarchy_changed (old_toplevel);
  if (anchored())
    {
      find_adjustments (AdjustmentSourceType::ANCESTRY_HORIZONTAL, &hadjustment_,
                        AdjustmentSourceType::ANCESTRY_VERTICAL, &vadjustment_);
      if (hadjustment_)
        conid_hadjustment_ = hadjustment_->sig_value_changed() += Aida::slot (*this, &ScrollPortImpl::adjustment_changed);
      if (vadjustment_)
        conid_vadjustment_ = vadjustment_->sig_value_changed() += Aida::slot (*this, &ScrollPortImpl::adjustment_changed);
    }
}

void
ScrollPortImpl::fix_adjustments ()
{
  assert_return (fix_id_ != 0);
  fix_id_ = 0;
  if (!has_visible_child() || test_any_flag (INVALID_REQUISITION | INVALID_ALLOCATION))
    return;
  /* size allocation negotiation may re-allocate the widget several times with new tentative sizes.
   * since some combinations could lead to the adjustments contraining the current scroll values
   * undesirably and loosing the last scroll position, we fix up the adjustments once the dust settled.
   */
  WidgetImpl &child = get_child();
  const Allocation area = child.allocation();
  const double small_step = 10;
  if (hadjustment_)
    {
      hadjustment_->freeze();
      hadjustment_->lower (0);
      hadjustment_->upper (area.width);
      double d, w = allocation().width, l = min (w, 1);
      hadjustment_->page (w);
      hadjustment_->step_increment (min (small_step, round (hadjustment_->page() / 10)));
      hadjustment_->page_increment (max (small_step, round (hadjustment_->page() / 3)));
      w = MIN (hadjustment_->page(), area.width);
      d = hadjustment_->step_increment ();
      hadjustment_->step_increment (CLAMP (d, l, w));
      d = hadjustment_->page_increment ();
      hadjustment_->page_increment (CLAMP (d, l, w));
    }
  if (vadjustment_)
    {
      vadjustment_->freeze();
      vadjustment_->lower (0);
      vadjustment_->upper (area.height);
      double d, h = allocation().height, l = min (h, 1);
      vadjustment_->page (h);
      vadjustment_->step_increment (min (small_step, round (vadjustment_->page() / 10)));
      vadjustment_->page_increment (max (small_step, round (vadjustment_->page() / 3)));
      h = MIN (vadjustment_->page(), area.height);
      d = vadjustment_->step_increment ();
      vadjustment_->step_increment (CLAMP (d, l, h));
      d = vadjustment_->page_increment ();
      vadjustment_->page_increment (CLAMP (d, l, h));
    }
  if (hadjustment_)
    hadjustment_->constrain();
  if (vadjustment_)
    vadjustment_->constrain();
  const int xoffset = hadjustment_ ? iround (hadjustment_->value()) : 0;
  const int yoffset = vadjustment_ ? iround (vadjustment_->value()) : 0;
  scroll_offsets (xoffset, yoffset); // syncronize offsets, before adjustment_changed() kicks in
  if (hadjustment_)
    hadjustment_->thaw();
  if (vadjustment_)
    vadjustment_->thaw();
}

void
ScrollPortImpl::size_allocate (Allocation area, bool changed)
{
  if (!has_visible_child())
    return;
  WidgetImpl &child = get_child();
  const Requisition rq = child.requisition();
  Allocation child_area;
  child_area.x = 0; // 0-offset
  child_area.y = 0; // 0-offset
  if (child.hexpand())
    child_area.width = max (rq.width, area.width);
  else
    child_area.width = rq.width;
  if (child.vexpand())
    child_area.height = max (rq.height, area.height);
  else
    child_area.height = rq.height;
  const Allocation clip_area = child_area;
  child_area = layout_child (child, child_area);
  child.set_allocation (child_area, &clip_area); // clipping is done in render instead
  if (!fix_id_)
    {
      WindowImpl *window = get_window();
      EventLoop *loop = window ? window->get_loop() : NULL;
      if (loop)
        fix_id_ = loop->exec_callback (Aida::slot (*this, &ScrollPortImpl::fix_adjustments), EventLoop::PRIORITY_NOW);
    }
}

void
ScrollPortImpl::adjustment_changed()
{
  const int xoffset = hadjustment_ ? iround (hadjustment_->value()) : 0;
  const int yoffset = vadjustment_ ? iround (vadjustment_->value()) : 0;
  scroll_offsets (xoffset, yoffset);
}

void
ScrollPortImpl::set_focus_child (WidgetImpl *widget)
{
  ViewportImpl::set_focus_child (widget);
  WidgetImpl *fchild = get_focus_child();
  if (!fchild)
    return;
  WindowImpl *rt = get_window();
  if (!rt)
    return;
  WidgetImpl *const rfwidget = rt->get_focus();
  WidgetImpl *fwidget = rfwidget;
  if (!fwidget)
    return;
  /* list focus widgets between focus_widget and our immediate child */
  std::list<WidgetImpl*> fwidgets;
  while (fwidget)
    {
      fwidgets.push_back (fwidget);
      if (fwidget == fchild)
        break;
      fwidget = fwidget->parent();
    }
  /* find the first focus descendant that fits the scroll area */
  fwidget = rfwidget; /* fallback to innermost focus widget */
  const Rect area = allocation();
  for (WidgetImpl *widget : fwidgets)
    {
      Rect a = widget->allocation();
      if (!translate_from (*widget, 1, &a))
        {
          fwidget = NULL; // no geographic descendant
          break;
        }
      if (a.width <= area.width && a.height <= area.height)
        {
          fwidget = widget;
          break;
        }
    }
  if (!fwidget)
    return;
  scroll_to_child (*fwidget);
}

void
ScrollPortImpl::scroll_to_child (WidgetImpl &widget)
{
  const Rect area = allocation();
  /* adjust scroll area to widget's area */
  Rect farea = widget.allocation();
  if (!translate_from (widget, 1, &farea))
    return;           // not geographic descendant
  if (0)
    printerr ("scroll-focus: area=%s farea=%s child=%p (%s)\n",
              area.string().c_str(), farea.string().c_str(), &widget,
              widget.allocation().string().c_str());
  /* calc new scroll position, giving precedence to lower left */
  double deltax = 0, deltay = 0;
  if (farea.upper_x() > area.upper_x() + deltax)
    deltax += farea.upper_x() - (area.upper_x() + deltax);
  if (farea.x < area.x + deltax)
    deltax += farea.x - (area.x + deltax);
  if (farea.upper_y() > area.upper_y() + deltay)
    deltay += farea.upper_y() - (area.upper_y() + deltay);
  if (farea.y < area.y + deltay)
    deltay += farea.y - (area.y + deltay);
  /* scroll to new position */
  hadjustment_->freeze();
  vadjustment_->freeze();
  if (deltax)
    hadjustment_->value (hadjustment_->value() + deltax);
  if (deltay)
    vadjustment_->value (vadjustment_->value() + deltay);
  hadjustment_->constrain();
  vadjustment_->constrain();
  hadjustment_->thaw();
  vadjustment_->thaw();
}

bool
ScrollPortImpl::scroll (EventType scroll_dir)
{
  switch (scroll_dir)
    {
    case SCROLL_UP:           return vadjustment_ ? vadjustment_->move (MOVE_STEP_BACKWARD) : false;
    case SCROLL_LEFT:         return hadjustment_ ? hadjustment_->move (MOVE_STEP_BACKWARD) : false;
    case SCROLL_DOWN:         return vadjustment_ ? vadjustment_->move (MOVE_STEP_FORWARD) : false;
    case SCROLL_RIGHT:        return hadjustment_ ? hadjustment_->move (MOVE_STEP_FORWARD) : false;
    default:                  ;
    }
  return false;
}

const CommandList&
ScrollPortImpl::list_commands ()
{
  static CommandP commands[] = {
    MakeNamedCommand (ScrollPortImpl, "scroll-up",    _("Scroll upwards"),    scroll, SCROLL_UP),
    MakeNamedCommand (ScrollPortImpl, "scroll-left",  _("Scroll leftwards"),  scroll, SCROLL_LEFT),
    MakeNamedCommand (ScrollPortImpl, "scroll-down",  _("Scroll downwards"),  scroll, SCROLL_DOWN),
    MakeNamedCommand (ScrollPortImpl, "scroll-right", _("Scroll rightwards"), scroll, SCROLL_RIGHT),
  };
  static const CommandList command_list (commands, ViewportImpl::list_commands());
  return command_list;
}

bool
ScrollPortImpl::handle_event (const Event &event)
{
  bool handled = false;
  switch (event.type)
    {
    case SCROLL_UP:           handled = exec_command ("scroll-up");           break;
    case SCROLL_LEFT:         handled = exec_command ("scroll-left");         break;
    case SCROLL_DOWN:         handled = exec_command ("scroll-down");         break;
    case SCROLL_RIGHT:        handled = exec_command ("scroll-right");        break;
    default:                  break;
    }
  return handled;
}

void
ScrollPortImpl::reset (ResetMode mode)
{}

ScrollPortImpl::~ScrollPortImpl()
{
  clear_exec (&fix_id_);
}

static const WidgetFactory<ScrollPortImpl> scroll_port_factory ("Rapicorn::ScrollPort");

} // Rapicorn
<|endoftext|>