<commit_before>#include <algorithm>
#include "player.h"
#include "matrix.h"
#include "math.h"

namespace konstructs {

    using namespace Eigen;
    using nonstd::nullopt;

    static float CAMERA_OFFSET = 0.5f;
    static Vector3f CAMERA_OFFSET_VECTOR = Vector3f(0, CAMERA_OFFSET, 0);

    Player::Player(const int _id, const Vector3f _position, const float _rx,
                   const float _ry):
        id(_id), position(_position), mrx(_rx), mry(_ry), flying(false), dy(0) {}

    Matrix4f Player::direction() const {
        return (Affine3f(AngleAxisf(mrx, Vector3f::UnitX())) *
                Affine3f(AngleAxisf(mry, Vector3f::UnitY()))).matrix();
    }

    Matrix4f Player::translation() const {
        return (Affine3f(Translation3f(position)) *
                Affine3f(AngleAxisf(-mry, Vector3f::UnitY())) *
                Affine3f(AngleAxisf(-mrx, Vector3f::UnitX()))).matrix();
    }

    Matrix4f Player::view() const {
        return (Affine3f(AngleAxisf(mrx, Vector3f::UnitX())) *
                Affine3f(AngleAxisf(mry, Vector3f::UnitY())) *
                Affine3f(Translation3f(-camera()))).matrix();
    }

    Vector3f Player::camera() const {
        return position + CAMERA_OFFSET_VECTOR;
    }

    Vector3f Player::camera_direction() const {
        float m = cosf(mrx);
        Vector3f vec(cosf(mry - (M_PI / 2.0f)) * m, -sinf(mrx), sinf(mry - (M_PI / 2.0f)) * m);
        vec.normalize();
        return vec;
    }

    Vector3f Player::update_position(int sz, int sx, float dt,
                                     const World &world, const BlockData &blocks,
                                     const float near_distance, const bool jump) {
        float vx = 0, vy = 0, vz = 0;
        if (!sz && !sx) { // Not mowing in X or Z
            vx = 0;
            vz = 0;
        } else { // Moving in X or Z

            float strafe = atan2f(sz, sx);

            if (flying) {
                float m = cosf(mrx);
                float y = sinf(mrx);
                if (sx) {
                    if (!sz) {
                        y = 0;
                    }
                    m = 1;
                }
                if (sz < 0) {
                    y = -y;
                }
                vx = cosf(mry + strafe) * m;
                vy = y;
                vz = sinf(mry + strafe) * m;
            } else {
                vx = cosf(mry + strafe);
                vy = 0;
                vz = sinf(mry + strafe);
            }
        }

        if(jump) {
            if(flying) {
                // Jump in flight moves upward at constant speed
                vy = 1;
            } else if(dy == 0) {
                // Jump when walking changes the acceleration upwards to 8
                dy = 8;
            } else {
                // Get middle of block
                Vector3i iPos((int)(position[0] + 0.5f), (int)(position[1]), (int)(position[2] + 0.5f));
                ChunkData *chunk = world.chunk_at(iPos).get();
                if(blocks.state[chunk->get(iPos)] == STATE_LIQUID) {
                    dy = 5.5;
                }
            }
        }

        float speed = flying ? 20 : 5;
        int estimate =
            roundf(sqrtf(powf(vx * speed, 2) +
                         powf(vy * speed + std::abs(dy) * 2, 2) +
                         powf(vz * speed, 2)) * dt * 8);
        int step = std::max(8, estimate);
        float ut = dt / step;
        vx = vx * ut * speed;
        vy = vy * ut * speed;
        vz = vz * ut * speed;
        for (int i = 0; i < step; i++) {
            if (flying) {
                // When flying upwards acceleration is constant i.e. not falling
                dy = 0;
            } else {
                // Calculate "gravity" by decreasing upwards acceleration
                dy -= ut * 25;
                dy = std::max(dy, -250.0f);
            }
            position += Vector3f(vx, vy + dy * ut, vz);
            if (collide(world, blocks, near_distance)) {
                dy = 0;
            }
        }
        if (position[1] < 0) {
            position[1] = 2;
        }
        return position;
    }

    optional<pair<Block, Block>> Player::looking_at(const World &world,
                                       const BlockData &blocks) const {
        optional<pair<Block, Block>> block(nullopt);
        float best = 0;
        const Vector3f v = camera_direction();
        const Vector3f camera_position = camera();
        int p = chunked(camera_position[0]);
        int q = chunked(camera_position[2]);
        int k = chunked(camera_position[1]);
        const auto &atAndAround = world.atAndAround({p, q, k});
        for (const auto &chunk: atAndAround) {
            const auto &seen = chunk->get(camera_position, v, 8.0f, blocks);
            if (seen) {
                auto h = seen->second;
                float d = sqrtf(powf(h.position[0] - camera_position[0], 2) +
                                powf(h.position[1] - camera_position[1], 2) +
                                powf(h.position[2] - camera_position[2], 2));
                if (best == 0 || d < best) {
                    best = d;
                    block = seen;
                }
            }
        }
        return block;
    }

    void Player::rotate_x(float speed) {
        mrx += speed;
        mrx = std::max(mrx, -((float)M_PI / 2.0f));
        mrx = std::min(mrx, ((float)M_PI / 2.0f));
    }

    void Player::rotate_y(float speed) {
        mry += speed;
        if (mry < 0) {
            mry += (M_PI * 2);
        }
        if (mry >= (M_PI * 2)){
            mry -= (M_PI * 2);
        }
    }

    void Player::fly() {
        flying = !flying;
    }

    float Player::rx() {
        return mrx;
    }

    float Player::ry() {
        return mry;
    }

    int Player::collide(const World &world, const BlockData &blocks, const float near_distance) {
        int result = 0;
        float x = position[0];
        float y = position[1];
        float z = position[2];
        int height = 2;
        int p = chunked(x);
        int q = chunked(z);
        int k = chunked(y);
        int nx = roundf(x);
        int ny = roundf(y);
        int nz = roundf(z);
        float px = x - nx;
        float py = y - ny;
        float pz = z - nz;
        float pad = near_distance * 2;
        int r = 1;
        for (int dp = -r; dp <= r; dp++) {
            for (int dq = -r; dq <= r; dq++) {
                for (int dk = -r; dk <= r; dk++) {
                    try {
                        ChunkData *chunk = world.chunk(Vector3i(p + dp, q + dq, k + dk)).get();
                        if (blocks.is_obstacle[chunk->get(Vector3i(nx, ny-1, nz))]) {
                            position[1] += 1.0f;
                            return 1;
                        }
                        for (int dy = 0; dy < height; dy++) {
                            if (px < -pad && blocks.is_obstacle[chunk->get(Vector3i(nx - 1, ny - dy, nz))]) {
                                position[0] = nx - pad;
                            }
                            if (px > pad && blocks.is_obstacle[chunk->get(Vector3i(nx + 1, ny - dy, nz))]) {
                                position[0] = nx + pad;
                            }
                            if (py < -pad && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy - 1, nz))]) {
                                position[1] = ny - pad;
                                result = 1;
                            }
                            if (py > (pad - CAMERA_OFFSET) && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy + 1, nz))]) {
                                position[1] = ny + pad - CAMERA_OFFSET;
                                result = 1;
                            }
                            if (pz < -pad && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy, nz - 1))]) {
                                position[2] = nz - pad;
                            }
                            if (pz > pad && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy, nz + 1))]) {
                                position[2] = nz + pad;
                            }
                        }
                    } catch(std::out_of_range e) {
                        continue;
                    }
                }
            }
        }

        return result;
    }
};
<commit_msg>Fix indentation<commit_after>#include <algorithm>
#include "player.h"
#include "matrix.h"
#include "math.h"

namespace konstructs {

    using namespace Eigen;
    using nonstd::nullopt;

    static float CAMERA_OFFSET = 0.5f;
    static Vector3f CAMERA_OFFSET_VECTOR = Vector3f(0, CAMERA_OFFSET, 0);

    Player::Player(const int _id, const Vector3f _position, const float _rx,
                   const float _ry):
        id(_id), position(_position), mrx(_rx), mry(_ry), flying(false), dy(0) {}

    Matrix4f Player::direction() const {
        return (Affine3f(AngleAxisf(mrx, Vector3f::UnitX())) *
                Affine3f(AngleAxisf(mry, Vector3f::UnitY()))).matrix();
    }

    Matrix4f Player::translation() const {
        return (Affine3f(Translation3f(position)) *
                Affine3f(AngleAxisf(-mry, Vector3f::UnitY())) *
                Affine3f(AngleAxisf(-mrx, Vector3f::UnitX()))).matrix();
    }

    Matrix4f Player::view() const {
        return (Affine3f(AngleAxisf(mrx, Vector3f::UnitX())) *
                Affine3f(AngleAxisf(mry, Vector3f::UnitY())) *
                Affine3f(Translation3f(-camera()))).matrix();
    }

    Vector3f Player::camera() const {
        return position + CAMERA_OFFSET_VECTOR;
    }

    Vector3f Player::camera_direction() const {
        float m = cosf(mrx);
        Vector3f vec(cosf(mry - (M_PI / 2.0f)) * m, -sinf(mrx), sinf(mry - (M_PI / 2.0f)) * m);
        vec.normalize();
        return vec;
    }

