<commit_before>/*
 * Copyright (c) 2008 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/cpuid.hh"
#include "base/bitfield.hh"
#include "cpu/thread_context.hh"

namespace X86ISA {
    enum StandardCpuidFunction {
        VendorAndLargestStdFunc,
        FamilyModelStepping,
        NumStandardCpuidFuncs
    };

    enum ExtendedCpuidFunctions {
        VendorAndLargestExtFunc,
        FamilyModelSteppingBrandFeatures,
        NameString1,
        NameString2,
        NameString3,
        L1CacheAndTLB,
        L2L3CacheAndL2TLB,
        APMInfo,
        LongModeAddressSize,

        /*
         * The following are defined by the spec but not yet implemented
         */
/*      // Function 9 is reserved
        SVMInfo = 10,
        // Functions 11-24 are reserved
        TLB1GBPageInfo = 25,
        PerformanceInfo,*/

        NumExtendedCpuidFuncs
    };

    static const int vendorStringSize = 13;
    static const char vendorString[vendorStringSize] = "M5 Simulator";
    static const int nameStringSize = 48;
    static const char nameString[nameStringSize] = "Fake M5 x86_64 CPU";

    uint64_t
    stringToRegister(const char *str)
    {
        uint64_t reg = 0;
        for (int pos = 3; pos >=0; pos--) {
            reg <<= 8;
            reg |= str[pos];
        }
        return reg;
    }

    bool
    doCpuid(ThreadContext * tc, uint32_t function,
            uint32_t index, CpuidResult &result)
    {
        uint16_t family = bits(function, 31, 16);
        uint16_t funcNum = bits(function, 15, 0);
        if (family == 0x8000) {
            // The extended functions
            switch (funcNum) {
              case VendorAndLargestExtFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        0x80000000 + NumExtendedCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelSteppingBrandFeatures:
                result = CpuidResult(0x00020f51, 0x00000405,
                                     0xe3d3fbff, 0x00000001);
                break;
              case NameString1:
              case NameString2:
              case NameString3:
                {
                    // Zero fill anything beyond the end of the string. This
                    // should go away once the string is a vetted parameter.
                    char cleanName[nameStringSize];
                    memset(cleanName, '\0', nameStringSize);
                    strncpy(cleanName, nameString, nameStringSize);

                    int offset = (funcNum - NameString1) * 16;
                    assert(nameStringSize >= offset + 16);
                    result = CpuidResult(
                            stringToRegister(cleanName + offset + 0),
                            stringToRegister(cleanName + offset + 4),
                            stringToRegister(cleanName + offset + 12),
                            stringToRegister(cleanName + offset + 8));
                }
                break;
              case L1CacheAndTLB:
                result = CpuidResult(0xff08ff08, 0xff20ff20,
                                     0x40020140, 0x40020140);
                break;
              case L2L3CacheAndL2TLB:
                result = CpuidResult(0x00000000, 0x42004200,
                                     0x00000000, 0x04008140);
                break;
              case APMInfo:
                result = CpuidResult(0x80000018, 0x68747541,
                                     0x69746e65, 0x444d4163);
                break;
              case LongModeAddressSize:
                result = CpuidResult(0x00003030, 0x00000000,
                                     0x00000000, 0x00000000);
                break;
/*            case SVMInfo:
              case TLB1GBPageInfo:
              case PerformanceInfo:*/
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else if(family == 0x0000) {
            // The standard functions
            switch (funcNum) {
              case VendorAndLargestStdFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        NumStandardCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelStepping:
                result = CpuidResult(0x00020f51, 0x00000805,
                                     0xe7dbfbff, 0x00000001);
                break;
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else {
            warn("x86 cpuid: unknown family %#x", family);
            return false;
        }

        return true;
    }
} // namespace X86ISA
<commit_msg>cpuid, x86: Enabling more features in CPUid Adding more features in the CPUid with the purpose of supporting running the KvmCPU in SE mode.<commit_after>/*
 * Copyright (c) 2008 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/cpuid.hh"
#include "base/bitfield.hh"
#include "cpu/thread_context.hh"

namespace X86ISA {
    enum StandardCpuidFunction {
        VendorAndLargestStdFunc,
        FamilyModelStepping,
        NumStandardCpuidFuncs
    };

    enum ExtendedCpuidFunctions {
        VendorAndLargestExtFunc,
        FamilyModelSteppingBrandFeatures,
        NameString1,
        NameString2,
        NameString3,
        L1CacheAndTLB,
        L2L3CacheAndL2TLB,
        APMInfo,
        LongModeAddressSize,

        /*
         * The following are defined by the spec but not yet implemented
         */
/*      // Function 9 is reserved
        SVMInfo = 10,
        // Functions 11-24 are reserved
        TLB1GBPageInfo = 25,
        PerformanceInfo,*/

        NumExtendedCpuidFuncs
    };

    static const int vendorStringSize = 13;
    static const char vendorString[vendorStringSize] = "M5 Simulator";
    static const int nameStringSize = 48;
    static const char nameString[nameStringSize] = "Fake M5 x86_64 CPU";

    uint64_t
    stringToRegister(const char *str)
    {
        uint64_t reg = 0;
        for (int pos = 3; pos >=0; pos--) {
            reg <<= 8;
            reg |= str[pos];
        }
        return reg;
    }

    bool
    doCpuid(ThreadContext * tc, uint32_t function,
            uint32_t index, CpuidResult &result)
    {
        uint16_t family = bits(function, 31, 16);
        uint16_t funcNum = bits(function, 15, 0);
        if (family == 0x8000) {
            // The extended functions
            switch (funcNum) {
              case VendorAndLargestExtFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        0x80000000 + NumExtendedCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelSteppingBrandFeatures:
                /** Features Enabling
                 * rdx, enabling most of the features except:
                 * FXSR, FFXSR, Page1GB in EDX
                 *
                 * rcx, disabling most of the features except:
                 * SSE4A, XOP, FMA4 in ECX
                 */
                result = CpuidResult(0x00020f51, 0x00000405,
                                     0xe3d3fbff, 0x00010840);
                break;
              case NameString1:
              case NameString2:
              case NameString3:
                {
                    // Zero fill anything beyond the end of the string. This
                    // should go away once the string is a vetted parameter.
                    char cleanName[nameStringSize];
                    memset(cleanName, '\0', nameStringSize);
                    strncpy(cleanName, nameString, nameStringSize);

                    int offset = (funcNum - NameString1) * 16;
                    assert(nameStringSize >= offset + 16);
                    result = CpuidResult(
                            stringToRegister(cleanName + offset + 0),
                            stringToRegister(cleanName + offset + 4),
                            stringToRegister(cleanName + offset + 12),
                            stringToRegister(cleanName + offset + 8));
                }
                break;
              case L1CacheAndTLB:
                result = CpuidResult(0xff08ff08, 0xff20ff20,
                                     0x40020140, 0x40020140);
                break;
              case L2L3CacheAndL2TLB:
                result = CpuidResult(0x00000000, 0x42004200,
                                     0x00000000, 0x04008140);
                break;
              case APMInfo:
                result = CpuidResult(0x80000018, 0x68747541,
                                     0x69746e65, 0x444d4163);
                break;
              case LongModeAddressSize:
                result = CpuidResult(0x00003030, 0x00000000,
                                     0x00000000, 0x00000000);
                break;
/*            case SVMInfo:
              case TLB1GBPageInfo:
              case PerformanceInfo:*/
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else if(family == 0x0000) {
            // The standard functions
            switch (funcNum) {
              case VendorAndLargestStdFunc:
                assert(vendorStringSize >= 12);
                result = CpuidResult(
                        NumStandardCpuidFuncs - 1,
                        stringToRegister(vendorString),
                        stringToRegister(vendorString + 4),
                        stringToRegister(vendorString + 8));
                break;
              case FamilyModelStepping:
                /** Features Enabling
                 * rdx, enabling most of the features except:
                 * HTT in EDX
                 *
                 * rcx, disabling most of the features except:
                 * SSE3, SSSE3, FMA, SSE41, XSAVE, AVX in ECX
                 */
                result = CpuidResult(0x00020f51, 0x00000805,
                                     0xe7dbfbff, 0x14081201);
                break;
              default:
                warn("x86 cpuid: unimplemented function %u", funcNum);
                return false;
            }
        } else {
            warn("x86 cpuid: unknown family %#x", family);
            return false;
        }

        return true;
    }
} // namespace X86ISA
<|endoftext|>


<commit_before>/*
The MIT License

Copyright (c) 2011 by Jorrit Tyberghein

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
 */

#include "apparesed.h"
#include "camera.h"

//---------------------------------------------------------------------------

Camera::Camera (AppAresEdit* aresed) : aresed (aresed)
{
  do_panning = false;
  do_gravity = false;
  camera = 0;
}

void Camera::EnablePanning (const csVector3& center)
{
  do_panning = true;
  panningCenter = center;
}

void Camera::DisablePanning ()
{
  do_panning = false;
}

// Convenience function to get a csVector3 from a config file.
static csVector3 GetConfigVector (iConfigFile* config,
        const char* key, const char* def)
{
  csVector3 v;
  csScanStr (config->GetStr (key, def), "%f,%f,%f", &v.x, &v.y, &v.z);
  return v;
}

void Camera::Init (iCamera* camera, iSector* sector, const csVector3& pos)
{
  Camera::camera = camera;

  // Now we need to position the camera in our world.
  camera->SetSector (sector);
  camera->GetTransform ().SetOrigin (pos);
  current.pos = pos;
  current.rot = csVector3 (0, 0, 0);
  desired = current;

  // Initialize our collider actor.
  collider_actor.SetCollideSystem (aresed->GetCollisionSystem ());
  collider_actor.SetEngine (aresed->GetEngine ());
  collider_actor.SetGravity (0);

  // Creates an accessor for configuration settings.
  csConfigAccess cfgAcc (aresed->GetObjectRegistry ());

  // Read the values from config files, and use default values if not set.
  csVector3 legs (GetConfigVector (cfgAcc, "Actor.Legs", "0.2,0.5,0.2"));
  csVector3 body (GetConfigVector (cfgAcc, "Actor.Body", "0.2,1.8,0.2"));
  csVector3 shift (GetConfigVector (cfgAcc, "Actor.Shift", "0.0,-1.7,0.0"));
  collider_actor.InitializeColliders (camera, legs, body, shift);
}

static float InterpolateAngle (float desired, float current, float damping)
{
  float PI2 = PI * 2.0f;
  while (desired < 0) desired += PI2;
  while (desired >= PI2) desired -= PI2;
  while (current < 0) current += PI2;
  while (current >= PI2) current -= PI2;

  if (fabs (current-desired) < PI)
    return current + (desired - current) * damping;

  if (current < desired)
    current += PI2;
  else
    desired += PI2;

  current += (desired - current) * damping;
  if (current >= PI2) current -= PI2;
  else if (current < 0) current += PI2;
  return current;
}

void Camera::InterpolateCamera (csTicks elapsed_time)
{
  float sqdist = csSquaredDist::PointPoint (current.pos, desired.pos);
  bool rotEqual = (current.rot - desired.rot).IsZero ();
  if (sqdist < 0.02f && rotEqual) return;

  float damping = 10.0f * float (elapsed_time) / 1000.0f;
  if (damping >= 1.0f) damping = 1.0f;
  current.pos += (desired.pos - current.pos) * damping;

  current.rot.x = InterpolateAngle (desired.rot.x, current.rot.x, damping);
  current.rot.y = InterpolateAngle (desired.rot.y, current.rot.y, damping);
  current.rot.z = InterpolateAngle (desired.rot.z, current.rot.z, damping);

  SetCameraTransform (current);
}

void Camera::Frame (csTicks elapsed_time)
{
  if (!do_gravity)
    InterpolateCamera (elapsed_time);

  iKeyboardDriver* kbd = aresed->GetKeyboardDriver ();
  bool slow = kbd->GetKeyState (CSKEY_CTRL);

  csVector3 obj_move (0);
  csVector3 obj_rotate (0);
  if (kbd->GetKeyState (CSKEY_SHIFT))
  {
    // If the user is holding down shift, the arrow keys will cause
    // the camera to strafe up, down, left or right from it's
    // current position.
    if (kbd->GetKeyState ('d')) obj_move = CS_VEC_RIGHT * 3.0f;
    if (kbd->GetKeyState ('a')) obj_move = CS_VEC_LEFT * 3.0f;
    if (kbd->GetKeyState ('w')) obj_move = CS_VEC_UP * 3.0f;
    if (kbd->GetKeyState ('s')) obj_move = CS_VEC_DOWN * 3.0f;
  }
  else
  {
    // left and right cause the camera to rotate on the global Y
    // axis; page up and page down cause the camera to rotate on the
    // _camera's_ X axis (more on this in a second) and up and down
    // arrows cause the camera to go forwards and backwards.
    if (kbd->GetKeyState ('d')) obj_rotate.Set (0, 1, 0);
    if (kbd->GetKeyState ('a')) obj_rotate.Set (0, -1, 0);
    if (kbd->GetKeyState (CSKEY_PGUP)) obj_rotate.Set (1, 0, 0);
    if (kbd->GetKeyState (CSKEY_PGDN)) obj_rotate.Set (-1, 0, 0);
    if (kbd->GetKeyState ('w')) obj_move = CS_VEC_FORWARD * 3.0f;
    if (kbd->GetKeyState ('s')) obj_move = CS_VEC_BACKWARD * 3.0f;
  }

  if (do_gravity)
  {
    const float speed = elapsed_time / 1000.0;
    collider_actor.Move (speed, slow ? 0.5f : 2.0f, obj_move, obj_rotate);
  }
  else
  {
    float speed = slow ? 0.1f : 1.0f;
    CamMoveRelative (obj_move * speed, obj_rotate * speed);
  }
}

bool Camera::OnMouseDown (iEvent& ev, uint but, int mouseX, int mouseY)
{
  if (do_gravity) return false;

  if (but == 3)	// MouseWheelUp
  {
    CamZoom (mouseX, mouseY);
    return true;
  }
  else if (but == 4)	// MouseWheelDown
  {
    CamMoveRelative (CS_VEC_BACKWARD * 10.0f, csVector3 (0));
    return true;
  }
  return false;
}

bool Camera::OnMouseUp (iEvent& ev, uint but, int mouseX, int mouseY)
{
  return false;
}

void Camera::SetCameraTransform (const CamLocation& loc)
{
  camera->GetTransform ().SetOrigin (loc.pos);

  csMatrix3 rot;
  if (fabs (loc.rot.x) < SMALL_EPSILON && fabs (loc.rot.z) < SMALL_EPSILON)
    rot = csYRotMatrix3 (loc.rot.y);
  else
    rot = csXRotMatrix3 (loc.rot.x) * csYRotMatrix3 (loc.rot.y) * csZRotMatrix3 (loc.rot.z);
  csOrthoTransform ot (rot, camera->GetTransform().GetOrigin ());
  camera->SetTransform (ot);
}

CamLocation Camera::GetCameraLocation ()
{
  if (do_gravity)
  {
    CamLocation loc;
    loc.pos = camera->GetTransform ().GetOrigin ();
    loc.rot = collider_actor.GetRotation ();
    return loc;
  }
  else
    return current;
}

void Camera::SetCameraLocation (const CamLocation& loc)
{
  desired = loc;
  if (do_gravity)
  {
    camera->GetTransform ().SetOrigin (loc.pos);
    collider_actor.SetRotation (loc.rot);
  }
}

void Camera::CamMove (const csVector3& pos)
{
  desired.pos = pos;
  if (do_gravity)
    camera->GetTransform ().SetOrigin (pos);
}

void Camera::CamMoveRelative (const csVector3& offset, const csVector3& rotate)
{
  if (do_gravity)
  {
    desired = GetCameraLocation ();
  }
  desired.pos += camera->GetTransform ().This2OtherRelative (offset);
  desired.rot += rotate / 10.0f;
  if (do_gravity)
  {
    camera->GetTransform ().SetOrigin (desired.pos);
    collider_actor.SetRotation (desired.rot);
  }
}

void Camera::CamMoveAndLookAt (const csVector3& pos, const csVector3& rot)
{
  desired.pos = pos;
  desired.rot = rot;
  if (do_gravity)
    SetCameraLocation (desired);
}

void Camera::CamLookAt (const csVector3& rot)
{
  desired.rot = rot;
  if (do_gravity)
    collider_actor.SetRotation (desired.rot);
}

static float GetAngle2D (float dx, float dy)
{
  if (dy < 0)
    return 2 * M_PI - acos (dx);
  else
    return acos (dx);
}

static float GetHorizontalAngle (const csVector3& diff)
{
  csVector2 diff2 (diff.x, diff.z);
  float n = diff2.Norm ();
  if (fabs (n) < .00001) return 0.0;
  diff2 /= n;
  return GetAngle2D (diff2.y, diff2.x);
}

static float GetVerticalAngle (const csVector3& diff)
{
  csVector2 diff2 (diff.y, diff.z);
  float n = diff2.Norm ();
  if (fabs (n) < .00001) return 0.0;
  diff2 /= n;
  return GetAngle2D (diff2.y, diff2.x);
}

void Camera::CamLookAtPosition (const csVector3& center)
{
  csVector3 diff = center - camera->GetTransform ().GetOrigin ();
  float hangle = GetHorizontalAngle (diff);
  float vangle = GetVerticalAngle (diff);
  //printf ("diff=%g,%g,%g hangle=%g vangle=%g -> ", diff.x, diff.y, diff.z, hangle, vangle); fflush (stdout);
  if (hangle >= PI/2.0f && hangle <= PI*1.5f)
  {
    vangle = PI - vangle;
  }
  CamLookAt (csVector3 (vangle, hangle, 0));
}

void Camera::CamZoom (int x, int y)
{
  csVector2 v2d (x, aresed->GetG2D ()->GetHeight () - y);
  csVector3 v3d = camera->InvPerspective (v2d, 10);
  csVector3 endBeam = camera->GetTransform ().This2Other (v3d);

  CamLookAtPosition (endBeam);
  CamMove (endBeam);
}

void Camera::EnableGravity ()
{
  do_gravity = true;
  collider_actor.SetGravity (9.806);
  CamMoveAndLookAt (current.pos, current.rot);
}

void Camera::DisableGravity ()
{
  do_gravity = false;
  collider_actor.SetGravity (0);
  if (camera)
  {
    current.pos = camera->GetTransform ().GetOrigin ();
    current.rot = collider_actor.GetRotation ();
    desired = current;
  }
}

<commit_msg>Finally! I got a perfect lookat function (using quaternions). This also makes the zoom much nicer. The angle interpolation is occasionally still a bit wrong in the sense that it doesn't always pick the shortest interpolation path. I guess that using quaternions there too might be a solution. Possibly moving completely towards quaternions is the best thing to do so that's what I'm going to try in a moment.<commit_after>/*
The MIT License

Copyright (c) 2011 by Jorrit Tyberghein

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
 */

#include "apparesed.h"
#include "camera.h"

//---------------------------------------------------------------------------

Camera::Camera (AppAresEdit* aresed) : aresed (aresed)
{
  do_panning = false;
  do_gravity = false;
  camera = 0;
}

void Camera::EnablePanning (const csVector3& center)
{
  do_panning = true;
  panningCenter = center;
}

void Camera::DisablePanning ()
{
  do_panning = false;
}

// Convenience function to get a csVector3 from a config file.
static csVector3 GetConfigVector (iConfigFile* config,
        const char* key, const char* def)
{
  csVector3 v;
  csScanStr (config->GetStr (key, def), "%f,%f,%f", &v.x, &v.y, &v.z);
  return v;
}

void Camera::Init (iCamera* camera, iSector* sector, const csVector3& pos)
{
  Camera::camera = camera;

  // Now we need to position the camera in our world.
  camera->SetSector (sector);
  camera->GetTransform ().SetOrigin (pos);
  current.pos = pos;
  current.rot = csVector3 (0, 0, 0);
  desired = current;

  // Initialize our collider actor.
  collider_actor.SetCollideSystem (aresed->GetCollisionSystem ());
  collider_actor.SetEngine (aresed->GetEngine ());
  collider_actor.SetGravity (0);

  // Creates an accessor for configuration settings.
  csConfigAccess cfgAcc (aresed->GetObjectRegistry ());

  // Read the values from config files, and use default values if not set.
  csVector3 legs (GetConfigVector (cfgAcc, "Actor.Legs", "0.2,0.5,0.2"));
  csVector3 body (GetConfigVector (cfgAcc, "Actor.Body", "0.2,1.8,0.2"));
  csVector3 shift (GetConfigVector (cfgAcc, "Actor.Shift", "0.0,-1.7,0.0"));
  collider_actor.InitializeColliders (camera, legs, body, shift);
}

static float InterpolateAngle (float desired, float current, float damping)
{
  float PI2 = PI * 2.0f;
  while (desired < 0) desired += PI2;
  while (desired >= PI2) desired -= PI2;
  while (current < 0) current += PI2;
  while (current >= PI2) current -= PI2;

  if (fabs (current-desired) < PI)
    return current + (desired - current) * damping;

  if (current < desired)
    current += PI2;
  else
    desired += PI2;

  current += (desired - current) * damping;
  if (current >= PI2) current -= PI2;
  else if (current < 0) current += PI2;
  return current;
}

void Camera::InterpolateCamera (csTicks elapsed_time)
{
  float sqdist = csSquaredDist::PointPoint (current.pos, desired.pos);
  bool rotEqual = (current.rot - desired.rot).IsZero ();
  if (sqdist < 0.02f && rotEqual) return;

  float damping = 10.0f * float (elapsed_time) / 1000.0f;
  if (damping >= 1.0f) damping = 1.0f;
  current.pos += (desired.pos - current.pos) * damping;

  current.rot.x = InterpolateAngle (desired.rot.x, current.rot.x, damping);
  current.rot.y = InterpolateAngle (desired.rot.y, current.rot.y, damping);
  current.rot.z = InterpolateAngle (desired.rot.z, current.rot.z, damping);

  SetCameraTransform (current);
}

void Camera::Frame (csTicks elapsed_time)
{
  if (!do_gravity)
    InterpolateCamera (elapsed_time);

  iKeyboardDriver* kbd = aresed->GetKeyboardDriver ();
  bool slow = kbd->GetKeyState (CSKEY_CTRL);

  csVector3 obj_move (0);
  csVector3 obj_rotate (0);
  if (kbd->GetKeyState (CSKEY_SHIFT))
  {
    // If the user is holding down shift, the arrow keys will cause
    // the camera to strafe up, down, left or right from it's
    // current position.
    if (kbd->GetKeyState ('d')) obj_move = CS_VEC_RIGHT * 3.0f;
    if (kbd->GetKeyState ('a')) obj_move = CS_VEC_LEFT * 3.0f;
    if (kbd->GetKeyState ('w')) obj_move = CS_VEC_UP * 3.0f;
    if (kbd->GetKeyState ('s')) obj_move = CS_VEC_DOWN * 3.0f;
  }
  else
  {
    // left and right cause the camera to rotate on the global Y
    // axis; page up and page down cause the camera to rotate on the
    // _camera's_ X axis (more on this in a second) and up and down
    // arrows cause the camera to go forwards and backwards.
    if (kbd->GetKeyState ('d')) obj_rotate.Set (0, 1, 0);
    if (kbd->GetKeyState ('a')) obj_rotate.Set (0, -1, 0);
    if (kbd->GetKeyState (CSKEY_PGUP)) obj_rotate.Set (1, 0, 0);
    if (kbd->GetKeyState (CSKEY_PGDN)) obj_rotate.Set (-1, 0, 0);
    if (kbd->GetKeyState ('w')) obj_move = CS_VEC_FORWARD * 3.0f;
    if (kbd->GetKeyState ('s')) obj_move = CS_VEC_BACKWARD * 3.0f;
  }

  if (do_gravity)
  {
    const float speed = elapsed_time / 1000.0;
    collider_actor.Move (speed, slow ? 0.5f : 2.0f, obj_move, obj_rotate);
  }
  else
  {
    float speed = slow ? 0.1f : 1.0f;
    CamMoveRelative (obj_move * speed, obj_rotate * speed);
  }
}

bool Camera::OnMouseDown (iEvent& ev, uint but, int mouseX, int mouseY)
{
  if (do_gravity) return false;

  if (but == 3)	// MouseWheelUp
  {
    CamZoom (mouseX, mouseY);
    return true;
  }
  else if (but == 4)	// MouseWheelDown
  {
    CamMoveRelative (CS_VEC_BACKWARD * 10.0f, csVector3 (0));
    return true;
  }
  return false;
}

bool Camera::OnMouseUp (iEvent& ev, uint but, int mouseX, int mouseY)
{
  return false;
}

void Camera::SetCameraTransform (const CamLocation& loc)
{
  camera->GetTransform ().SetOrigin (loc.pos);

  csMatrix3 rot;
  if (fabs (loc.rot.x) < SMALL_EPSILON && fabs (loc.rot.z) < SMALL_EPSILON)
    rot = csYRotMatrix3 (loc.rot.y);
  else
    rot = csXRotMatrix3 (loc.rot.x) * csYRotMatrix3 (loc.rot.y) * csZRotMatrix3 (loc.rot.z);

#if 0
  // The code below is equivalent to the code above.
  // Don't know if it is better ot not though.
  csQuaternion quat;
  csVector3 r = loc.rot;
  r.x = -r.x;
  quat.SetEulerAngles (r);
  rot.Set (quat);
  rot.Invert ();
#endif

  csOrthoTransform ot (rot, camera->GetTransform().GetOrigin ());
  camera->SetTransform (ot);
}

CamLocation Camera::GetCameraLocation ()
{
  if (do_gravity)
  {
    CamLocation loc;
    loc.pos = camera->GetTransform ().GetOrigin ();
    loc.rot = collider_actor.GetRotation ();
    return loc;
  }
  else
    return current;
}

void Camera::SetCameraLocation (const CamLocation& loc)
{
  desired = loc;
  if (do_gravity)
  {
    camera->GetTransform ().SetOrigin (loc.pos);
    collider_actor.SetRotation (loc.rot);
  }
}

void Camera::CamMove (const csVector3& pos)
{
  desired.pos = pos;
  if (do_gravity)
    camera->GetTransform ().SetOrigin (pos);
}

void Camera::CamMoveRelative (const csVector3& offset, const csVector3& rotate)
{
  if (do_gravity)
  {
    desired = GetCameraLocation ();
  }
  desired.pos += camera->GetTransform ().This2OtherRelative (offset);
  desired.rot += rotate / 10.0f;
  if (do_gravity)
  {
    camera->GetTransform ().SetOrigin (desired.pos);
    collider_actor.SetRotation (desired.rot);
  }
}

void Camera::CamMoveAndLookAt (const csVector3& pos, const csVector3& rot)
{
  desired.pos = pos;
  desired.rot = rot;
  if (do_gravity)
    SetCameraLocation (desired);
}

void Camera::CamLookAt (const csVector3& rot)
{
  desired.rot = rot;
  if (do_gravity)
    collider_actor.SetRotation (desired.rot);
}

void Camera::CamLookAtPosition (const csVector3& center)
{
  csVector3 diff = center - camera->GetTransform ().GetOrigin ();

  csOrthoTransform trans = camera->GetTransform ();
  trans.LookAt (diff, csVector3 (0, 1, 0));
  csQuaternion quat;
  quat.SetMatrix (trans.GetT2O ());
  csVector3 euler = quat.GetEulerAngles ();
  euler.x = -euler.x;
  CamLookAt (euler);
}

void Camera::CamZoom (int x, int y)
{
  csVector2 v2d (x, aresed->GetG2D ()->GetHeight () - y);
  csVector3 v3d = camera->InvPerspective (v2d, 10);
  csVector3 endBeam = camera->GetTransform ().This2Other (v3d);

  CamLookAtPosition (endBeam);
  CamMove (endBeam);
}

void Camera::EnableGravity ()
{
  do_gravity = true;
  collider_actor.SetGravity (9.806);
  CamMoveAndLookAt (current.pos, current.rot);
}

void Camera::DisableGravity ()
{
  do_gravity = false;
  collider_actor.SetGravity (0);
  if (camera)
  {
    current.pos = camera->GetTransform ().GetOrigin ();
    current.rot = collider_actor.GetRotation ();
    desired = current;
  }
}

<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// 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
//
// 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 "precompiled.h"
#include "common_generated.h"
#include "fplbase/asset_manager.h"
#include "fplbase/flatbuffer_utils.h"
#include "fplbase/utilities.h"
#include "materials_generated.h"
#include "mesh_generated.h"

namespace fpl {

static_assert(kBlendModeOff == static_cast<BlendMode>(matdef::BlendMode_OFF) &&
                  kBlendModeTest ==
                      static_cast<BlendMode>(matdef::BlendMode_TEST) &&
                  kBlendModeAlpha ==
                      static_cast<BlendMode>(matdef::BlendMode_ALPHA),
              "BlendMode enums in renderer.h and material.fbs must match.");
static_assert(kBlendModeCount == kBlendModeAlpha + 1,
              "Please update static_assert above with new enum values.");

template <typename T>
T FindInMap(const std::map<std::string, T> &map, const char *name) {
  auto it = map.find(name);
  return it != map.end() ? it->second : 0;
}

AssetManager::AssetManager(Renderer &renderer)
    : renderer_(renderer) {
  // Empty material for default case.
  material_map_[""] = new Material();
}

Shader *AssetManager::FindShader(const char *basename) {
  return FindInMap(shader_map_, basename);
}

Shader *AssetManager::LoadShader(const char *basename) {
  auto shader = FindShader(basename);
  if (shader) return shader;
  std::string vs_file, ps_file;
  std::string filename = std::string(basename) + ".glslv";
  std::string failedfile;
  if (LoadFileWithIncludes(filename.c_str(), &vs_file, &failedfile)) {
    filename = std::string(basename) + ".glslf";
    if (LoadFileWithIncludes(filename.c_str(), &ps_file, &failedfile)) {
      shader = renderer_.CompileAndLinkShader(vs_file.c_str(), ps_file.c_str());
      if (shader) {
        shader_map_[basename] = shader;
      } else {
        LogError(kError, "Shader Error:\n%s\nVS:\n%s\nPS:\n%s\n",
                 renderer_.last_error().c_str(),
                 vs_file.c_str(), ps_file.c_str());
      }
      return shader;
    }
  }
  LogError(kError, "Can\'t load shader file: %s", failedfile.c_str());
  renderer_.last_error() = "Couldn\'t load: " + failedfile;
  return nullptr;
}

Texture *AssetManager::FindTexture(const char *filename) {
  return FindInMap(texture_map_, filename);
}

Texture *AssetManager::LoadTexture(const char *filename,
                                      TextureFormat format) {
  auto tex = FindTexture(filename);
  if (tex) return tex;
  tex = new Texture(renderer_, filename);
  tex->set_desired_format(format);
  loader_.QueueJob(tex);
  texture_map_[filename] = tex;
  return tex;
}

void AssetManager::StartLoadingTextures() { loader_.StartLoading(); }

bool AssetManager::TryFinalize() { return loader_.TryFinalize(); }

Material *AssetManager::FindMaterial(const char *filename) {
  return FindInMap(material_map_, filename);
}

Material *AssetManager::LoadMaterial(const char *filename) {
  auto mat = FindMaterial(filename);
  if (mat) return mat;
  std::string flatbuf;
  if (LoadFile(filename, &flatbuf)) {
    flatbuffers::Verifier verifier(
        reinterpret_cast<const uint8_t *>(flatbuf.c_str()), flatbuf.length());
    assert(matdef::VerifyMaterialBuffer(verifier));
    auto matdef = matdef::GetMaterial(flatbuf.c_str());
    mat = new Material();
    mat->set_blend_mode(static_cast<BlendMode>(matdef->blendmode()));
    for (size_t i = 0; i < matdef->texture_filenames()->size(); i++) {
      auto format =
          matdef->desired_format() && i < matdef->desired_format()->size()
              ? static_cast<TextureFormat>(matdef->desired_format()->Get(i))
              : kFormatAuto;
      auto tex =
          LoadTexture(matdef->texture_filenames()->Get(i)->c_str(), format);
      mat->textures().push_back(tex);
    }
    material_map_[filename] = mat;
    return mat;
  }
  renderer_.last_error() = std::string("Couldn\'t load: ") + filename;
  return nullptr;
}

void AssetManager::UnloadMaterial(const char *filename) {
  auto mat = FindMaterial(filename);
  if (!mat) return;
  mat->DeleteTextures();
  material_map_.erase(filename);
  for (auto it = mat->textures().begin(); it != mat->textures().end(); ++it) {
    texture_map_.erase((*it)->filename());
  }
}

Mesh *AssetManager::FindMesh(const char *filename) {
  return FindInMap(mesh_map_, filename);
}

template <typename T>
void CopyAttribute(const T *attr, uint8_t *&buf) {
  auto dest = (T *)buf;
  *dest = *attr;
  buf += sizeof(T);
}

Mesh *AssetManager::LoadMesh(const char *filename) {
  auto mesh = FindMesh(filename);
  if (mesh) return mesh;
  std::string flatbuf;
  if (LoadFile(filename, &flatbuf)) {
    flatbuffers::Verifier verifier(
        reinterpret_cast<const uint8_t *>(flatbuf.c_str()), flatbuf.length());
    assert(meshdef::VerifyMeshBuffer(verifier));
    auto meshdef = meshdef::GetMesh(flatbuf.c_str());
    // Collect what attributes are available.
    std::vector<Attribute> attrs;
    attrs.push_back(kPosition3f);
    if (meshdef->normals()) attrs.push_back(kNormal3f);
    if (meshdef->tangents()) attrs.push_back(kTangent4f);
    if (meshdef->colors()) attrs.push_back(kColor4ub);
    if (meshdef->texcoords()) attrs.push_back(kTexCoord2f);
    attrs.push_back(kEND);
    auto vert_size = Mesh::VertexSize(attrs.data());
    // Create an interleaved buffer. Would be cool to do this without
    // the additional copy, but that's not easy in OpenGL.
    // Could use multiple buffers instead, but likely less efficient.
    auto buf = new uint8_t[vert_size * meshdef->positions()->Length()];
    auto p = buf;
    for (size_t i = 0; i < meshdef->positions()->Length(); i++) {
      if (meshdef->positions()) CopyAttribute(meshdef->positions()->Get(i), p);
      if (meshdef->normals()) CopyAttribute(meshdef->normals()->Get(i), p);
      if (meshdef->tangents()) CopyAttribute(meshdef->tangents()->Get(i), p);
      if (meshdef->colors()) CopyAttribute(meshdef->colors()->Get(i), p);
      if (meshdef->texcoords()) CopyAttribute(meshdef->texcoords()->Get(i), p);
    }
    vec3 max = meshdef->max_position() ? LoadVec3(meshdef->max_position())
                                       : mathfu::kZeros3f;
    vec3 min = meshdef->min_position() ? LoadVec3(meshdef->min_position())
                                       : mathfu::kZeros3f;
    mesh = new Mesh(buf, meshdef->positions()->Length(), vert_size,
                    attrs.data(), meshdef->max_position() ? &max : nullptr,
                    meshdef->min_position() ? &min : nullptr);
    delete[] buf;
    // Load indices an materials.
    for (size_t i = 0; i < meshdef->surfaces()->size(); i++) {
      auto surface = meshdef->surfaces()->Get(i);
      auto mat = LoadMaterial(surface->material()->c_str());
      if (!mat) {
        delete mesh;
        return nullptr;
      }  // Error msg already set.
      mesh->AddIndices(
          reinterpret_cast<const uint16_t *>(surface->indices()->Data()),
          surface->indices()->Length(), mat);
    }
    mesh_map_[filename] = mesh;
    return mesh;
  }
  renderer_.last_error() = std::string("Couldn\'t load: ") + filename;
  return nullptr;
}

void AssetManager::UnloadMesh(const char *filename) {
  auto mesh = FindMesh(filename);
  if (!mesh) return;
  mesh_map_.erase(filename);
  delete mesh;
}

}  // namespace fpl
<commit_msg>Made the error messages for shader loading easier to read.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// 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
//
// 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 "precompiled.h"
#include "common_generated.h"
#include "fplbase/asset_manager.h"
#include "fplbase/flatbuffer_utils.h"
#include "fplbase/utilities.h"
#include "materials_generated.h"
#include "mesh_generated.h"

namespace fpl {

static_assert(kBlendModeOff == static_cast<BlendMode>(matdef::BlendMode_OFF) &&
                  kBlendModeTest ==
                      static_cast<BlendMode>(matdef::BlendMode_TEST) &&
                  kBlendModeAlpha ==
                      static_cast<BlendMode>(matdef::BlendMode_ALPHA),
              "BlendMode enums in renderer.h and material.fbs must match.");
static_assert(kBlendModeCount == kBlendModeAlpha + 1,
              "Please update static_assert above with new enum values.");

template <typename T>
T FindInMap(const std::map<std::string, T> &map, const char *name) {
  auto it = map.find(name);
  return it != map.end() ? it->second : 0;
}

AssetManager::AssetManager(Renderer &renderer)
    : renderer_(renderer) {
  // Empty material for default case.
  material_map_[""] = new Material();
}

Shader *AssetManager::FindShader(const char *basename) {
  return FindInMap(shader_map_, basename);
}

Shader *AssetManager::LoadShader(const char *basename) {
  auto shader = FindShader(basename);
  if (shader) return shader;
  std::string vs_file, ps_file;
  std::string filename = std::string(basename) + ".glslv";
  std::string failedfile;
  if (LoadFileWithIncludes(filename.c_str(), &vs_file, &failedfile)) {
    filename = std::string(basename) + ".glslf";
    if (LoadFileWithIncludes(filename.c_str(), &ps_file, &failedfile)) {
      shader = renderer_.CompileAndLinkShader(vs_file.c_str(), ps_file.c_str());
      if (shader) {
        shader_map_[basename] = shader;
      } else {
        LogError(kError, "Shader Error: ");
        LogError(kError, "VS:  -----------------------------------");
        LogError(kError, "%s", vs_file.c_str());
        LogError(kError, "PS:  -----------------------------------");
        LogError(kError, "%s", ps_file.c_str());
        LogError(kError, "----------------------------------------");
        LogError(kError, "%s", renderer_.last_error().c_str());
      }
      return shader;
    }
  }
  LogError(kError, "Can\'t load shader file: %s", failedfile.c_str());
  renderer_.last_error() = "Couldn\'t load: " + failedfile;
  return nullptr;
}

Texture *AssetManager::FindTexture(const char *filename) {
  return FindInMap(texture_map_, filename);
}

Texture *AssetManager::LoadTexture(const char *filename,
                                      TextureFormat format) {
  auto tex = FindTexture(filename);
  if (tex) return tex;
  tex = new Texture(renderer_, filename);
  tex->set_desired_format(format);
  loader_.QueueJob(tex);
  texture_map_[filename] = tex;
  return tex;
}

void AssetManager::StartLoadingTextures() { loader_.StartLoading(); }

bool AssetManager::TryFinalize() { return loader_.TryFinalize(); }

Material *AssetManager::FindMaterial(const char *filename) {
  return FindInMap(material_map_, filename);
}

Material *AssetManager::LoadMaterial(const char *filename) {
  auto mat = FindMaterial(filename);
  if (mat) return mat;
  std::string flatbuf;
  if (LoadFile(filename, &flatbuf)) {
    flatbuffers::Verifier verifier(
        reinterpret_cast<const uint8_t *>(flatbuf.c_str()), flatbuf.length());
    assert(matdef::VerifyMaterialBuffer(verifier));
    auto matdef = matdef::GetMaterial(flatbuf.c_str());
    mat = new Material();
    mat->set_blend_mode(static_cast<BlendMode>(matdef->blendmode()));
    for (size_t i = 0; i < matdef->texture_filenames()->size(); i++) {
      auto format =
          matdef->desired_format() && i < matdef->desired_format()->size()
              ? static_cast<TextureFormat>(matdef->desired_format()->Get(i))
              : kFormatAuto;
      auto tex =
          LoadTexture(matdef->texture_filenames()->Get(i)->c_str(), format);
      mat->textures().push_back(tex);
    }
    material_map_[filename] = mat;
    return mat;
  }
  renderer_.last_error() = std::string("Couldn\'t load: ") + filename;
  return nullptr;
}

void AssetManager::UnloadMaterial(const char *filename) {
  auto mat = FindMaterial(filename);
  if (!mat) return;
  mat->DeleteTextures();
  material_map_.erase(filename);
  for (auto it = mat->textures().begin(); it != mat->textures().end(); ++it) {
    texture_map_.erase((*it)->filename());
  }
}

Mesh *AssetManager::FindMesh(const char *filename) {
  return FindInMap(mesh_map_, filename);
}

template <typename T>
void CopyAttribute(const T *attr, uint8_t *&buf) {
  auto dest = (T *)buf;
  *dest = *attr;
  buf += sizeof(T);
}

Mesh *AssetManager::LoadMesh(const char *filename) {
  auto mesh = FindMesh(filename);
  if (mesh) return mesh;
  std::string flatbuf;
  if (LoadFile(filename, &flatbuf)) {
    flatbuffers::Verifier verifier(
        reinterpret_cast<const uint8_t *>(flatbuf.c_str()), flatbuf.length());
    assert(meshdef::VerifyMeshBuffer(verifier));
    auto meshdef = meshdef::GetMesh(flatbuf.c_str());
    // Collect what attributes are available.
    std::vector<Attribute> attrs;
    attrs.push_back(kPosition3f);
    if (meshdef->normals()) attrs.push_back(kNormal3f);
    if (meshdef->tangents()) attrs.push_back(kTangent4f);
    if (meshdef->colors()) attrs.push_back(kColor4ub);
    if (meshdef->texcoords()) attrs.push_back(kTexCoord2f);
    attrs.push_back(kEND);
    auto vert_size = Mesh::VertexSize(attrs.data());
    // Create an interleaved buffer. Would be cool to do this without
    // the additional copy, but that's not easy in OpenGL.
    // Could use multiple buffers instead, but likely less efficient.
    auto buf = new uint8_t[vert_size * meshdef->positions()->Length()];
    auto p = buf;
    for (size_t i = 0; i < meshdef->positions()->Length(); i++) {
      if (meshdef->positions()) CopyAttribute(meshdef->positions()->Get(i), p);
      if (meshdef->normals()) CopyAttribute(meshdef->normals()->Get(i), p);
      if (meshdef->tangents()) CopyAttribute(meshdef->tangents()->Get(i), p);
      if (meshdef->colors()) CopyAttribute(meshdef->colors()->Get(i), p);
      if (meshdef->texcoords()) CopyAttribute(meshdef->texcoords()->Get(i), p);
    }
    vec3 max = meshdef->max_position() ? LoadVec3(meshdef->max_position())
                                       : mathfu::kZeros3f;
    vec3 min = meshdef->min_position() ? LoadVec3(meshdef->min_position())
                                       : mathfu::kZeros3f;
    mesh = new Mesh(buf, meshdef->positions()->Length(), vert_size,
                    attrs.data(), meshdef->max_position() ? &max : nullptr,
                    meshdef->min_position() ? &min : nullptr);
    delete[] buf;
    // Load indices an materials.
    for (size_t i = 0; i < meshdef->surfaces()->size(); i++) {
      auto surface = meshdef->surfaces()->Get(i);
      auto mat = LoadMaterial(surface->material()->c_str());
      if (!mat) {
        delete mesh;
        return nullptr;
      }  // Error msg already set.
      mesh->AddIndices(
          reinterpret_cast<const uint16_t *>(surface->indices()->Data()),
          surface->indices()->Length(), mat);
    }
    mesh_map_[filename] = mesh;
    return mesh;
  }
  renderer_.last_error() = std::string("Couldn\'t load: ") + filename;
  return nullptr;
}

void AssetManager::UnloadMesh(const char *filename) {
  auto mesh = FindMesh(filename);
  if (!mesh) return;
  mesh_map_.erase(filename);
  delete mesh;
}

}  // namespace fpl
<|endoftext|>


<commit_before>/***************************************************************************
                          asyncappender.cpp  -  AsyncAppender
                             -------------------
    begin                : sam mai 17 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.      *
*                                                                         *
* This software is published under the terms of the Apache Software       *
* License version 1.1, a copy of which has been included with this        *
* distribution in the LICENSE.txt file.                                   *
***************************************************************************/

#include <log4cxx/asyncappender.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/boundedfifo.h>
#include <log4cxx/spi/loggingevent.h>

using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::spi;

IMPLEMENT_LOG4CXX_OBJECT(AsyncAppender)
IMPLEMENT_LOG4CXX_OBJECT(Dispatcher)

/** The default buffer size is set to 128 events. */
int AsyncAppender::DEFAULT_BUFFER_SIZE = 128;

AsyncAppender::AsyncAppender()
: locationInfo(false), interruptedWarningMessage(false)
{
	bf = new BoundedFIFO(DEFAULT_BUFFER_SIZE);

    aai = new AppenderAttachableImpl();

	dispatcher = new Dispatcher(bf, this);
	dispatcher->start();
}

AsyncAppender::~AsyncAppender()
{
	finalize();
}

void AsyncAppender::addAppender(AppenderPtr newAppender)
{
	synchronized sync(aai);
	aai->addAppender(newAppender);
}

void AsyncAppender::append(const spi::LoggingEvent& event)
{
	// Set the NDC and thread name for the calling thread as these
	// LoggingEvent fields were not set at event creation time.
	event.getNDC();
	// Get a copy of this thread's MDC.
	event.getMDCCopy();

/*	if(locationInfo)
	{
		event.getLocationInformation();
	}*/

	synchronized sync(bf);

	while(bf->isFull())
	{
		//LOGLOG_DEBUG(_T("Waiting for free space in buffer, ")
		//	 << bf->length());
		bf->wait();
	}

	bf->put(event.copy());
	if(bf->wasEmpty())
	{
		//LogLog::debug(_T("Notifying dispatcher to process events."));
		bf->notify();
	}
}

void AsyncAppender::close()
{
	{
		synchronized sync(this);
		// avoid multiple close, otherwise one gets NullPointerException
		if(closed)
		{
			return;
		}

		closed = true;
	}

	// The following cannot be synchronized on "this" because the
	// dispatcher synchronizes with "this" in its while loop. If we
	// did synchronize we would systematically get deadlocks when
	// close was called.
	dispatcher->close();

	dispatcher->join();
	dispatcher = 0;
	bf = 0;
}

AppenderList AsyncAppender::getAllAppenders()
{
	synchronized sync(aai);
	return aai->getAllAppenders();
}

AppenderPtr AsyncAppender::getAppender(const String& name)
{
	synchronized sync(aai);
	return aai->getAppender(name);
}

bool AsyncAppender::isAttached(AppenderPtr appender)
{
	synchronized sync(aai);
	return aai->isAttached(appender);
}

void AsyncAppender::setBufferSize(int size)
{
	bf->resize(size);
}

int AsyncAppender::getBufferSize()
{
	return bf->getMaxSize();
}

void AsyncAppender::removeAllAppenders()
{
    synchronized sync(aai);
	aai->removeAllAppenders();
}

void AsyncAppender::removeAppender(AppenderPtr appender)
{
    synchronized sync(aai);
	aai->removeAppender(appender);
}

void AsyncAppender::removeAppender(const String& name)
{
    synchronized sync(aai);
	aai->removeAppender(name);
}

Dispatcher::Dispatcher(helpers::BoundedFIFOPtr bf, AsyncAppender * container)
 : bf(bf), container(container), aai(container->aai), interrupted(false)
{
	// set the dispatcher priority to lowest possible value
	setPriority(Thread::MIN_PRIORITY);
}

void Dispatcher::close()
{
	synchronized sync(bf);

	interrupted = true;
	// We have a waiting dispacther if and only if bf.length is
	// zero.  In that case, we need to give it a death kiss.
	if(bf->length() == 0)
	{
		bf->notify();
	}
}

void Dispatcher::run()
{
	LoggingEvent * event;

	while(true)
	{
		{
			synchronized sync(bf);

			if(bf->length() == 0)
			{
				// Exit loop if interrupted but only if
				// the buffer is empty.
				if(interrupted)
				{
					break;
				}
				bf->wait();
			}

			event = bf->get();
			if(bf->wasFull())
			{
				bf->notify();
			}
		} // synchronized

		if(aai != 0 && event != 0)
		{
			synchronized sync(aai);
			aai->appendLoopOnAppenders(*event);
			delete event;
		}
	} // while

	// close and remove all appenders
	aai->removeAllAppenders();
}
<commit_msg>added loglog statements<commit_after>/***************************************************************************
                          asyncappender.cpp  -  AsyncAppender
                             -------------------
    begin                : sam mai 17 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.      *
*                                                                         *
* This software is published under the terms of the Apache Software       *
* License version 1.1, a copy of which has been included with this        *
* distribution in the LICENSE.txt file.                                   *
***************************************************************************/

#include <log4cxx/asyncappender.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/boundedfifo.h>
#include <log4cxx/spi/loggingevent.h>

using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::spi;

IMPLEMENT_LOG4CXX_OBJECT(AsyncAppender)
IMPLEMENT_LOG4CXX_OBJECT(Dispatcher)

/** The default buffer size is set to 128 events. */
int AsyncAppender::DEFAULT_BUFFER_SIZE = 128;

AsyncAppender::AsyncAppender()
: locationInfo(false), interruptedWarningMessage(false)
{
	bf = new BoundedFIFO(DEFAULT_BUFFER_SIZE);

    aai = new AppenderAttachableImpl();

	dispatcher = new Dispatcher(bf, this);
	dispatcher->start();
}

AsyncAppender::~AsyncAppender()
{
	finalize();
}

void AsyncAppender::addAppender(AppenderPtr newAppender)
{
	synchronized sync(aai);
	aai->addAppender(newAppender);
}

void AsyncAppender::append(const spi::LoggingEvent& event)
{
	// Set the NDC and thread name for the calling thread as these
	// LoggingEvent fields were not set at event creation time.
	event.getNDC();
	// Get a copy of this thread's MDC.
	event.getMDCCopy();

/*	if(locationInfo)
	{
		event.getLocationInformation();
	}*/

	synchronized sync(bf);

	while(bf->isFull())
	{
		//LOGLOG_DEBUG(_T("Waiting for free space in buffer, ")
		//	 << bf->length());
		bf->wait();
	}

	bf->put(event.copy());
	if(bf->wasEmpty())
	{
		//LOGLOG_DEBUG(_T("Notifying dispatcher to process events."));
		bf->notify();
	}
}

void AsyncAppender::close()
{
	{
		synchronized sync(this);
		// avoid multiple close, otherwise one gets NullPointerException
		if(closed)
		{
			return;
		}

		closed = true;
	}

	// The following cannot be synchronized on "this" because the
	// dispatcher synchronizes with "this" in its while loop. If we
	// did synchronize we would systematically get deadlocks when
	// close was called.
	dispatcher->close();

	dispatcher->join();
	dispatcher = 0;
	bf = 0;
}

AppenderList AsyncAppender::getAllAppenders()
{
	synchronized sync(aai);
	return aai->getAllAppenders();
}

AppenderPtr AsyncAppender::getAppender(const String& name)
{
	synchronized sync(aai);
	return aai->getAppender(name);
}

bool AsyncAppender::isAttached(AppenderPtr appender)
{
	synchronized sync(aai);
	return aai->isAttached(appender);
}

void AsyncAppender::setBufferSize(int size)
{
	bf->resize(size);
}

int AsyncAppender::getBufferSize()
{
	return bf->getMaxSize();
}

void AsyncAppender::removeAllAppenders()
{
    synchronized sync(aai);
	aai->removeAllAppenders();
}

void AsyncAppender::removeAppender(AppenderPtr appender)
{
    synchronized sync(aai);
	aai->removeAppender(appender);
}

void AsyncAppender::removeAppender(const String& name)
{
    synchronized sync(aai);
	aai->removeAppender(name);
}

Dispatcher::Dispatcher(helpers::BoundedFIFOPtr bf, AsyncAppender * container)
 : bf(bf), container(container), aai(container->aai), interrupted(false)
{
	// set the dispatcher priority to lowest possible value
	setPriority(Thread::MIN_PRIORITY);
}

void Dispatcher::close()
{
	synchronized sync(bf);

	interrupted = true;
	// We have a waiting dispacther if and only if bf.length is
	// zero.  In that case, we need to give it a death kiss.
	if(bf->length() == 0)
	{
		bf->notify();
	}
}

void Dispatcher::run()
{
	LoggingEvent * event;

	while(true)
	{
		{
			synchronized sync(bf);

			if(bf->length() == 0)
			{
				// Exit loop if interrupted but only if
				// the buffer is empty.
				if(interrupted)
				{
					//LOGLOG_DEBUG("Exiting.");
					break;
				}
				//LOGLOG_DEBUG("Waiting for new event to dispatch.");
				bf->wait();
			}

			event = bf->get();
			if(bf->wasFull())
			{
				//LOGLOG_DEBUG("Notifying AsyncAppender about freed space.");
				bf->notify();
			}
		} // synchronized

		if(aai != 0 && event != 0)
		{
			synchronized sync(aai);
			aai->appendLoopOnAppenders(*event);
			delete event;
		}
	} // while

	// close and remove all appenders
	aai->removeAllAppenders();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: titlectrl.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 16:30:29 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _PAD_TITLECTRL_HXX_
#define _PAD_TITLECTRL_HXX_

#ifndef _SV_CTRL_HXX
#include <vcl/ctrl.hxx>
#endif
#ifndef _SV_IMAGE_HXX
#include <vcl/image.hxx>
#endif

namespace padmin
{

class TitleImage : public Control
{
    Image               m_aImage;
    String              m_aText;
    Color               m_aBGColor;
    Point               m_aImagePos;
    Point               m_aTextPos;

    bool                m_bArranged;

    void arrange();
public:
    TitleImage( Window* pParent, const ResId& rResId );
    ~TitleImage();

    virtual void Paint( const Rectangle& rRect );

    void SetImage( const Image& rImage );
    const Image& GetImage() const { return m_aImage; }

    virtual void SetText( const String& rText );
    virtual String GetText() const { return m_aText; }

    void SetBackgroundColor( const Color& rColor );
    const Color& GetBackgroundColor() const { return m_aBGColor; }
};

}

#endif // _PAD_TITLECTRL_HXX_
<commit_msg>INTEGRATION: CWS changefileheader (1.2.148); FILE MERGED 2008/04/01 15:22:34 thb 1.2.148.2: #i85898# Stripping all external header guards 2008/03/31 13:15:12 rt 1.2.148.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: titlectrl.hxx,v $
 * $Revision: 1.3 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _PAD_TITLECTRL_HXX_
#define _PAD_TITLECTRL_HXX_

#include <vcl/ctrl.hxx>
#include <vcl/image.hxx>

namespace padmin
{

class TitleImage : public Control
{
    Image               m_aImage;
    String              m_aText;
    Color               m_aBGColor;
    Point               m_aImagePos;
    Point               m_aTextPos;

    bool                m_bArranged;

    void arrange();
public:
    TitleImage( Window* pParent, const ResId& rResId );
    ~TitleImage();

    virtual void Paint( const Rectangle& rRect );

    void SetImage( const Image& rImage );
    const Image& GetImage() const { return m_aImage; }

    virtual void SetText( const String& rText );
    virtual String GetText() const { return m_aText; }

    void SetBackgroundColor( const Color& rColor );
    const Color& GetBackgroundColor() const { return m_aBGColor; }
};

}

#endif // _PAD_TITLECTRL_HXX_
<|endoftext|>


<commit_before>/** @file
 *
 * @ingroup modularLibrary
 *
 * @brief A Viewer Object.
 *
 * @details
 *
 * @authors Théo de la Hogue
 *
 * @copyright © 2010, Théo de la Hogue @n
 * This code is licensed under the terms of the "New BSD License" @n
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTViewer.h"

#define thisTTClass			TTViewer
#define thisTTClassName		"Viewer"
#define thisTTClassTags		"viewer"

TTObjectBasePtr TTViewer::instantiate (TTSymbol name, TTValue arguments)
{
	return new TTViewer(arguments);
}

extern "C" void TTViewer::registerClass()
{
	TTClassRegister(TTSymbol("Viewer"), thisTTClassTags, TTViewer::instantiate);
}

TTViewer::TTViewer(const TTValue& arguments) :
TTCallback(arguments),
mAddress(kTTAdrsEmpty),
mDescription(kTTSym_none),
mType(kTTSym_generic),
mTag(kTTSym_none),
mHighlight(NO),
mFreeze(NO),
mDataspace(kTTSym_none),
mDataspaceUnit(kTTSym_none),
mDataspaceConverter("dataspace"),
mActive(YES)
{
	addAttributeWithSetter(Address, kTypeSymbol);
	addAttribute(Description, kTypeSymbol);
	addAttribute(Type, kTypeSymbol);
	addAttribute(Tag, kTypeSymbol);
	addAttributeWithSetter(Highlight, kTypeBoolean);
	addAttributeWithSetter(Freeze, kTypeBoolean);

	addAttribute(Dataspace, kTypeSymbol);
	addAttributeProperty(Dataspace, readOnly, YES);
	addAttributeWithSetter(DataspaceUnit, kTypeSymbol);

	addAttributeWithSetter(Active, kTypeBoolean);

	addAttributeWithSetter(ReturnedValue, kTypeLocalValue);
	addAttributeProperty(ReturnedValue, readOnly, YES);
	addAttributeProperty(ReturnedValue, hidden, YES);

	addMessageWithArguments(Send);
	addMessageProperty(Send, hidden, YES);

    addMessageWithArguments(Grab);
	addMessageProperty(Grab, hidden, YES);

    // create sender
    mSender = TTObject(kTTSym_Sender);

    // create receiver
    TTValue     args;
    TTObject    empty;

    TTObject returnAddressCallback = TTObject("callback");
	returnAddressCallback.set(kTTSym_baton, TTObject(this));
	returnAddressCallback.set(kTTSym_function, TTPtr(&TTViewerReceiveAddressCallback));
	args.append(returnAddressCallback);

	TTObject returnValueCallback = TTObject("callback");
	returnValueCallback.set(kTTSym_baton, TTObject(this));
	returnValueCallback.set(kTTSym_function, TTPtr(&TTViewerReceiveValueCallback));
	args.append(returnValueCallback);

	mReceiver = TTObject(kTTSym_Receiver, args);

    // create dataspace observer
    args.clear();
    args.append(empty);

	TTObject returnDataspaceCallback = TTObject("callback");
	returnDataspaceCallback.set(kTTSym_baton, TTObject(this));
	returnDataspaceCallback.set(kTTSym_function, TTPtr(&TTViewerDataspaceCallback));
	args.append(returnDataspaceCallback);

	mDataspaceObserver = TTObject(kTTSym_Receiver, args);

    // create dataspace unit observer
    args.clear();
    args.append(empty);

	TTObject returnDataspaceUnitCallback = TTObject("callback");
	returnDataspaceUnitCallback.set(kTTSym_baton, TTObject(this));
	returnDataspaceUnitCallback.set(kTTSym_function, TTPtr(&TTViewerDataspaceUnitCallback));
	args.append(returnDataspaceUnitCallback);

	mDataspaceUnitObserver = TTObject(kTTSym_Receiver, args);
}

TTViewer::~TTViewer()
{
    // disable reception to avoid crash
    mActive = NO;
}

TTErr TTViewer::setAddress(const TTValue& value)
{
    TTBoolean   memoActive = mActive;

	mAddress = value[0];

    // disable reception to avoid crash
    mActive = NO;

	if (mAddress == kTTAdrsEmpty)
		return kTTErrGeneric;

	// the default attribute to bind is value
	if (mAddress.getAttribute() == NO_ATTRIBUTE)
		mAddress.appendAttribute(kTTSym_value);

	// change sender address
	mSender.set(kTTSym_address, mAddress);

	// change receiver address
	mReceiver.set(kTTSym_address, mAddress);

    // change dataspace observer address and get the value
    mDataspaceObserver.set(kTTSym_address, mAddress.appendAttribute(kTTSym_dataspace));
	mDataspaceObserver.send(kTTSym_Get);

    // change dataspace unit observer address and get the value
    mDataspaceUnitObserver.set(kTTSym_address, mAddress.appendAttribute(kTTSym_dataspaceUnit));
	mDataspaceUnitObserver.send(kTTSym_Get);

    // enable reception
    mActive = memoActive;

    // refresh
    return mReceiver.send(kTTSym_Get);
}

TTErr TTViewer::setActive(const TTValue& value)
{
	mActive = value;

	mReceiver.set(kTTSym_active, mActive);

    if (mActive)
        mReceiver.send(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::setHighlight(const TTValue& value)
{
    TTAttributePtr	anAttribute = NULL;
	TTErr			err;

	mHighlight = value;

	err = this->findAttribute(kTTSym_highlight, &anAttribute);

	if (!err)
		anAttribute->sendNotification(kTTSym_notify, mHighlight);	// we use kTTSym_notify because we know that observers are TTCallback

	return kTTErrNone;
}

TTErr TTViewer::setFreeze(const TTValue& value)
{
	mFreeze = value;

    // update the value if the Viewer is unfreezed
    if (!mFreeze)
        return mReceiver.send(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::setReturnedValue(const TTValue& value)
{
	TTAttributePtr	anAttribute = NULL;
	TTErr			err;

	mReturnedValue = value;

	err = this->findAttribute(kTTSym_returnedValue, &anAttribute);

	if (!err)
		anAttribute->sendNotification(kTTSym_notify, mReturnedValue);	// we use kTTSym_notify because we know that observers are TTCallback

	return kTTErrNone;
}

TTErr TTViewer::Send(const TTValue& inputValue, TTValue& outputValue)
{
    TTValue none, valueToSend = inputValue;

    // append view unit except for empty value
    if (valueToSend.size() > 0 && mDataspaceUnit != kTTSym_none)
        valueToSend.append(mDataspaceUnit);

    return mSender.send(kTTSym_Send, valueToSend, none);
}

TTErr TTViewer::Grab(const TTValue& inputValue, TTValue& outputValue)
{
    return mReceiver.send(kTTSym_Grab, inputValue, outputValue);
}

TTErr TTViewer::setDataspaceUnit(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mDataspaceUnit = value;

	return mDataspaceConverter.set("outputUnit", mDataspaceUnit);

	// TODO : notifyObservers(kTTSym_dataspaceUnit, n);
}

#if 0
#pragma mark -
#pragma mark Some Methods
#endif

TTErr TTViewerReceiveAddressCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
    TTViewerPtr aViewer;
	TTValue		converted;

    // unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

    if (aViewer->mActive) {

        if (aViewer->mDataspace == kTTSym_none) {

            aViewer->mDataspaceObserver.send(kTTSym_Get);
            aViewer->mDataspaceUnitObserver.send(kTTSym_Get);
        }
    }

	return kTTErrNone;
}

TTErr TTViewerReceiveValueCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
	TTViewerPtr aViewer;
	TTValue		converted;

	// unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

	if (aViewer->mActive) {

		if (!aViewer->mFreeze)
			// convert data
			aViewer->mDataspaceConverter.send("convert", data, converted);

		else
			// use last data
			converted = aViewer->mReturnedValue;

		// return value
        aViewer->deliver(converted);
        aViewer->setReturnedValue(converted);
	}

	return kTTErrNone;
}

TTErr TTViewerDataspaceCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
	TTViewerPtr aViewer;
	TTValue		v;
    TTSymbol    dataspace;

	// unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

    dataspace = data;

    // filter repetitions
    if (dataspace != aViewer->mDataspace) {

        aViewer->mDataspace = data;

        return aViewer->mDataspaceConverter.set(kTTSym_dataspace, aViewer->mDataspace);
    }

	return kTTErrNone;
}

TTErr TTViewerDataspaceUnitCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
	TTViewerPtr aViewer;
	TTValue		v;
	TTErr		err;

	// unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

    // set input unit like the data unit
    aViewer->mDataspaceConverter.set("inputUnit", data);

    // if no unit : set the output unit like the data unit
    if (aViewer->mDataspaceUnit == kTTSym_none)
        aViewer->mDataspaceUnit = data;

    // if the unit is wrong : use the default unit
    err = aViewer->mDataspaceConverter.set("outputUnit", aViewer->mDataspaceUnit);
    if (err) {
        aViewer->mDataspaceConverter.get("outputUnit", v);
        aViewer->mDataspaceUnit = v[0];
        aViewer->mDataspaceConverter.set("outputUnit", aViewer->mDataspaceUnit);
    }

	return kTTErrNone;
}
<commit_msg>Fixing reference count for j.remote<commit_after>/** @file
 *
 * @ingroup modularLibrary
 *
 * @brief A Viewer Object.
 *
 * @details
 *
 * @authors Théo de la Hogue
 *
 * @copyright © 2010, Théo de la Hogue @n
 * This code is licensed under the terms of the "New BSD License" @n
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTViewer.h"

#define thisTTClass			TTViewer
#define thisTTClassName		"Viewer"
#define thisTTClassTags		"viewer"

TTObjectBasePtr TTViewer::instantiate (TTSymbol name, TTValue arguments)
{
	return new TTViewer(arguments);
}

extern "C" void TTViewer::registerClass()
{
	TTClassRegister(TTSymbol("Viewer"), thisTTClassTags, TTViewer::instantiate);
}

TTViewer::TTViewer(const TTValue& arguments) :
TTCallback(arguments),
mAddress(kTTAdrsEmpty),
mDescription(kTTSym_none),
mType(kTTSym_generic),
mTag(kTTSym_none),
mHighlight(NO),
mFreeze(NO),
mDataspace(kTTSym_none),
mDataspaceUnit(kTTSym_none),
mDataspaceConverter("dataspace"),
mActive(YES)
{
	addAttributeWithSetter(Address, kTypeSymbol);
	addAttribute(Description, kTypeSymbol);
	addAttribute(Type, kTypeSymbol);
	addAttribute(Tag, kTypeSymbol);
	addAttributeWithSetter(Highlight, kTypeBoolean);
	addAttributeWithSetter(Freeze, kTypeBoolean);

	addAttribute(Dataspace, kTypeSymbol);
	addAttributeProperty(Dataspace, readOnly, YES);
	addAttributeWithSetter(DataspaceUnit, kTypeSymbol);

	addAttributeWithSetter(Active, kTypeBoolean);

	addAttributeWithSetter(ReturnedValue, kTypeLocalValue);
	addAttributeProperty(ReturnedValue, readOnly, YES);
	addAttributeProperty(ReturnedValue, hidden, YES);

	addMessageWithArguments(Send);
	addMessageProperty(Send, hidden, YES);

    addMessageWithArguments(Grab);
	addMessageProperty(Grab, hidden, YES);
}

TTViewer::~TTViewer()
{
    // disable reception to avoid crash
    mActive = NO;
}

TTErr TTViewer::setAddress(const TTValue& value)
{
    TTBoolean   memoActive = mActive;

	mAddress = value[0];

    // disable reception to avoid crash
    mActive = NO;

    // if no address : delete sender, receiver and observers
	if (mAddress == kTTAdrsEmpty) {

        mSender = TTObject();
        mReceiver = TTObject();
        mDataspaceObserver = TTObject();
        mDataspaceUnitObserver = TTObject();

		return kTTErrGeneric;
    }

	// the default attribute to bind is value
	if (mAddress.getAttribute() == NO_ATTRIBUTE)
		mAddress.appendAttribute(kTTSym_value);

    // create sender if needed
    if (!mSender.valid())
        mSender = TTObject(kTTSym_Sender);

	// change sender address
	mSender.set(kTTSym_address, mAddress);

    // create receiver if needed
    if (!mReceiver.valid()) {

        TTValue args;

        TTObject returnAddressCallback = TTObject("callback");
        returnAddressCallback.set(kTTSym_baton, TTObject(this));
        returnAddressCallback.set(kTTSym_function, TTPtr(&TTViewerReceiveAddressCallback));
        args.append(returnAddressCallback);

        TTObject returnValueCallback = TTObject("callback");
        returnValueCallback.set(kTTSym_baton, TTObject(this));
        returnValueCallback.set(kTTSym_function, TTPtr(&TTViewerReceiveValueCallback));
        args.append(returnValueCallback);

        mReceiver = TTObject(kTTSym_Receiver, args);
    }

	// change receiver address
	mReceiver.set(kTTSym_address, mAddress);

    // create dataspace observer if needed
    if (!mDataspaceObserver.valid()) {

        TTValue     args;
        TTObject    empty;

        args.append(empty);

        TTObject returnDataspaceCallback = TTObject("callback");
        returnDataspaceCallback.set(kTTSym_baton, TTObject(this));
        returnDataspaceCallback.set(kTTSym_function, TTPtr(&TTViewerDataspaceCallback));
        args.append(returnDataspaceCallback);

        mDataspaceObserver = TTObject(kTTSym_Receiver, args);
    }

    // change dataspace observer address and get the value
    mDataspaceObserver.set(kTTSym_address, mAddress.appendAttribute(kTTSym_dataspace));
	mDataspaceObserver.send(kTTSym_Get);

    // create dataspace unit observer if needed
    if (!mDataspaceUnitObserver.valid()) {

        TTValue     args;
        TTObject    empty;

        args.append(empty);

        TTObject returnDataspaceUnitCallback = TTObject("callback");
        returnDataspaceUnitCallback.set(kTTSym_baton, TTObject(this));
        returnDataspaceUnitCallback.set(kTTSym_function, TTPtr(&TTViewerDataspaceUnitCallback));
        args.append(returnDataspaceUnitCallback);

        mDataspaceUnitObserver = TTObject(kTTSym_Receiver, args);
    }

    // change dataspace unit observer address and get the value
    mDataspaceUnitObserver.set(kTTSym_address, mAddress.appendAttribute(kTTSym_dataspaceUnit));
	mDataspaceUnitObserver.send(kTTSym_Get);

    // enable reception
    mActive = memoActive;

    // refresh
    return mReceiver.send(kTTSym_Get);
}

TTErr TTViewer::setActive(const TTValue& value)
{
	mActive = value;

	mReceiver.set(kTTSym_active, mActive);

    if (mActive)
        mReceiver.send(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::setHighlight(const TTValue& value)
{
    TTAttributePtr	anAttribute = NULL;
	TTErr			err;

	mHighlight = value;

	err = this->findAttribute(kTTSym_highlight, &anAttribute);

	if (!err)
		anAttribute->sendNotification(kTTSym_notify, mHighlight);	// we use kTTSym_notify because we know that observers are TTCallback

	return kTTErrNone;
}

TTErr TTViewer::setFreeze(const TTValue& value)
{
	mFreeze = value;

    // update the value if the Viewer is unfreezed
    if (!mFreeze)
        return mReceiver.send(kTTSym_Get);

	return kTTErrNone;
}

TTErr TTViewer::setReturnedValue(const TTValue& value)
{
	TTAttributePtr	anAttribute = NULL;
	TTErr			err;

	mReturnedValue = value;

	err = this->findAttribute(kTTSym_returnedValue, &anAttribute);

	if (!err)
		anAttribute->sendNotification(kTTSym_notify, mReturnedValue);	// we use kTTSym_notify because we know that observers are TTCallback

	return kTTErrNone;
}

TTErr TTViewer::Send(const TTValue& inputValue, TTValue& outputValue)
{
    TTValue none, valueToSend = inputValue;

    // append view unit except for empty value
    if (valueToSend.size() > 0 && mDataspaceUnit != kTTSym_none)
        valueToSend.append(mDataspaceUnit);

    return mSender.send(kTTSym_Send, valueToSend, none);
}

TTErr TTViewer::Grab(const TTValue& inputValue, TTValue& outputValue)
{
    return mReceiver.send(kTTSym_Grab, inputValue, outputValue);
}

TTErr TTViewer::setDataspaceUnit(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mDataspaceUnit = value;

	return mDataspaceConverter.set("outputUnit", mDataspaceUnit);

	// TODO : notifyObservers(kTTSym_dataspaceUnit, n);
}

#if 0
#pragma mark -
#pragma mark Some Methods
#endif

TTErr TTViewerReceiveAddressCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
    TTViewerPtr aViewer;
	TTValue		converted;

    // unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

    if (aViewer->mActive) {

        if (aViewer->mDataspace == kTTSym_none) {

            aViewer->mDataspaceObserver.send(kTTSym_Get);
            aViewer->mDataspaceUnitObserver.send(kTTSym_Get);
        }
    }

	return kTTErrNone;
}

TTErr TTViewerReceiveValueCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
	TTViewerPtr aViewer;
	TTValue		converted;

	// unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

	if (aViewer->mActive) {

		if (!aViewer->mFreeze)
			// convert data
			aViewer->mDataspaceConverter.send("convert", data, converted);

		else
			// use last data
			converted = aViewer->mReturnedValue;

		// return value
        aViewer->deliver(converted);
        aViewer->setReturnedValue(converted);
	}

	return kTTErrNone;
}

TTErr TTViewerDataspaceCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
	TTViewerPtr aViewer;
	TTValue		v;
    TTSymbol    dataspace;

	// unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

    dataspace = data;

    // filter repetitions
    if (dataspace != aViewer->mDataspace) {

        aViewer->mDataspace = data;

        return aViewer->mDataspaceConverter.set(kTTSym_dataspace, aViewer->mDataspace);
    }

	return kTTErrNone;
}

TTErr TTViewerDataspaceUnitCallback(const TTValue& baton, const TTValue& data)
{
    TTObject    o;
	TTViewerPtr aViewer;
	TTValue		v;
	TTErr		err;

	// unpack baton (a #TTViewer)
    o = baton[0];
	aViewer = (TTViewerPtr)o.instance();

    // set input unit like the data unit
    aViewer->mDataspaceConverter.set("inputUnit", data);

    // if no unit : set the output unit like the data unit
    if (aViewer->mDataspaceUnit == kTTSym_none)
        aViewer->mDataspaceUnit = data;

    // if the unit is wrong : use the default unit
    err = aViewer->mDataspaceConverter.set("outputUnit", aViewer->mDataspaceUnit);
    if (err) {
        aViewer->mDataspaceConverter.get("outputUnit", v);
        aViewer->mDataspaceUnit = v[0];
        aViewer->mDataspaceConverter.set("outputUnit", aViewer->mDataspaceUnit);
    }

	return kTTErrNone;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "core/dom/DOMTokenList.h"

#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "wtf/text/StringBuilder.h"

namespace blink {

bool DOMTokenList::validateToken(const String& token, ExceptionState& exceptionState)
{
    if (token.isEmpty()) {
        exceptionState.throwDOMException(SyntaxError, "The token provided must not be empty.");
        return false;
    }

    unsigned length = token.length();
    for (unsigned i = 0; i < length; ++i) {
        if (isHTMLSpace<UChar>(token[i])) {
            exceptionState.throwDOMException(InvalidCharacterError, "The token provided ('" + token + "') contains HTML space characters, which are not valid in tokens.");
            return false;
        }
    }

    return true;
}

bool DOMTokenList::validateTokens(const Vector<String>& tokens, ExceptionState& exceptionState)
{
    for (size_t i = 0; i < tokens.size(); ++i) {
        if (!validateToken(tokens[i], exceptionState))
            return false;
    }

    return true;
}

bool DOMTokenList::contains(const AtomicString& token, ExceptionState& exceptionState) const
{
    if (!validateToken(token, exceptionState))
        return false;
    return containsInternal(token);
}

void DOMTokenList::add(const AtomicString& token, ExceptionState& exceptionState)
{
    Vector<String> tokens;
    tokens.append(token.string());
    add(tokens, exceptionState);
}

// Optimally, this should take a Vector<AtomicString> const ref in argument but the
// bindings generator does not handle that.
void DOMTokenList::add(const Vector<String>& tokens, ExceptionState& exceptionState)
{
    Vector<String> filteredTokens;
    filteredTokens.reserveCapacity(tokens.size());
    for (size_t i = 0; i < tokens.size(); ++i) {
        if (!validateToken(tokens[i], exceptionState))
            return;
        if (containsInternal(AtomicString(tokens[i])))
            continue;
        if (filteredTokens.contains(tokens[i]))
            continue;
        filteredTokens.append(tokens[i]);
    }

    if (filteredTokens.isEmpty())
        return;

    setValue(addTokens(value(), filteredTokens));
}

void DOMTokenList::remove(const AtomicString& token, ExceptionState& exceptionState)
{
    Vector<String> tokens;
    tokens.append(token.string());
    remove(tokens, exceptionState);
}

// Optimally, this should take a Vector<AtomicString> const ref in argument but the
// bindings generator does not handle that.
void DOMTokenList::remove(const Vector<String>& tokens, ExceptionState& exceptionState)
{
    if (!validateTokens(tokens, exceptionState))
        return;

    // Check using containsInternal first since it is a lot faster than going
    // through the string character by character.
    bool found = false;
    for (size_t i = 0; i < tokens.size(); ++i) {
        if (containsInternal(AtomicString(tokens[i]))) {
            found = true;
            break;
        }
    }

    if (found)
        setValue(removeTokens(value(), tokens));
}

bool DOMTokenList::toggle(const AtomicString& token, ExceptionState& exceptionState)
{
    if (!validateToken(token, exceptionState))
        return false;

    if (containsInternal(token)) {
        removeInternal(token);
        return false;
    }
    addInternal(token);
    return true;
}

bool DOMTokenList::toggle(const AtomicString& token, bool force, ExceptionState& exceptionState)
{
    if (!validateToken(token, exceptionState))
        return false;

    if (force)
        addInternal(token);
    else
        removeInternal(token);

    return force;
}

void DOMTokenList::addInternal(const AtomicString& token)
{
    if (!containsInternal(token))
        setValue(addToken(value(), token));
}

void DOMTokenList::removeInternal(const AtomicString& token)
{
    // Check using contains first since it uses AtomicString comparisons instead
    // of character by character testing.
    if (!containsInternal(token))
        return;
    setValue(removeToken(value(), token));
}

AtomicString DOMTokenList::addToken(const AtomicString& input, const AtomicString& token)
{
    Vector<String> tokens;
    tokens.append(token.string());
    return addTokens(input, tokens);
}

// This returns an AtomicString because it is always passed as argument to setValue() and setValue()
// takes an AtomicString in argument.
AtomicString DOMTokenList::addTokens(const AtomicString& input, const Vector<String>& tokens)
{
    bool needsSpace = false;

    StringBuilder builder;
    if (!input.isEmpty()) {
        builder.append(input);
        needsSpace = !isHTMLSpace<UChar>(input[input.length() - 1]);
    }

    for (size_t i = 0; i < tokens.size(); ++i) {
        if (needsSpace)
            builder.append(' ');
        builder.append(tokens[i]);
        needsSpace = true;
    }

    return builder.toAtomicString();
}

AtomicString DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token)
{
    Vector<String> tokens;
    tokens.append(token.string());
    return removeTokens(input, tokens);
}

// This returns an AtomicString because it is always passed as argument to setValue() and setValue()
// takes an AtomicString in argument.
AtomicString DOMTokenList::removeTokens(const AtomicString& input, const Vector<String>& tokens)
{
    // Algorithm defined at http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string
    // New spec is at http://dom.spec.whatwg.org/#remove-a-token-from-a-string

    unsigned inputLength = input.length();
    StringBuilder output; // 3
    output.reserveCapacity(inputLength);
    unsigned position = 0; // 4

    // Step 5
    while (position < inputLength) {
        if (isHTMLSpace<UChar>(input[position])) { // 6
            output.append(input[position++]); // 6.1, 6.2
            continue; // 6.3
        }

        // Step 7
        StringBuilder tokenBuilder;
        while (position < inputLength && isNotHTMLSpace<UChar>(input[position]))
            tokenBuilder.append(input[position++]);

        // Step 8
        String token = tokenBuilder.toString();
        if (tokens.contains(token)) {
            // Step 8.1
            while (position < inputLength && isHTMLSpace<UChar>(input[position]))
                ++position;

            // Step 8.2
            size_t j = output.length();
            while (j > 0 && isHTMLSpace<UChar>(output[j - 1]))
                --j;
            output.resize(j);

            // Step 8.3
            if (position < inputLength && !output.isEmpty())
                output.append(' ');
        } else {
            output.append(token); // Step 9
        }
    }

    return output.toAtomicString();
}

} // namespace blink
<commit_msg>Simplify DOMTokenList::validateToken() code<commit_after>/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "core/dom/DOMTokenList.h"

#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "wtf/text/StringBuilder.h"

namespace blink {

bool DOMTokenList::validateToken(const String& token, ExceptionState& exceptionState)
{
    if (token.isEmpty()) {
        exceptionState.throwDOMException(SyntaxError, "The token provided must not be empty.");
        return false;
    }

    if (token.find(isHTMLSpace) != kNotFound) {
        exceptionState.throwDOMException(InvalidCharacterError, "The token provided ('" + token + "') contains HTML space characters, which are not valid in tokens.");
        return false;
    }

    return true;
}

bool DOMTokenList::validateTokens(const Vector<String>& tokens, ExceptionState& exceptionState)
{
    for (size_t i = 0; i < tokens.size(); ++i) {
        if (!validateToken(tokens[i], exceptionState))
            return false;
    }

    return true;
}

bool DOMTokenList::contains(const AtomicString& token, ExceptionState& exceptionState) const
{
    if (!validateToken(token, exceptionState))
        return false;
    return containsInternal(token);
}

void DOMTokenList::add(const AtomicString& token, ExceptionState& exceptionState)
{
    Vector<String> tokens;
    tokens.append(token.string());
    add(tokens, exceptionState);
}

// Optimally, this should take a Vector<AtomicString> const ref in argument but the
// bindings generator does not handle that.
void DOMTokenList::add(const Vector<String>& tokens, ExceptionState& exceptionState)
{
    Vector<String> filteredTokens;
    filteredTokens.reserveCapacity(tokens.size());
    for (size_t i = 0; i < tokens.size(); ++i) {
        if (!validateToken(tokens[i], exceptionState))
            return;
        if (containsInternal(AtomicString(tokens[i])))
            continue;
        if (filteredTokens.contains(tokens[i]))
            continue;
        filteredTokens.append(tokens[i]);
    }

    if (filteredTokens.isEmpty())
        return;

    setValue(addTokens(value(), filteredTokens));
}

void DOMTokenList::remove(const AtomicString& token, ExceptionState& exceptionState)
{
    Vector<String> tokens;
    tokens.append(token.string());
    remove(tokens, exceptionState);
}

// Optimally, this should take a Vector<AtomicString> const ref in argument but the
// bindings generator does not handle that.
void DOMTokenList::remove(const Vector<String>& tokens, ExceptionState& exceptionState)
{
    if (!validateTokens(tokens, exceptionState))
        return;

    // Check using containsInternal first since it is a lot faster than going
    // through the string character by character.
    bool found = false;
    for (size_t i = 0; i < tokens.size(); ++i) {
        if (containsInternal(AtomicString(tokens[i]))) {
            found = true;
            break;
        }
    }

    if (found)
        setValue(removeTokens(value(), tokens));
}

bool DOMTokenList::toggle(const AtomicString& token, ExceptionState& exceptionState)
{
    if (!validateToken(token, exceptionState))
        return false;

    if (containsInternal(token)) {
        removeInternal(token);
        return false;
    }
    addInternal(token);
    return true;
}

bool DOMTokenList::toggle(const AtomicString& token, bool force, ExceptionState& exceptionState)
{
    if (!validateToken(token, exceptionState))
        return false;

    if (force)
        addInternal(token);
    else
        removeInternal(token);

    return force;
}

void DOMTokenList::addInternal(const AtomicString& token)
{
    if (!containsInternal(token))
        setValue(addToken(value(), token));
}

void DOMTokenList::removeInternal(const AtomicString& token)
{
    // Check using contains first since it uses AtomicString comparisons instead
    // of character by character testing.
    if (!containsInternal(token))
        return;
    setValue(removeToken(value(), token));
}

AtomicString DOMTokenList::addToken(const AtomicString& input, const AtomicString& token)
{
    Vector<String> tokens;
    tokens.append(token.string());
    return addTokens(input, tokens);
}

// This returns an AtomicString because it is always passed as argument to setValue() and setValue()
// takes an AtomicString in argument.
AtomicString DOMTokenList::addTokens(const AtomicString& input, const Vector<String>& tokens)
{
    bool needsSpace = false;

    StringBuilder builder;
    if (!input.isEmpty()) {
        builder.append(input);
        needsSpace = !isHTMLSpace<UChar>(input[input.length() - 1]);
    }

    for (size_t i = 0; i < tokens.size(); ++i) {
        if (needsSpace)
            builder.append(' ');
        builder.append(tokens[i]);
        needsSpace = true;
    }

    return builder.toAtomicString();
}

AtomicString DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token)
{
    Vector<String> tokens;
    tokens.append(token.string());
    return removeTokens(input, tokens);
}

// This returns an AtomicString because it is always passed as argument to setValue() and setValue()
// takes an AtomicString in argument.
AtomicString DOMTokenList::removeTokens(const AtomicString& input, const Vector<String>& tokens)
{
    // Algorithm defined at http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string
    // New spec is at http://dom.spec.whatwg.org/#remove-a-token-from-a-string

    unsigned inputLength = input.length();
    StringBuilder output; // 3
    output.reserveCapacity(inputLength);
    unsigned position = 0; // 4

    // Step 5
    while (position < inputLength) {
        if (isHTMLSpace<UChar>(input[position])) { // 6
            output.append(input[position++]); // 6.1, 6.2
            continue; // 6.3
        }

        // Step 7
        StringBuilder tokenBuilder;
        while (position < inputLength && isNotHTMLSpace<UChar>(input[position]))
            tokenBuilder.append(input[position++]);

        // Step 8
        String token = tokenBuilder.toString();
        if (tokens.contains(token)) {
            // Step 8.1
            while (position < inputLength && isHTMLSpace<UChar>(input[position]))
                ++position;

            // Step 8.2
            size_t j = output.length();
            while (j > 0 && isHTMLSpace<UChar>(output[j - 1]))
                --j;
            output.resize(j);

            // Step 8.3
            if (position < inputLength && !output.isEmpty())
                output.append(' ');
        } else {
            output.append(token); // Step 9
        }
    }

    return output.toAtomicString();
}

} // namespace blink
<|endoftext|>