    Vector3f Player::update_position(int sz, int sx, float dt,
                                     const World &world, const BlockData &blocks,
                                     const float near_distance, const bool jump) {
        float vx = 0, vy = 0, vz = 0;
        if (!sz && !sx) { // Not mowing in X or Z
            vx = 0;
            vz = 0;
        } else { // Moving in X or Z

            float strafe = atan2f(sz, sx);

            if (flying) {
                float m = cosf(mrx);
                float y = sinf(mrx);
                if (sx) {
                    if (!sz) {
                        y = 0;
                    }
                    m = 1;
                }
                if (sz < 0) {
                    y = -y;
                }
                vx = cosf(mry + strafe) * m;
                vy = y;
                vz = sinf(mry + strafe) * m;
            } else {
                vx = cosf(mry + strafe);
                vy = 0;
                vz = sinf(mry + strafe);
            }
        }

        if(jump) {
            if(flying) {
                // Jump in flight moves upward at constant speed
                vy = 1;
            } else if(dy == 0) {
                // Jump when walking changes the acceleration upwards to 8
                dy = 8;
            } else {
                // Get middle of block
                Vector3i iPos((int)(position[0] + 0.5f), (int)(position[1]), (int)(position[2] + 0.5f));
                ChunkData *chunk = world.chunk_at(iPos).get();
                if(blocks.state[chunk->get(iPos)] == STATE_LIQUID) {
                    dy = 5.5;
                }
            }
        }

        float speed = flying ? 20 : 5;
        int estimate =
            roundf(sqrtf(powf(vx * speed, 2) +
                         powf(vy * speed + std::abs(dy) * 2, 2) +
                         powf(vz * speed, 2)) * dt * 8);
        int step = std::max(8, estimate);
        float ut = dt / step;
        vx = vx * ut * speed;
        vy = vy * ut * speed;
        vz = vz * ut * speed;
        for (int i = 0; i < step; i++) {
            if (flying) {
                // When flying upwards acceleration is constant i.e. not falling
                dy = 0;
            } else {
                // Calculate "gravity" by decreasing upwards acceleration
                dy -= ut * 25;
                dy = std::max(dy, -250.0f);
            }
            position += Vector3f(vx, vy + dy * ut, vz);
            if (collide(world, blocks, near_distance)) {
                dy = 0;
            }
        }
        if (position[1] < 0) {
            position[1] = 2;
        }
        return position;
    }

    optional<pair<Block, Block>> Player::looking_at(const World &world,
                                                    const BlockData &blocks) const {
        optional<pair<Block, Block>> block(nullopt);
        float best = 0;
        const Vector3f v = camera_direction();
        const Vector3f camera_position = camera();
        int p = chunked(camera_position[0]);
        int q = chunked(camera_position[2]);
        int k = chunked(camera_position[1]);
        const auto &atAndAround = world.atAndAround({p, q, k});
        for (const auto &chunk: atAndAround) {
            const auto &seen = chunk->get(camera_position, v, 8.0f, blocks);
            if (seen) {
                auto h = seen->second;
                float d = sqrtf(powf(h.position[0] - camera_position[0], 2) +
                                powf(h.position[1] - camera_position[1], 2) +
                                powf(h.position[2] - camera_position[2], 2));
                if (best == 0 || d < best) {
                    best = d;
                    block = seen;
                }
            }
        }
        return block;
    }

    void Player::rotate_x(float speed) {
        mrx += speed;
        mrx = std::max(mrx, -((float)M_PI / 2.0f));
        mrx = std::min(mrx, ((float)M_PI / 2.0f));
    }

    void Player::rotate_y(float speed) {
        mry += speed;
        if (mry < 0) {
            mry += (M_PI * 2);
        }
        if (mry >= (M_PI * 2)){
            mry -= (M_PI * 2);
        }
    }

    void Player::fly() {
        flying = !flying;
    }

    float Player::rx() {
        return mrx;
    }

    float Player::ry() {
        return mry;
    }

    int Player::collide(const World &world, const BlockData &blocks, const float near_distance) {
        int result = 0;
        float x = position[0];
        float y = position[1];
        float z = position[2];
        int height = 2;
        int p = chunked(x);
        int q = chunked(z);
        int k = chunked(y);
        int nx = roundf(x);
        int ny = roundf(y);
        int nz = roundf(z);
        float px = x - nx;
        float py = y - ny;
        float pz = z - nz;
        float pad = near_distance * 2;
        int r = 1;
        for (int dp = -r; dp <= r; dp++) {
            for (int dq = -r; dq <= r; dq++) {
                for (int dk = -r; dk <= r; dk++) {
                    try {
                        ChunkData *chunk = world.chunk(Vector3i(p + dp, q + dq, k + dk)).get();
                        if (blocks.is_obstacle[chunk->get(Vector3i(nx, ny-1, nz))]) {
                            position[1] += 1.0f;
                            return 1;
                        }
                        for (int dy = 0; dy < height; dy++) {
                            if (px < -pad && blocks.is_obstacle[chunk->get(Vector3i(nx - 1, ny - dy, nz))]) {
                                position[0] = nx - pad;
                            }
                            if (px > pad && blocks.is_obstacle[chunk->get(Vector3i(nx + 1, ny - dy, nz))]) {
                                position[0] = nx + pad;
                            }
                            if (py < -pad && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy - 1, nz))]) {
                                position[1] = ny - pad;
                                result = 1;
                            }
                            if (py > (pad - CAMERA_OFFSET) && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy + 1, nz))]) {
                                position[1] = ny + pad - CAMERA_OFFSET;
                                result = 1;
                            }
                            if (pz < -pad && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy, nz - 1))]) {
                                position[2] = nz - pad;
                            }
                            if (pz > pad && blocks.is_obstacle[chunk->get(Vector3i(nx, ny - dy, nz + 1))]) {
                                position[2] = nz + pad;
                            }
                        }
                    } catch(std::out_of_range e) {
                        continue;
                    }
                }
            }
        }

        return result;
    }
};
<|endoftext|>


<commit_before>// @(#)root/gui:$Name:  $:$Id: TGSplitter.cxx,v 1.10 2005/11/17 19:09:28 rdm Exp $
// Author: Fons Rademakers   6/09/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGSplitter, TGVSplitter and TGHSplitter                              //
//                                                                      //
// A splitter allows the frames left and right or above and below of    //
// it to be resized. The frame to be resized must have the kFixedWidth  //
// or kFixedHeight property set.                                        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGSplitter.h"
#include "TGPicture.h"
#include "Riostream.h"

ClassImp(TGSplitter)
ClassImp(TGVSplitter)
ClassImp(TGHSplitter)

//______________________________________________________________________________
TGSplitter::TGSplitter(const TGWindow *p, UInt_t w, UInt_t h,
              UInt_t options, ULong_t back) : TGFrame(p, w, h, options, back)
{
   // Create a splitter.

   fDragging = kFALSE;
   fEditDisabled = kTRUE;
}

//______________________________________________________________________________
TGVSplitter::TGVSplitter(const TGWindow *p, UInt_t w, UInt_t h,
              UInt_t options, ULong_t back) : TGSplitter(p, w, h, options, back)
{
   // Create a vertical splitter.

   fSplitCursor = kNone;
   fSplitterPic = fClient->GetPicture("splitterv.xpm");

   if (!fSplitterPic)
      Error("TGVSplitter", "splitterv.xpm not found");

   if (p && !p->InheritsFrom(TGCompositeFrame::Class())) {
      Error("TGVSplitter", "parent must inherit from a TGCompositeFrame");
      return;
   }
   if (p && !(((TGCompositeFrame*)p)->GetOptions() & kHorizontalFrame)) {
      Error("TGVSplitter", "parent must have a horizontal layout manager");
      return;
   }

   fSplitCursor = gVirtualX->CreateCursor(kArrowHor);
   fFrame = 0;

   gVirtualX->GrabButton(fId, kAnyButton, kAnyModifier,
                         kButtonPressMask | kButtonReleaseMask |
                         kPointerMotionMask, kNone, kNone);

   AddInput(kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TGVSplitter::~TGVSplitter()
{
   // Delete vertical slider widget.

   if (fSplitterPic) fClient->FreePicture(fSplitterPic);
}

//______________________________________________________________________________
void TGVSplitter::SetFrame(TGFrame *frame, Bool_t left)
{
   // Set frame to be resized. If frame is on the left of the splitter
   // set left to true.

   fFrame = frame;
   fLeft  = left;

   if (!(fFrame->GetOptions() & kFixedWidth))
      Error("SetFrame", "resize frame must have kFixedWidth option set");
}

//______________________________________________________________________________
Bool_t TGVSplitter::HandleButton(Event_t *event)
{
   // Handle mouse button event in vertical splitter.

   if (fSplitCursor == kNone) return kTRUE;

   if (!fFrame) {
      Error("HandleButton", "frame to be resized not set");
      return kTRUE;
   }

   if (event->fType == kButtonPress) {
      fStartX   = event->fXRoot;
      fDragging = kTRUE;

      Int_t  x, y;
      gVirtualX->GetWindowSize(fFrame->GetId(), x, y, fFrameWidth, fFrameHeight);

      // get fMin and fMax in root coordinates
      Int_t    xroot, yroot;
      UInt_t   w, h;
      Window_t wdum;
      gVirtualX->GetWindowSize(fParent->GetId(), x, y, w, h);
      gVirtualX->TranslateCoordinates(fParent->GetParent()->GetId(),
                                      fClient->GetDefaultRoot()->GetId(),
                                      x, y, xroot, yroot, wdum);
      fMin = xroot;
      fMax = xroot + w - 2;

      // last argument kFALSE forces all specified events to this window
      gVirtualX->GrabPointer(fId, kButtonPressMask | kButtonReleaseMask |
                             kPointerMotionMask, kNone, fSplitCursor,
                             kTRUE, kFALSE);
   } else {
      fDragging = kFALSE;
      gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE);  // ungrab pointer
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGVSplitter::HandleMotion(Event_t *event)
{
   // Handle mouse motion event in vertical splitter.

   if (fDragging) {
      Int_t xr = event->fXRoot;
      if (xr > fMax) xr = fMax;
      if (xr < fMin) xr = fMin;
      Int_t delta = xr - fStartX;
      Int_t w = (Int_t) fFrameWidth;
      if (fLeft)
         w += delta;
      else
         w -= delta;
      if (w < 0) w = 0;
      fStartX = xr;

      if (delta != 0) {
         fFrameWidth = w;
         fFrame->Resize(fFrameWidth, fFrameHeight);

         TGCompositeFrame *p = (TGCompositeFrame *) GetParent();
         p->Layout();
      }
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGVSplitter::HandleCrossing(Event_t *event)
{
   // Handle mouse motion event in vertical splitter.

   if (event->fType == kEnterNotify)
      gVirtualX->SetCursor(fId, fSplitCursor);
   else
      gVirtualX->SetCursor(fId, kNone);

   return kTRUE;
}

//______________________________________________________________________________
void TGVSplitter::DrawBorder()
{
   // Draw vertical splitter.

   if (fSplitterPic) {
      Int_t posx = (fWidth/2)-(fSplitterPic->GetWidth()/2);
      Int_t posy = (fHeight/2)-(fSplitterPic->GetHeight()/2);
      fSplitterPic->Draw(fId, GetBckgndGC()(), posx, posy);
   }
}

//______________________________________________________________________________
TGHSplitter::TGHSplitter(const TGWindow *p, UInt_t w, UInt_t h,
              UInt_t options, ULong_t back) : TGSplitter(p, w, h, options, back)
{
   // Create a horizontal splitter.

   fSplitCursor = kNone;

   fSplitterPic = fClient->GetPicture("splitterh.xpm");

   if (!fSplitterPic)
      Error("TGVSplitter", "splitterh.xpm not found");

   if (p && !p->InheritsFrom(TGCompositeFrame::Class())) {
      Error("TGHSplitter", "parent must inherit from a TGCompositeFrame");
      return;
   }
   if (p && !(((TGCompositeFrame*)p)->GetOptions() & kVerticalFrame)) {
      Error("TGVSplitter", "parent must have a vertical layout manager");
      return;
   }

   fSplitCursor = gVirtualX->CreateCursor(kArrowVer);
   fFrame = 0;

   gVirtualX->GrabButton(fId, kAnyButton, kAnyModifier,
                         kButtonPressMask | kButtonReleaseMask |
                         kPointerMotionMask, kNone, kNone);

   AddInput(kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TGHSplitter::~TGHSplitter()
{
   // Delete vertical slider widget.

   if (fSplitterPic) fClient->FreePicture(fSplitterPic);
}

//______________________________________________________________________________
void TGHSplitter::SetFrame(TGFrame *frame, Bool_t above)
{
   // Set frame to be resized. If frame is above the splitter
   // set above to true.

   fFrame = frame;
   fAbove = above;

   if (!(fFrame->GetOptions() & kFixedHeight))
      Error("SetFrame", "resize frame must have kFixedHeight option set");
}

//______________________________________________________________________________
Bool_t TGHSplitter::HandleButton(Event_t *event)
{
   // Handle mouse button event in horizontal splitter.

   if (fSplitCursor == kNone) return kTRUE;

   if (!fFrame) {
      Error("HandleButton", "frame to be resized not set");
      return kTRUE;
   }

   if (event->fType == kButtonPress) {
      fStartY   = event->fYRoot;
      fDragging = kTRUE;

      Int_t  x, y;
      gVirtualX->GetWindowSize(fFrame->GetId(), x, y, fFrameWidth, fFrameHeight);

      // get fMin and fMax in root coordinates
      Int_t    xroot, yroot;
      UInt_t   w, h;
      Window_t wdum;
      gVirtualX->GetWindowSize(fParent->GetId(), x, y, w, h);
      gVirtualX->TranslateCoordinates(fParent->GetParent()->GetId(),
                                      fClient->GetDefaultRoot()->GetId(),
                                      x, y, xroot, yroot, wdum);
      fMin = yroot;
      fMax = yroot + h - 2;

      // last argument kFALSE forces all specified events to this window
      gVirtualX->GrabPointer(fId, kButtonPressMask | kButtonReleaseMask |
                             kPointerMotionMask, kNone, fSplitCursor,
                             kTRUE, kFALSE);
   } else {
      fDragging = kFALSE;
      gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE);  // ungrab pointer
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGHSplitter::HandleMotion(Event_t *event)
{
   // Handle mouse motion event in horizontal splitter.

   if (fDragging) {
      Int_t yr = event->fYRoot;
      if (yr > fMax) yr = fMax;
      if (yr < fMin) yr = fMin;
      Int_t delta = yr - fStartY;
      Int_t h = (Int_t) fFrameHeight;
      if (fAbove)
         h += delta;
      else
         h -= delta;
      if (h < 0) h = 0;
      fStartY = yr;

      if (delta != 0) {
         fFrameHeight = h;
         fFrame->Resize(fFrameWidth, fFrameHeight);

         TGCompositeFrame *p = (TGCompositeFrame *) GetParent();
         p->Layout();
      }
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGHSplitter::HandleCrossing(Event_t *event)
{
   // Handle mouse motion event in horizontal splitter.

   if (event->fType == kEnterNotify)
      gVirtualX->SetCursor(fId, fSplitCursor);
   else
      gVirtualX->SetCursor(fId, kNone);

   return kTRUE;
}

//______________________________________________________________________________
void TGHSplitter::DrawBorder()
{
   // Draw horizontal splitter.

   if (fSplitterPic) {
      Int_t posx = (fWidth/2)-(fSplitterPic->GetWidth()/2);
      Int_t posy = (fHeight/2)-(fSplitterPic->GetHeight()/2);
      fSplitterPic->Draw(fId, GetBckgndGC()(), posx, posy);
   }
}

//______________________________________________________________________________
void TGVSplitter::SavePrimitive(ofstream &out, Option_t *option)
{
    // Save a splitter widget as a C++ statement(s) on output stream out.

   if (fBackground != GetDefaultFrameBackground()) SaveUserColor(out, option);

   out << "   TGVSplitter *";
   out << GetName() <<" = new TGVSplitter("<< fParent->GetName()
       << "," << GetWidth() << "," << GetHeight();

   if (fBackground == GetDefaultFrameBackground()) {
      if (!GetOptions()) {
         out <<");" << endl;
      } else {
         out << "," << GetOptionString() <<");" << endl;
      }
   } else {
      out << "," << GetOptionString() << ",ucolor);" << endl;
   }

   out << "   " << GetName() << "->SetFrame(" << GetFrame()->GetName();
   if (GetLeft()) out << ",kTRUE);" << endl;
   else           out << ",kFALSE);"<< endl;
}

//______________________________________________________________________________
void TGHSplitter::SavePrimitive(ofstream &out, Option_t *option)
{
    // Save a splitter widget as a C++ statement(s) on output stream out.

   if (fBackground != GetDefaultFrameBackground()) SaveUserColor(out, option);

   out << "   TGHSplitter *";
   out << GetName() <<" = new TGHSplitter("<< fParent->GetName()
       << "," << GetWidth() << "," << GetHeight();

   if (fBackground == GetDefaultFrameBackground()) {
      if (!GetOptions()) {
         out <<");" << endl;
      } else {
         out << "," << GetOptionString() <<");" << endl;
      }
   } else {
      out << "," << GetOptionString() << ",ucolor);" << endl;
   }

   out << "   " << GetName() << "->SetFrame(" << GetFrame()->GetName();
   if (GetAbove()) out << ",kTRUE);" << endl;
   else            out << ",kFALSE);"<< endl;
}
<commit_msg>- print correct class name in Error message<commit_after>// @(#)root/gui:$Name:  $:$Id: TGSplitter.cxx,v 1.11 2006/04/12 11:11:43 rdm Exp $
// Author: Fons Rademakers   6/09/2000

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGSplitter, TGVSplitter and TGHSplitter                              //
//                                                                      //
// A splitter allows the frames left and right or above and below of    //
// it to be resized. The frame to be resized must have the kFixedWidth  //
// or kFixedHeight property set.                                        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TGSplitter.h"
#include "TGPicture.h"
#include "Riostream.h"

ClassImp(TGSplitter)
ClassImp(TGVSplitter)
ClassImp(TGHSplitter)

//______________________________________________________________________________
TGSplitter::TGSplitter(const TGWindow *p, UInt_t w, UInt_t h,
              UInt_t options, ULong_t back) : TGFrame(p, w, h, options, back)
{
   // Create a splitter.

   fDragging = kFALSE;
   fEditDisabled = kTRUE;
}

//______________________________________________________________________________
TGVSplitter::TGVSplitter(const TGWindow *p, UInt_t w, UInt_t h,
              UInt_t options, ULong_t back) : TGSplitter(p, w, h, options, back)
{
   // Create a vertical splitter.

   fSplitCursor = kNone;
   fSplitterPic = fClient->GetPicture("splitterv.xpm");

   if (!fSplitterPic)
      Error("TGVSplitter", "splitterv.xpm not found");

   if (p && !p->InheritsFrom(TGCompositeFrame::Class())) {
      Error("TGVSplitter", "parent must inherit from a TGCompositeFrame");
      return;
   }
   if (p && !(((TGCompositeFrame*)p)->GetOptions() & kHorizontalFrame)) {
      Error("TGVSplitter", "parent must have a horizontal layout manager");
      return;
   }

   fSplitCursor = gVirtualX->CreateCursor(kArrowHor);
   fFrame = 0;

   gVirtualX->GrabButton(fId, kAnyButton, kAnyModifier,
                         kButtonPressMask | kButtonReleaseMask |
                         kPointerMotionMask, kNone, kNone);

   AddInput(kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TGVSplitter::~TGVSplitter()
{
   // Delete vertical slider widget.

   if (fSplitterPic) fClient->FreePicture(fSplitterPic);
}

//______________________________________________________________________________
void TGVSplitter::SetFrame(TGFrame *frame, Bool_t left)
{
   // Set frame to be resized. If frame is on the left of the splitter
   // set left to true.

   fFrame = frame;
   fLeft  = left;

   if (!(fFrame->GetOptions() & kFixedWidth))
      Error("SetFrame", "resize frame must have kFixedWidth option set");
}

//______________________________________________________________________________
Bool_t TGVSplitter::HandleButton(Event_t *event)
{
   // Handle mouse button event in vertical splitter.

   if (fSplitCursor == kNone) return kTRUE;

   if (!fFrame) {
      Error("HandleButton", "frame to be resized not set");
      return kTRUE;
   }

   if (event->fType == kButtonPress) {
      fStartX   = event->fXRoot;
      fDragging = kTRUE;

      Int_t  x, y;
      gVirtualX->GetWindowSize(fFrame->GetId(), x, y, fFrameWidth, fFrameHeight);

      // get fMin and fMax in root coordinates
      Int_t    xroot, yroot;
      UInt_t   w, h;
      Window_t wdum;
      gVirtualX->GetWindowSize(fParent->GetId(), x, y, w, h);
      gVirtualX->TranslateCoordinates(fParent->GetParent()->GetId(),
                                      fClient->GetDefaultRoot()->GetId(),
                                      x, y, xroot, yroot, wdum);
      fMin = xroot;
      fMax = xroot + w - 2;

      // last argument kFALSE forces all specified events to this window
      gVirtualX->GrabPointer(fId, kButtonPressMask | kButtonReleaseMask |
                             kPointerMotionMask, kNone, fSplitCursor,
                             kTRUE, kFALSE);
   } else {
      fDragging = kFALSE;
      gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE);  // ungrab pointer
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGVSplitter::HandleMotion(Event_t *event)
{
   // Handle mouse motion event in vertical splitter.

   if (fDragging) {
      Int_t xr = event->fXRoot;
      if (xr > fMax) xr = fMax;
      if (xr < fMin) xr = fMin;
      Int_t delta = xr - fStartX;
      Int_t w = (Int_t) fFrameWidth;
      if (fLeft)
         w += delta;
      else
         w -= delta;
      if (w < 0) w = 0;
      fStartX = xr;

      if (delta != 0) {
         fFrameWidth = w;
         fFrame->Resize(fFrameWidth, fFrameHeight);

         TGCompositeFrame *p = (TGCompositeFrame *) GetParent();
         p->Layout();
      }
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGVSplitter::HandleCrossing(Event_t *event)
{
   // Handle mouse motion event in vertical splitter.

   if (event->fType == kEnterNotify)
      gVirtualX->SetCursor(fId, fSplitCursor);
   else
      gVirtualX->SetCursor(fId, kNone);

   return kTRUE;
}

//______________________________________________________________________________
void TGVSplitter::DrawBorder()
{
   // Draw vertical splitter.

   if (fSplitterPic) {
      Int_t posx = (fWidth/2)-(fSplitterPic->GetWidth()/2);
      Int_t posy = (fHeight/2)-(fSplitterPic->GetHeight()/2);
      fSplitterPic->Draw(fId, GetBckgndGC()(), posx, posy);
   }
}

//______________________________________________________________________________
TGHSplitter::TGHSplitter(const TGWindow *p, UInt_t w, UInt_t h,
              UInt_t options, ULong_t back) : TGSplitter(p, w, h, options, back)
{
   // Create a horizontal splitter.

   fSplitCursor = kNone;

   fSplitterPic = fClient->GetPicture("splitterh.xpm");

   if (!fSplitterPic)
      Error("TGHSplitter", "splitterh.xpm not found");

   if (p && !p->InheritsFrom(TGCompositeFrame::Class())) {
      Error("TGHSplitter", "parent must inherit from a TGCompositeFrame");
      return;
   }
   if (p && !(((TGCompositeFrame*)p)->GetOptions() & kVerticalFrame)) {
      Error("TGHSplitter", "parent must have a vertical layout manager");
      return;
   }

   fSplitCursor = gVirtualX->CreateCursor(kArrowVer);
   fFrame = 0;

   gVirtualX->GrabButton(fId, kAnyButton, kAnyModifier,
                         kButtonPressMask | kButtonReleaseMask |
                         kPointerMotionMask, kNone, kNone);

   AddInput(kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TGHSplitter::~TGHSplitter()
{
   // Delete vertical slider widget.

   if (fSplitterPic) fClient->FreePicture(fSplitterPic);
}

//______________________________________________________________________________
void TGHSplitter::SetFrame(TGFrame *frame, Bool_t above)
{
   // Set frame to be resized. If frame is above the splitter
   // set above to true.

   fFrame = frame;
   fAbove = above;

   if (!(fFrame->GetOptions() & kFixedHeight))
      Error("SetFrame", "resize frame must have kFixedHeight option set");
}

//______________________________________________________________________________
Bool_t TGHSplitter::HandleButton(Event_t *event)
{
   // Handle mouse button event in horizontal splitter.

   if (fSplitCursor == kNone) return kTRUE;

   if (!fFrame) {
      Error("HandleButton", "frame to be resized not set");
      return kTRUE;
   }

   if (event->fType == kButtonPress) {
      fStartY   = event->fYRoot;
      fDragging = kTRUE;

      Int_t  x, y;
      gVirtualX->GetWindowSize(fFrame->GetId(), x, y, fFrameWidth, fFrameHeight);

      // get fMin and fMax in root coordinates
      Int_t    xroot, yroot;
      UInt_t   w, h;
      Window_t wdum;
      gVirtualX->GetWindowSize(fParent->GetId(), x, y, w, h);
      gVirtualX->TranslateCoordinates(fParent->GetParent()->GetId(),
                                      fClient->GetDefaultRoot()->GetId(),
                                      x, y, xroot, yroot, wdum);
      fMin = yroot;
      fMax = yroot + h - 2;

      // last argument kFALSE forces all specified events to this window
      gVirtualX->GrabPointer(fId, kButtonPressMask | kButtonReleaseMask |
                             kPointerMotionMask, kNone, fSplitCursor,
                             kTRUE, kFALSE);
   } else {
      fDragging = kFALSE;
      gVirtualX->GrabPointer(0, 0, 0, 0, kFALSE);  // ungrab pointer
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGHSplitter::HandleMotion(Event_t *event)
{
   // Handle mouse motion event in horizontal splitter.

   if (fDragging) {
      Int_t yr = event->fYRoot;
      if (yr > fMax) yr = fMax;
      if (yr < fMin) yr = fMin;
      Int_t delta = yr - fStartY;
      Int_t h = (Int_t) fFrameHeight;
      if (fAbove)
         h += delta;
      else
         h -= delta;
      if (h < 0) h = 0;
      fStartY = yr;

      if (delta != 0) {
         fFrameHeight = h;
         fFrame->Resize(fFrameWidth, fFrameHeight);

         TGCompositeFrame *p = (TGCompositeFrame *) GetParent();
         p->Layout();
      }
   }
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGHSplitter::HandleCrossing(Event_t *event)
{
   // Handle mouse motion event in horizontal splitter.

   if (event->fType == kEnterNotify)
      gVirtualX->SetCursor(fId, fSplitCursor);
   else
      gVirtualX->SetCursor(fId, kNone);

   return kTRUE;
}

//______________________________________________________________________________
void TGHSplitter::DrawBorder()
{
   // Draw horizontal splitter.

   if (fSplitterPic) {
      Int_t posx = (fWidth/2)-(fSplitterPic->GetWidth()/2);
      Int_t posy = (fHeight/2)-(fSplitterPic->GetHeight()/2);
      fSplitterPic->Draw(fId, GetBckgndGC()(), posx, posy);
   }
}

//______________________________________________________________________________
void TGVSplitter::SavePrimitive(ofstream &out, Option_t *option)
{
    // Save a splitter widget as a C++ statement(s) on output stream out.

   if (fBackground != GetDefaultFrameBackground()) SaveUserColor(out, option);

   out << "   TGVSplitter *";
   out << GetName() <<" = new TGVSplitter("<< fParent->GetName()
       << "," << GetWidth() << "," << GetHeight();

   if (fBackground == GetDefaultFrameBackground()) {
      if (!GetOptions()) {
         out <<");" << endl;
      } else {
         out << "," << GetOptionString() <<");" << endl;
      }
   } else {
      out << "," << GetOptionString() << ",ucolor);" << endl;
   }

   out << "   " << GetName() << "->SetFrame(" << GetFrame()->GetName();
   if (GetLeft()) out << ",kTRUE);" << endl;
   else           out << ",kFALSE);"<< endl;
}

//______________________________________________________________________________
void TGHSplitter::SavePrimitive(ofstream &out, Option_t *option)
{
    // Save a splitter widget as a C++ statement(s) on output stream out.

   if (fBackground != GetDefaultFrameBackground()) SaveUserColor(out, option);

   out << "   TGHSplitter *";
   out << GetName() <<" = new TGHSplitter("<< fParent->GetName()
       << "," << GetWidth() << "," << GetHeight();

   if (fBackground == GetDefaultFrameBackground()) {
      if (!GetOptions()) {
         out <<");" << endl;
      } else {
         out << "," << GetOptionString() <<");" << endl;
      }
   } else {
      out << "," << GetOptionString() << ",ucolor);" << endl;
   }

   out << "   " << GetName() << "->SetFrame(" << GetFrame()->GetName();
   if (GetAbove()) out << ",kTRUE);" << endl;
   else            out << ",kFALSE);"<< endl;
}
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <common/config.h>

#if VSNRAY_COMMON_HAVE_PNG
#include <png.h>
#endif

#include "cfile.h"
#include "png_image.h"

namespace visionaray
{

#if VSNRAY_COMMON_HAVE_PNG
struct png_read_context
{
    png_structp png;
    png_infop info;

    png_read_context()
        : png(0)
        , info(0)
    {
    }

   ~png_read_context()
    {
        png_destroy_read_struct(&png, &info, 0);
    }
};

struct png_write_context
{
    png_structp png;
    png_infop info;
    png_bytep row;

    png_write_context()
        : png(0)
        , info(0)
        , row(0)
    {
    }

   ~png_write_context()
    {
        png_free_data(png, info, PNG_FREE_ALL, -1);
        png_destroy_write_struct(&png, &info);

        delete[] row;
    }
};

static void png_error_callback(png_structp png_ptr, png_const_charp msg)
{
    fprintf(stderr, "PNG error: \"%s\"\n", msg);

    longjmp(png_jmpbuf(png_ptr), 1);
}

static void png_warning_callback(png_structp /*png_ptr*/, png_const_charp /*msg*/)
{
    // TODO
}

static int png_num_components(int color_type)
{
    switch (color_type)
    {
    case PNG_COLOR_TYPE_GRAY:
        return 1;
    case PNG_COLOR_TYPE_GRAY_ALPHA:
        return 2;
    case PNG_COLOR_TYPE_RGB:
        return 3;
    case PNG_COLOR_TYPE_RGB_ALPHA:
        return 4;
    }

    return -1;
}
#endif

//-------------------------------------------------------------------------------------------------
// png_image
//

png_image::png_image(int width, int height, pixel_format format, uint8_t const* data)
    : image_base(width, height, format, data)
{
}

bool png_image::load(std::string const& filename)
{
#if VSNRAY_COMMON_HAVE_PNG
    cfile file(filename.c_str(), "r");

    if (!file.good())
    {
        return false;
    }

    png_read_context context;

    context.png = png_create_read_struct(
            PNG_LIBPNG_VER_STRING,
            0 /*user-data*/,
            png_error_callback,
            png_warning_callback
            );

    if (context.png == 0)
    {
        return false;
    }

    context.info = png_create_info_struct(context.png);

    if (context.info == 0)
    {
        return false;
    }

    png_init_io(context.png, file.get());

    png_read_info(context.png, context.info);

    png_uint_32 w = 0;
    png_uint_32 h = 0;
    int bit_depth = 0;
    int color_type = 0;

    png_get_IHDR(context.png, context.info, &w, &h, &bit_depth, &color_type, 0, 0, 0);

    // expand paletted images to RGB
    if (color_type == PNG_COLOR_TYPE_PALETTE)
    {
        png_set_expand(context.png);
    }

    png_read_update_info(context.png, context.info);

    w           = png_get_image_width(context.png, context.info);
    h           = png_get_image_height(context.png, context.info);
    bit_depth   = png_get_bit_depth(context.png, context.info);
    color_type  = png_get_color_type(context.png, context.info);

    // Only support 8-bit and 16-bit per pixel images
    if (bit_depth != 8 && bit_depth != 16)
    {
        return false;
    }

    auto num_components = png_num_components(color_type);

    switch (num_components)
    {
    case 3:
        format_ = bit_depth == 8 ? PF_RGB8 : PF_RGB16UI;
        break;

    case 4:
        format_ = bit_depth == 8 ? PF_RGBA8 : PF_RGBA16UI;
        break;

    default:
        format_ = PF_UNSPECIFIED;
        return false;
    }

    auto pitch = png_get_rowbytes(context.png, context.info);

    data_.resize(pitch * h);

    for (png_uint_32 y = 0; y < h; ++y)
    {
        png_bytep row = data_.data() + (h - 1) * pitch - y * pitch;
        png_read_rows(context.png, &row, nullptr, 1);
    }

    png_read_end(context.png, context.info);

    width_  = static_cast<int>(w);
    height_ = static_cast<int>(h);

    return true;
#else
    VSNRAY_UNUSED(filename);

    return false;
#endif
}

bool png_image::save(std::string const& filename, file_base::save_options const& options)
{
#if VSNRAY_COMMON_HAVE_PNG
    cfile file(filename.c_str(), "wb");

    if (!file.good())
    {
        return false;
    }

    png_write_context context;

    context.png = png_create_write_struct(
            PNG_LIBPNG_VER_STRING,
            0 /*user-data*/,
            png_error_callback,
            png_warning_callback
            );

    if (context.png == 0)
    {
        return false;
    }

    context.info = png_create_info_struct(context.png);

    if (context.info == 0)
    {
        return false;
    }

    png_init_io(context.png, file.get());

    // TODO: support other formats than RGB8
    png_set_IHDR(
            context.png,
            context.info,
            width_,
            height_,
            8,
            PNG_COLOR_TYPE_RGB,
            PNG_INTERLACE_NONE,
            PNG_COMPRESSION_TYPE_BASE,
            PNG_FILTER_TYPE_BASE
            );

    png_write_info(context.png, context.info);

    size_t pitch = width_ * 3;
    context.row = new png_byte[pitch];

    for (int y = 0; y < height_; ++y)
    {
        for (int x = 0; x < width_; ++x)
        {
            std::memcpy(
                context.row + x * 3,
                data() + (y * width_ + x) * 3,
                3
                );
        }
        png_write_row(context.png, context.row);
    }

    png_write_end(context.png, 0);

    return true;

#else
    VSNRAY_UNUSED(filename);

    return false;
#endif
}

} // visionaray
<commit_msg>Silence warning<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <common/config.h>

#if VSNRAY_COMMON_HAVE_PNG
#include <png.h>
#endif

#include "cfile.h"
#include "png_image.h"

namespace visionaray
{

#if VSNRAY_COMMON_HAVE_PNG
struct png_read_context
{
    png_structp png;
    png_infop info;

    png_read_context()
        : png(0)
        , info(0)
    {
    }

   ~png_read_context()
    {
        png_destroy_read_struct(&png, &info, 0);
    }
};

struct png_write_context
{
    png_structp png;
    png_infop info;
    png_bytep row;

    png_write_context()
        : png(0)
        , info(0)
        , row(0)
    {
    }

   ~png_write_context()
    {
        png_free_data(png, info, PNG_FREE_ALL, -1);
        png_destroy_write_struct(&png, &info);

        delete[] row;
    }
};

static void png_error_callback(png_structp png_ptr, png_const_charp msg)
{
    fprintf(stderr, "PNG error: \"%s\"\n", msg);

    longjmp(png_jmpbuf(png_ptr), 1);
}

static void png_warning_callback(png_structp /*png_ptr*/, png_const_charp /*msg*/)
{
    // TODO
}

static int png_num_components(int color_type)
{
    switch (color_type)
    {
    case PNG_COLOR_TYPE_GRAY:
        return 1;
    case PNG_COLOR_TYPE_GRAY_ALPHA:
        return 2;
    case PNG_COLOR_TYPE_RGB:
        return 3;
    case PNG_COLOR_TYPE_RGB_ALPHA:
        return 4;
    }

    return -1;
}
#endif

//-------------------------------------------------------------------------------------------------
// png_image
//

png_image::png_image(int width, int height, pixel_format format, uint8_t const* data)
    : image_base(width, height, format, data)
{
}

bool png_image::load(std::string const& filename)
{
#if VSNRAY_COMMON_HAVE_PNG
    cfile file(filename.c_str(), "r");

    if (!file.good())
    {
        return false;
    }

    png_read_context context;

    context.png = png_create_read_struct(
            PNG_LIBPNG_VER_STRING,
            0 /*user-data*/,
            png_error_callback,
            png_warning_callback
            );

    if (context.png == 0)
    {
        return false;
    }

    context.info = png_create_info_struct(context.png);

    if (context.info == 0)
    {
        return false;
    }

    png_init_io(context.png, file.get());

    png_read_info(context.png, context.info);

    png_uint_32 w = 0;
    png_uint_32 h = 0;
    int bit_depth = 0;
    int color_type = 0;

    png_get_IHDR(context.png, context.info, &w, &h, &bit_depth, &color_type, 0, 0, 0);

    // expand paletted images to RGB
    if (color_type == PNG_COLOR_TYPE_PALETTE)
    {
        png_set_expand(context.png);
    }

    png_read_update_info(context.png, context.info);

    w           = png_get_image_width(context.png, context.info);
    h           = png_get_image_height(context.png, context.info);
    bit_depth   = png_get_bit_depth(context.png, context.info);
    color_type  = png_get_color_type(context.png, context.info);

    // Only support 8-bit and 16-bit per pixel images
    if (bit_depth != 8 && bit_depth != 16)
    {
        return false;
    }

    auto num_components = png_num_components(color_type);

    switch (num_components)
    {
    case 3:
        format_ = bit_depth == 8 ? PF_RGB8 : PF_RGB16UI;
        break;

    case 4:
        format_ = bit_depth == 8 ? PF_RGBA8 : PF_RGBA16UI;
        break;

    default:
        format_ = PF_UNSPECIFIED;
        return false;
    }

    auto pitch = png_get_rowbytes(context.png, context.info);

    data_.resize(pitch * h);

    for (png_uint_32 y = 0; y < h; ++y)
    {
        png_bytep row = data_.data() + (h - 1) * pitch - y * pitch;
        png_read_rows(context.png, &row, nullptr, 1);
    }

    png_read_end(context.png, context.info);

    width_  = static_cast<int>(w);
    height_ = static_cast<int>(h);

    return true;
#else
    VSNRAY_UNUSED(filename);

    return false;
#endif
}

bool png_image::save(std::string const& filename, file_base::save_options const& /*options*/)
{
#if VSNRAY_COMMON_HAVE_PNG
    cfile file(filename.c_str(), "wb");

    if (!file.good())
    {
        return false;
    }

    png_write_context context;

    context.png = png_create_write_struct(
            PNG_LIBPNG_VER_STRING,
            0 /*user-data*/,
            png_error_callback,
            png_warning_callback
            );

    if (context.png == 0)
    {
        return false;
    }

    context.info = png_create_info_struct(context.png);

    if (context.info == 0)
    {
        return false;
    }

    png_init_io(context.png, file.get());

    // TODO: support other formats than RGB8
    png_set_IHDR(
            context.png,
            context.info,
            width_,
            height_,
            8,
            PNG_COLOR_TYPE_RGB,
            PNG_INTERLACE_NONE,
            PNG_COMPRESSION_TYPE_BASE,
            PNG_FILTER_TYPE_BASE
            );

    png_write_info(context.png, context.info);

    size_t pitch = width_ * 3;
    context.row = new png_byte[pitch];

    for (int y = 0; y < height_; ++y)
    {
        for (int x = 0; x < width_; ++x)
        {
            std::memcpy(
                context.row + x * 3,
                data() + (y * width_ + x) * 3,
                3
                );
        }
        png_write_row(context.png, context.row);
    }

    png_write_end(context.png, 0);

    return true;

#else
    VSNRAY_UNUSED(filename);

    return false;
#endif
}

} // visionaray
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 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
 *
 *****************************************************************************/

#include <mapnik/plugin.hpp>
#include <stdexcept>

#ifdef _WINDOWS
  #define NOMINMAX
  #include <windows.h>
  #define handle HMODULE
  #define dlsym GetProcAddress
  #define dlclose FreeLibrary
  #define dlerror GetLastError
  #define MAPNIK_SUPPORTS_DLOPEN
#else
  #ifdef MAPNIK_HAS_DLCFN
    #include <dlfcn.h>
    #define MAPNIK_SUPPORTS_DLOPEN
  #endif
  #define handle void *
#endif

// TODO - handle/report dlerror

namespace mapnik
{

struct _mapnik_lib_t {
    handle dl;
};

PluginInfo::PluginInfo(std::string const& filename,
                       std::string const& library_name)
    : filename_(filename),
      name_(),
      module_(new mapnik_lib_t)
      {
#ifdef _WINDOWS
          if (module_) module_->dl = LoadLibraryA(filename.c_str());
          if (module_ && module_->dl)
          {
                name_func name = reinterpret_cast<name_func>(dlsym(module_->dl, library_name.c_str()));
                if (name) name_ = name();
          }
#else
  #ifdef MAPNIK_HAS_DLCFN
          if (module_) module_->dl = dlopen(filename.c_str(),RTLD_LAZY);
          if (module_ && module_->dl)
          {
                name_func name = reinterpret_cast<name_func>(dlsym(module_->dl, library_name.c_str()));
                if (name) name_ = name();
          }
  #else
          throw std::runtime_error("no support for loading dynamic objects (Mapnik not compiled with -DMAPNIK_HAS_DLCFN)");
  #endif
#endif
      }

PluginInfo::~PluginInfo()
{
    if (module_)
    {
#ifdef MAPNIK_SUPPORTS_DLOPEN
        if (module_->dl) dlclose(module_->dl),module_->dl=0;
#endif
        delete module_;
    }
}

void * PluginInfo::get_symbol(std::string const& sym_name) const
{
#ifdef MAPNIK_SUPPORTS_DLOPEN
    return static_cast<void *>(dlsym(module_->dl, sym_name.c_str()));
#else
    return NULL;
#endif
}

std::string const& PluginInfo::name() const
{
    return name_;
}

bool PluginInfo::valid() const
{
#ifdef MAPNIK_SUPPORTS_DLOPEN
    if (module_ && module_->dl && !name_.empty()) return true;
#endif
    return false;
}

std::string PluginInfo::get_error() const
{
    return std::string("could not open: '") + name_ + "'";
}

void PluginInfo::init()
{
    // do any initialization needed
}

void PluginInfo::exit()
{
    // do any shutdown needed
}

}
<commit_msg>do not call dlclose on plugins linking libgdal<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 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
 *
 *****************************************************************************/

#include <mapnik/plugin.hpp>
#include <stdexcept>

#ifdef _WINDOWS
  #define NOMINMAX
  #include <windows.h>
  #define handle HMODULE
  #define dlsym GetProcAddress
  #define dlclose FreeLibrary
  #define dlerror GetLastError
  #define MAPNIK_SUPPORTS_DLOPEN
#else
  #ifdef MAPNIK_HAS_DLCFN
    #include <dlfcn.h>
    #define MAPNIK_SUPPORTS_DLOPEN
  #endif
  #define handle void *
#endif

// TODO - handle/report dlerror

namespace mapnik
{

struct _mapnik_lib_t {
    handle dl;
};

PluginInfo::PluginInfo(std::string const& filename,
                       std::string const& library_name)
    : filename_(filename),
      name_(),
      module_(new mapnik_lib_t)
      {
#ifdef _WINDOWS
          if (module_) module_->dl = LoadLibraryA(filename.c_str());
          if (module_ && module_->dl)
          {
                name_func name = reinterpret_cast<name_func>(dlsym(module_->dl, library_name.c_str()));
                if (name) name_ = name();
          }
#else
  #ifdef MAPNIK_HAS_DLCFN
          if (module_) module_->dl = dlopen(filename.c_str(),RTLD_LAZY);
          if (module_ && module_->dl)
          {
                name_func name = reinterpret_cast<name_func>(dlsym(module_->dl, library_name.c_str()));
                if (name) name_ = name();
          }
  #else
          throw std::runtime_error("no support for loading dynamic objects (Mapnik not compiled with -DMAPNIK_HAS_DLCFN)");
  #endif
#endif
      }

PluginInfo::~PluginInfo()
{
    if (module_)
    {
#ifdef MAPNIK_SUPPORTS_DLOPEN
        /*
          We do not call dlclose for plugins that link libgdal.
          This is a terrible hack, but necessary to prevent crashes
          at exit when gdal attempts to shutdown. The problem arises
          when Mapnik is used with another library that uses thread-local
          storage (like libuv). In this case GDAL also tries to cleanup thread
          local storage and leaves things in a state that causes libuv to crash.
          This is partially fixed by http://trac.osgeo.org/gdal/ticket/5509 but only
          in the case that gdal is linked as a shared library. This workaround therefore
          prevents crashes with gdal 1.11.x and gdal 2.x when using a static libgdal.
        */
        if (module_->dl && name_ != "gdal" && name_ != "ogr")
        {
            dlclose(module_->dl),module_->dl=0;
        }
#endif
        delete module_;
    }
}

void * PluginInfo::get_symbol(std::string const& sym_name) const
{
#ifdef MAPNIK_SUPPORTS_DLOPEN
    return static_cast<void *>(dlsym(module_->dl, sym_name.c_str()));
#else
    return NULL;
#endif
}

std::string const& PluginInfo::name() const
{
    return name_;
}

bool PluginInfo::valid() const
{
#ifdef MAPNIK_SUPPORTS_DLOPEN
    if (module_ && module_->dl && !name_.empty()) return true;
#endif
    return false;
}

std::string PluginInfo::get_error() const
{
    return std::string("could not open: '") + name_ + "'";
}

void PluginInfo::init()
{
    // do any initialization needed
}

void PluginInfo::exit()
{
    // do any shutdown needed
}

}
<|endoftext|>


<commit_before>#include <node.h>
#include <node_buffer.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mqueue.h>

#if defined(__linux__)
#  define MQDES_TO_FD(mqdes) (int)(mqdes)
#elif defined(__FreeBSD__)
#  define MQDES_TO_FD(mqdes) __mq_oshandle(mqdes)
#endif

using namespace node;
using namespace v8;

static Persistent<FunctionTemplate> constructor;
static Persistent<String> emit_symbol;
static Persistent<Value> read_emit_argv[1];
static Persistent<Value> write_emit_argv[1];
static const mqd_t MQDES_INVALID = (mqd_t)-1;

class PosixMQ : public ObjectWrap {
  public:
    mqd_t mqdes;
    struct mq_attr mqattrs;
    uv_poll_t* mqpollhandle;
    char* mqname;
    Persistent<Function> Emit;
    bool canread;
    bool canwrite;

    PosixMQ() : mqpollhandle(NULL), mqdes(MQDES_INVALID), mqname(NULL),
                canread(false), canwrite(false) {};

    ~PosixMQ() {
      if (mqdes != MQDES_INVALID) {
        close();
        delete mqpollhandle;
        mqpollhandle = NULL;
      }
      if (mqname) {
        free(mqname);
        mqname = NULL;
      }
      Emit.Dispose();
      Emit.Clear();
    }

    int close() {
      int r;
      uv_poll_stop(mqpollhandle);
      uv_close((uv_handle_t *)mqpollhandle, on_close);
      r = mq_close(mqdes);
      mqdes = MQDES_INVALID;
      return r;
    }

    static void on_close (uv_handle_t *handle) {}

    static Handle<Value> New(const Arguments& args) {
      HandleScope scope;

      if (!args.IsConstructCall()) {
        return ThrowException(Exception::TypeError(
            String::New("Use `new` to create instances of this object.")));
      }

      PosixMQ* obj = new PosixMQ();
      obj->Wrap(args.This());

      return args.This();
    }

    static Handle<Value> Open(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());
      bool doCreate = false;
      int flags = O_RDWR | O_NONBLOCK;
      mode_t mode;
      const char* name;

      if (args.Length() != 1) {
        return ThrowException(Exception::TypeError(
            String::New("Expecting 1 argument")));
      }
      if (!args[0]->IsObject()) {
        return ThrowException(Exception::TypeError(
            String::New("Argument must be an object")));
      }

      Local<Object> config = args[0]->ToObject();
      Local<Value> val;

      if (!(val = config->Get(String::New("create")))->IsUndefined()) {
        if (!val->IsBoolean()) {
          return ThrowException(Exception::TypeError(
              String::New("'create' property must be a boolean")));
        }
        doCreate = val->BooleanValue();
      }

      val = config->Get(String::New("name"));
      if (!val->IsString()) {
        return ThrowException(Exception::TypeError(
            String::New("'name' property must be a string")));
      }
      String::AsciiValue namestr(val->ToString());
      name = (const char*)(*namestr);

      val = config->Get(String::New("mode"));
      if (doCreate) {
        if (val->IsUint32())
          mode = (mode_t)val->Uint32Value();
        else if (val->IsString()) {
          String::AsciiValue modestr(val->ToString());
          mode = (mode_t)strtoul((const char*)(*modestr), NULL, 8);
        } else {
          return ThrowException(Exception::TypeError(
              String::New("'mode' property must be a string or integer")));
        }
        flags |= O_CREAT;

        val = config->Get(String::New("exclusive"));
        if (val->IsBoolean() && val->BooleanValue() == true)
          flags |= O_EXCL;

        val = config->Get(String::New("maxmsgs"));
        if (val->IsUint32())
          obj->mqattrs.mq_maxmsg = val->Uint32Value();
        else
          obj->mqattrs.mq_maxmsg = 10;
        val = config->Get(String::New("msgsize"));
        if (val->IsUint32())
          obj->mqattrs.mq_msgsize = val->Uint32Value();
        else
          obj->mqattrs.mq_msgsize = 8192;
      }

      if (obj->mqdes != MQDES_INVALID)
        obj->close();

      if (doCreate)
        obj->mqdes = mq_open(name, flags, mode, &(obj->mqattrs));
      else
        obj->mqdes = mq_open(name, flags);

      if (obj->mqdes == MQDES_INVALID ||
          mq_getattr(obj->mqdes, &(obj->mqattrs)) == -1) {
        return ThrowException(Exception::Error(
            String::New(uv_strerror(uv_last_error(uv_default_loop())))));
      }

      if (obj->mqname) {
        free(obj->mqname);
        obj->mqname = NULL;
      } else {
        obj->Emit = Persistent<Function>::New(Local<Function>::Cast(
                                               obj->handle_->Get(emit_symbol)));
      }

      obj->mqname = strdup(name);

      obj->canread = !(obj->mqattrs.mq_curmsgs > 0);
      obj->canwrite = !(obj->mqattrs.mq_curmsgs < obj->mqattrs.mq_maxmsg);

      if (!obj->mqpollhandle)
        obj->mqpollhandle = new uv_poll_t;
      obj->mqpollhandle->data = obj;
      uv_poll_init(uv_default_loop(), obj->mqpollhandle, MQDES_TO_FD(obj->mqdes));
      uv_poll_start(obj->mqpollhandle, UV_READABLE | UV_WRITABLE, poll_cb);

      return Undefined();
    }

    static void poll_cb(uv_poll_t *handle, int status, int events) {
      HandleScope scope;
      assert(status == 0);

      PosixMQ* obj = (PosixMQ*)handle->data;

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      if ((events & UV_READABLE) && !obj->canread) {
        obj->canread = true;

        TryCatch try_catch;
        obj->Emit->Call(obj->handle_, 1, read_emit_argv);
        if (try_catch.HasCaught())
          FatalException(try_catch);
      } else if (!(events & UV_READABLE))
        obj->canread = false;

      if ((events & UV_WRITABLE) && !obj->canwrite) {
        obj->canwrite = true;
        TryCatch try_catch;
        obj->Emit->Call(obj->handle_, 1, write_emit_argv);
        if (try_catch.HasCaught())
          FatalException(try_catch);
      } else if (!(events & UV_WRITABLE))
        obj->canwrite = false;
    }

    static Handle<Value> Close(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());

      if (obj->mqdes == MQDES_INVALID) {
        return ThrowException(Exception::Error(
            String::New("Queue already closed")));
      }

      int r = obj->close();

      if (r == -1) {
        return ThrowException(Exception::Error(
            String::New(uv_strerror(uv_last_error(uv_default_loop())))));
      }

      return Undefined();
    }

    static Handle<Value> Unlink(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());

      if (!obj->mqname) {
        return ThrowException(Exception::Error(
            String::New("Nothing to unlink")));
      }

      if (mq_unlink((const char*)obj->mqname) == -1) {
        return ThrowException(Exception::Error(
            String::New(uv_strerror(uv_last_error(uv_default_loop())))));
      }

      if (obj->mqname) {
        free(obj->mqname);
        obj->mqname = NULL;
      }

      return Undefined();
    }

    static Handle<Value> Send(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());
      uint32_t priority = 0;
      bool ret = true;

      if (args.Length() < 1) {
        return ThrowException(Exception::TypeError(
            String::New("Expected at least 1 argument")));
      } else if (!Buffer::HasInstance(args[0])) {
        return ThrowException(Exception::TypeError(
            String::New("First argument must be a Buffer")));
      } else if (args.Length() >= 2) {
        if (args[1]->IsUint32() && args[1]->Uint32Value() < 32)
          priority = args[1]->Uint32Value();
        else {
          return ThrowException(Exception::TypeError(
              String::New("Second argument must be an integer 0 <= n < 32")));
        }
      }

      if (mq_send(obj->mqdes, Buffer::Data(args[0]->ToObject()),
                  Buffer::Length(args[0]->ToObject()), priority) == -1) {
        if (errno != EAGAIN) {
          return ThrowException(Exception::Error(
              String::New(uv_strerror(uv_last_error(uv_default_loop())))));
        }
        ret = false;
      }

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Boolean::New(ret));
    }

    static Handle<Value> Receive(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());
      ssize_t nBytes;
      uint32_t priority;
      bool retTuple = false;
      Local<Value> ret;

      if (args.Length() < 1) {
        return ThrowException(Exception::TypeError(
            String::New("Expected at least 1 argument")));
      } else if (!Buffer::HasInstance(args[0])) {
        return ThrowException(Exception::TypeError(
            String::New("First argument must be a Buffer")));
      } else if (args.Length() > 1)
        retTuple = args[1]->BooleanValue();

      Local<Object> buf = args[0]->ToObject();
      if ((nBytes = mq_receive(obj->mqdes, Buffer::Data(buf),
                               Buffer::Length(buf), &priority)) == -1) {
        if (errno != EAGAIN) {
          return ThrowException(Exception::Error(
              String::New(uv_strerror(uv_last_error(uv_default_loop())))));
        }
        ret = Local<Value>::New(Boolean::New(false));
      } else if (!retTuple)
        ret = Integer::New(nBytes);
      else {
        Local<Array> tuple = Array::New(2);
        tuple->Set(0, Integer::New(nBytes));
        tuple->Set(1, Integer::New(priority));
        ret = tuple;
      }

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(ret);
    }

    static Handle<Value> MsgsizeGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Integer::New(obj->mqattrs.mq_msgsize));
    }

    static Handle<Value> MaxmsgsGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Integer::New(obj->mqattrs.mq_maxmsg));
    }

    static Handle<Value> CurmsgsGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Integer::New(obj->mqattrs.mq_curmsgs));
    }

    static Handle<Value> IsfullGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Boolean::New(obj->mqattrs.mq_curmsgs == obj->mqattrs.mq_maxmsg));
    }

    static void Initialize(Handle<Object> target) {
      HandleScope scope;

      Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
      Local<String> name = String::NewSymbol("PosixMQ");

      constructor = Persistent<FunctionTemplate>::New(tpl);
      constructor->InstanceTemplate()->SetInternalFieldCount(1);
      constructor->SetClassName(name);

      NODE_SET_PROTOTYPE_METHOD(constructor, "open", Open);
      NODE_SET_PROTOTYPE_METHOD(constructor, "close", Close);
      NODE_SET_PROTOTYPE_METHOD(constructor, "push", Send);
      NODE_SET_PROTOTYPE_METHOD(constructor, "shift", Receive);
      NODE_SET_PROTOTYPE_METHOD(constructor, "unlink", Unlink);

      constructor->PrototypeTemplate()->SetAccessor(String::New("msgsize"),
                                                    MsgsizeGetter);
      constructor->PrototypeTemplate()->SetAccessor(String::New("maxmsgs"),
                                                    MaxmsgsGetter);
      constructor->PrototypeTemplate()->SetAccessor(String::New("curmsgs"),
                                                    CurmsgsGetter);
      constructor->PrototypeTemplate()->SetAccessor(String::New("isFull"),
                                                    IsfullGetter);
      emit_symbol = NODE_PSYMBOL("emit");
      read_emit_argv[0] = NODE_PSYMBOL("messages");
      write_emit_argv[0] = NODE_PSYMBOL("drain");
      target->Set(name, constructor->GetFunction());
    }
};

extern "C" {
  void init(Handle<Object> target) {
    HandleScope scope;
    PosixMQ::Initialize(target);
  }

  NODE_MODULE(posixmq, init);
}
<commit_msg>prevent excessive polling<commit_after>#include <node.h>
#include <node_buffer.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mqueue.h>

#if defined(__linux__)
#  define MQDES_TO_FD(mqdes) (int)(mqdes)
#elif defined(__FreeBSD__)
#  define MQDES_TO_FD(mqdes) __mq_oshandle(mqdes)
#endif

using namespace node;
using namespace v8;

static Persistent<FunctionTemplate> constructor;
static Persistent<String> emit_symbol;
static Persistent<Value> read_emit_argv[1];
static Persistent<Value> write_emit_argv[1];
static const mqd_t MQDES_INVALID = (mqd_t)-1;

class PosixMQ : public ObjectWrap {
  public:
    mqd_t mqdes;
    struct mq_attr mqattrs;
    uv_poll_t* mqpollhandle;
    char* mqname;
    Persistent<Function> Emit;
    bool canread;
    bool canwrite;
    int eventmask;

    PosixMQ() : mqpollhandle(NULL), mqdes(MQDES_INVALID), mqname(NULL),
                canread(false), canwrite(false), eventmask(0) {};

    ~PosixMQ() {
      if (mqdes != MQDES_INVALID) {
        close();
        delete mqpollhandle;
        mqpollhandle = NULL;
      }
      if (mqname) {
        free(mqname);
        mqname = NULL;
      }
      Emit.Dispose();
      Emit.Clear();
    }

    int close() {
      int r;
      uv_poll_stop(mqpollhandle);
      uv_close((uv_handle_t *)mqpollhandle, on_close);
      r = mq_close(mqdes);
      mqdes = MQDES_INVALID;
      return r;
    }

    static void on_close (uv_handle_t *handle) {}

    static Handle<Value> New(const Arguments& args) {
      HandleScope scope;

      if (!args.IsConstructCall()) {
        return ThrowException(Exception::TypeError(
            String::New("Use `new` to create instances of this object.")));
      }

      PosixMQ* obj = new PosixMQ();
      obj->Wrap(args.This());

      return args.This();
    }

    static Handle<Value> Open(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());
      bool doCreate = false;
      int flags = O_RDWR | O_NONBLOCK;
      mode_t mode;
      const char* name;

      if (args.Length() != 1) {
        return ThrowException(Exception::TypeError(
            String::New("Expecting 1 argument")));
      }
      if (!args[0]->IsObject()) {
        return ThrowException(Exception::TypeError(
            String::New("Argument must be an object")));
      }

      Local<Object> config = args[0]->ToObject();
      Local<Value> val;

      if (!(val = config->Get(String::New("create")))->IsUndefined()) {
        if (!val->IsBoolean()) {
          return ThrowException(Exception::TypeError(
              String::New("'create' property must be a boolean")));
        }
        doCreate = val->BooleanValue();
      }

      val = config->Get(String::New("name"));
      if (!val->IsString()) {
        return ThrowException(Exception::TypeError(
            String::New("'name' property must be a string")));
      }
      String::AsciiValue namestr(val->ToString());
      name = (const char*)(*namestr);

      val = config->Get(String::New("mode"));
      if (doCreate) {
        if (val->IsUint32())
          mode = (mode_t)val->Uint32Value();
        else if (val->IsString()) {
          String::AsciiValue modestr(val->ToString());
          mode = (mode_t)strtoul((const char*)(*modestr), NULL, 8);
        } else {
          return ThrowException(Exception::TypeError(
              String::New("'mode' property must be a string or integer")));
        }
        flags |= O_CREAT;

        val = config->Get(String::New("exclusive"));
        if (val->IsBoolean() && val->BooleanValue() == true)
          flags |= O_EXCL;

        val = config->Get(String::New("maxmsgs"));
        if (val->IsUint32())
          obj->mqattrs.mq_maxmsg = val->Uint32Value();
        else
          obj->mqattrs.mq_maxmsg = 10;
        val = config->Get(String::New("msgsize"));
        if (val->IsUint32())
          obj->mqattrs.mq_msgsize = val->Uint32Value();
        else
          obj->mqattrs.mq_msgsize = 8192;
      }

      if (obj->mqdes != MQDES_INVALID)
        obj->close();

      if (doCreate)
        obj->mqdes = mq_open(name, flags, mode, &(obj->mqattrs));
      else
        obj->mqdes = mq_open(name, flags);

      if (obj->mqdes == MQDES_INVALID ||
          mq_getattr(obj->mqdes, &(obj->mqattrs)) == -1) {
        return ThrowException(Exception::Error(
            String::New(uv_strerror(uv_last_error(uv_default_loop())))));
      }

      if (obj->mqname) {
        free(obj->mqname);
        obj->mqname = NULL;
      } else {
        obj->Emit = Persistent<Function>::New(Local<Function>::Cast(
                                               obj->handle_->Get(emit_symbol)));
      }

      obj->mqname = strdup(name);

      obj->canread = !(obj->mqattrs.mq_curmsgs > 0);
      obj->canwrite = !(obj->mqattrs.mq_curmsgs < obj->mqattrs.mq_maxmsg);

      if (!obj->mqpollhandle)
        obj->mqpollhandle = new uv_poll_t;
      obj->mqpollhandle->data = obj;
      obj->eventmask = UV_READABLE | UV_WRITABLE;
      uv_poll_init(uv_default_loop(), obj->mqpollhandle, MQDES_TO_FD(obj->mqdes));
      uv_poll_start(obj->mqpollhandle, obj->eventmask, poll_cb);

      return Undefined();
    }

    static void poll_cb(uv_poll_t *handle, int status, int events) {
      HandleScope scope;
      assert(status == 0);

      PosixMQ* obj = (PosixMQ*)handle->data;

      //mq_getattr(obj->mqdes, &(obj->mqattrs));

      if ((events & UV_READABLE) && !obj->canread) {
        obj->eventmask &= ~UV_READABLE;
        obj->canread = true;

        TryCatch try_catch;
        obj->Emit->Call(obj->handle_, 1, read_emit_argv);
        if (try_catch.HasCaught())
          FatalException(try_catch);
      } else if (!(events & UV_READABLE)) {
        obj->eventmask |= UV_READABLE;
        obj->canread = false;
      }

      if ((events & UV_WRITABLE) && !obj->canwrite) {
        obj->eventmask &= ~UV_WRITABLE;
        obj->canwrite = true;

        TryCatch try_catch;
        obj->Emit->Call(obj->handle_, 1, write_emit_argv);
        if (try_catch.HasCaught())
          FatalException(try_catch);
      } else if (!(events & UV_WRITABLE)) {
        obj->eventmask |= UV_WRITABLE;
        obj->canwrite = false;
      }

      if (obj->mqdes != MQDES_INVALID)
        uv_poll_start(obj->mqpollhandle, obj->eventmask, poll_cb);
    }

    static Handle<Value> Close(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());

      if (obj->mqdes == MQDES_INVALID) {
        return ThrowException(Exception::Error(
            String::New("Queue already closed")));
      }

      int r = obj->close();

      if (r == -1) {
        return ThrowException(Exception::Error(
            String::New(uv_strerror(uv_last_error(uv_default_loop())))));
      }

      return Undefined();
    }

    static Handle<Value> Unlink(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());

      if (!obj->mqname) {
        return ThrowException(Exception::Error(
            String::New("Nothing to unlink")));
      }

      if (mq_unlink((const char*)obj->mqname) == -1) {
        return ThrowException(Exception::Error(
            String::New(uv_strerror(uv_last_error(uv_default_loop())))));
      }

      if (obj->mqname) {
        free(obj->mqname);
        obj->mqname = NULL;
      }

      return Undefined();
    }

    static Handle<Value> Send(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());
      uint32_t priority = 0;
      bool ret = true;

      if (args.Length() < 1) {
        return ThrowException(Exception::TypeError(
            String::New("Expected at least 1 argument")));
      } else if (!Buffer::HasInstance(args[0])) {
        return ThrowException(Exception::TypeError(
            String::New("First argument must be a Buffer")));
      } else if (args.Length() >= 2) {
        if (args[1]->IsUint32() && args[1]->Uint32Value() < 32)
          priority = args[1]->Uint32Value();
        else {
          return ThrowException(Exception::TypeError(
              String::New("Second argument must be an integer 0 <= n < 32")));
        }
      }

      if (mq_send(obj->mqdes, Buffer::Data(args[0]->ToObject()),
                  Buffer::Length(args[0]->ToObject()), priority) == -1) {
        if (errno != EAGAIN) {
          return ThrowException(Exception::Error(
              String::New(uv_strerror(uv_last_error(uv_default_loop())))));
        }
        ret = false;
      }

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Boolean::New(ret));
    }

    static Handle<Value> Receive(const Arguments& args) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(args.This());
      ssize_t nBytes;
      uint32_t priority;
      bool retTuple = false;
      Local<Value> ret;

      if (args.Length() < 1) {
        return ThrowException(Exception::TypeError(
            String::New("Expected at least 1 argument")));
      } else if (!Buffer::HasInstance(args[0])) {
        return ThrowException(Exception::TypeError(
            String::New("First argument must be a Buffer")));
      } else if (args.Length() > 1)
        retTuple = args[1]->BooleanValue();

      Local<Object> buf = args[0]->ToObject();
      if ((nBytes = mq_receive(obj->mqdes, Buffer::Data(buf),
                               Buffer::Length(buf), &priority)) == -1) {
        if (errno != EAGAIN) {
          return ThrowException(Exception::Error(
              String::New(uv_strerror(uv_last_error(uv_default_loop())))));
        }
        ret = Local<Value>::New(Boolean::New(false));
      } else if (!retTuple)
        ret = Integer::New(nBytes);
      else {
        Local<Array> tuple = Array::New(2);
        tuple->Set(0, Integer::New(nBytes));
        tuple->Set(1, Integer::New(priority));
        ret = tuple;
      }

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(ret);
    }

    static Handle<Value> MsgsizeGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Integer::New(obj->mqattrs.mq_msgsize));
    }

    static Handle<Value> MaxmsgsGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Integer::New(obj->mqattrs.mq_maxmsg));
    }

    static Handle<Value> CurmsgsGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Integer::New(obj->mqattrs.mq_curmsgs));
    }

    static Handle<Value> IsfullGetter (Local<String> property, const AccessorInfo& info) {
      HandleScope scope;
      PosixMQ* obj = ObjectWrap::Unwrap<PosixMQ>(info.This());

      mq_getattr(obj->mqdes, &(obj->mqattrs));

      return scope.Close(Boolean::New(obj->mqattrs.mq_curmsgs == obj->mqattrs.mq_maxmsg));
    }

    static void Initialize(Handle<Object> target) {
      HandleScope scope;

      Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
      Local<String> name = String::NewSymbol("PosixMQ");

      constructor = Persistent<FunctionTemplate>::New(tpl);
      constructor->InstanceTemplate()->SetInternalFieldCount(1);
      constructor->SetClassName(name);

      NODE_SET_PROTOTYPE_METHOD(constructor, "open", Open);
      NODE_SET_PROTOTYPE_METHOD(constructor, "close", Close);
      NODE_SET_PROTOTYPE_METHOD(constructor, "push", Send);
      NODE_SET_PROTOTYPE_METHOD(constructor, "shift", Receive);
      NODE_SET_PROTOTYPE_METHOD(constructor, "unlink", Unlink);

      constructor->PrototypeTemplate()->SetAccessor(String::New("msgsize"),
                                                    MsgsizeGetter);
      constructor->PrototypeTemplate()->SetAccessor(String::New("maxmsgs"),
                                                    MaxmsgsGetter);
      constructor->PrototypeTemplate()->SetAccessor(String::New("curmsgs"),
                                                    CurmsgsGetter);
      constructor->PrototypeTemplate()->SetAccessor(String::New("isFull"),
                                                    IsfullGetter);
      emit_symbol = NODE_PSYMBOL("emit");
      read_emit_argv[0] = NODE_PSYMBOL("messages");
      write_emit_argv[0] = NODE_PSYMBOL("drain");
      target->Set(name, constructor->GetFunction());
    }
};

extern "C" {
  void init(Handle<Object> target) {
    HandleScope scope;
    PosixMQ::Initialize(target);
  }

  NODE_MODULE(posixmq, init);
}
<|endoftext|>