<commit_before>#pragma once

#include "mmu.h"
#include "atomic.hh"
#ifndef XV6_USER
// XXX(Austin) lib/wqalloc.c includes percpu, which includes cpu.hh.
// I don't understand at all how percpu works in user space, but
// spinlock definitely doesn't work in user space.
#include "spinlock.h"
#endif

using std::atomic;

// Per-CPU state
struct cpu {
  cpuid_t id;                  // Index into cpus[] below
  int ncli;                    // Depth of pushcli nesting.
  int intena;                  // Were interrupts enabled before pushcli?
  struct segdesc gdt[NSEGS];   // x86 global descriptor table
  struct taskstate ts;         // Used by x86 to find stack for interrupt
  struct context *scheduler;   // swtch() here to enter scheduler

  int timer_printpc;
  atomic<u64> tlbflush_done;   // last tlb flush req done on this cpu
  struct proc *prev;           // The previously-running process
  atomic<struct proc*> fpu_owner; // The proc with the current FPU state

#ifndef XV6_USER
  // The list of IPI calls to this CPU
  atomic<struct ipi_call *> ipi __mpalign__;
  atomic<struct ipi_call *> *ipi_tail;
  // The lock protecting updates to ipi and ipi_tail.
  spinlock ipi_lock;
#endif

  hwid_t hwid __mpalign__;     // Local APIC ID, accessed by other CPUs

  // Cpu-local storage variables; see below
  struct cpu *cpu;
  struct proc *proc;           // The currently-running process.
  struct cpu_mem *mem;         // The per-core memory metadata
  u64 syscallno;               // Temporary used by sysentry
  struct kstats *kstats;
} __mpalign__;

extern struct cpu cpus[NCPU];

// Per-CPU variables, holding pointers to the
// current cpu and to the current process.
// XXX(sbw) asm labels default to RIP-relative and
// I don't know how to force absolute addressing.
static inline struct cpu *
mycpu(void)
{
  u64 val;
  __asm volatile("movq %%gs:0, %0" : "=r" (val));
  return (struct cpu *)val;
}

static inline struct proc *
myproc(void)
{
  u64 val;
  __asm volatile("movq %%gs:8, %0" : "=r" (val));
  return (struct proc *)val;
}

static inline struct kmem *
mykmem(void)
{
  u64 val;
  __asm volatile("movq %%gs:16, %0" : "=r" (val));
  return (struct kmem *)val;
}

static inline struct kstats *
mykstats(void)
{
  u64 val;
  __asm volatile("movq %%gs:(8*4), %0" : "=r" (val));
  return (struct kstats *)val;
}

static inline cpuid_t
myid(void)
{
  return mycpu()->id;
}
<commit_msg>cpu: Remove now unnecessary hacks<commit_after>#pragma once

#include "mmu.h"
#include "atomic.hh"
#include "spinlock.h"

using std::atomic;

// Per-CPU state
struct cpu {
  cpuid_t id;                  // Index into cpus[] below
  int ncli;                    // Depth of pushcli nesting.
  int intena;                  // Were interrupts enabled before pushcli?
  struct segdesc gdt[NSEGS];   // x86 global descriptor table
  struct taskstate ts;         // Used by x86 to find stack for interrupt
  struct context *scheduler;   // swtch() here to enter scheduler

  int timer_printpc;
  atomic<u64> tlbflush_done;   // last tlb flush req done on this cpu
  struct proc *prev;           // The previously-running process
  atomic<struct proc*> fpu_owner; // The proc with the current FPU state

  // The list of IPI calls to this CPU
  atomic<struct ipi_call *> ipi __mpalign__;
  atomic<struct ipi_call *> *ipi_tail;
  // The lock protecting updates to ipi and ipi_tail.
  spinlock ipi_lock;

  hwid_t hwid __mpalign__;     // Local APIC ID, accessed by other CPUs

  // Cpu-local storage variables; see below
  struct cpu *cpu;
  struct proc *proc;           // The currently-running process.
  struct cpu_mem *mem;         // The per-core memory metadata
  u64 syscallno;               // Temporary used by sysentry
  struct kstats *kstats;
} __mpalign__;

extern struct cpu cpus[NCPU];

// Per-CPU variables, holding pointers to the
// current cpu and to the current process.
// XXX(sbw) asm labels default to RIP-relative and
// I don't know how to force absolute addressing.
static inline struct cpu *
mycpu(void)
{
  u64 val;
  __asm volatile("movq %%gs:0, %0" : "=r" (val));
  return (struct cpu *)val;
}

static inline struct proc *
myproc(void)
{
  u64 val;
  __asm volatile("movq %%gs:8, %0" : "=r" (val));
  return (struct proc *)val;
}

static inline struct kmem *
mykmem(void)
{
  u64 val;
  __asm volatile("movq %%gs:16, %0" : "=r" (val));
  return (struct kmem *)val;
}

static inline struct kstats *
mykstats(void)
{
  u64 val;
  __asm volatile("movq %%gs:(8*4), %0" : "=r" (val));
  return (struct kstats *)val;
}

static inline cpuid_t
myid(void)
{
  return mycpu()->id;
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2013 Intel Corporation
 *
 * 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 (including the next
 * paragraph) 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.
 */

/**
 * \file opt_flip_matrices.cpp
 *
 * Convert (matrix * vector) operations to (vector * matrixTranspose),
 * which can be done using dot products rather than multiplies and adds.
 * On some hardware, this is more efficient.
 *
 * This currently only does the conversion for built-in matrices which
 * already have transposed equivalents.  Namely, gl_ModelViewProjectionMatrix
 * and gl_TextureMatrix.
 */
#include "ir.h"
#include "ir_optimization.h"
#include "main/macros.h"

namespace {
class matrix_flipper : public ir_hierarchical_visitor {
public:
   matrix_flipper(exec_list *instructions)
   {
      progress = false;
      mvp_transpose = NULL;
      texmat_transpose = NULL;

      foreach_list(n, instructions) {
         ir_instruction *ir = (ir_instruction *) n;
         ir_variable *var = ir->as_variable();
         if (!var)
            continue;
         if (strcmp(var->name, "gl_ModelViewProjectionMatrixTranspose") == 0)
            mvp_transpose = var;
         if (strcmp(var->name, "gl_TextureMatrixTranspose") == 0)
            texmat_transpose = var;
      }
   }

   ir_visitor_status visit_enter(ir_expression *ir);

   bool progress;

private:
   ir_variable *mvp_transpose;
   ir_variable *texmat_transpose;
};
}

ir_visitor_status
matrix_flipper::visit_enter(ir_expression *ir)
{
   if (ir->operation != ir_binop_mul ||
       !ir->operands[0]->type->is_matrix() ||
       !ir->operands[1]->type->is_vector())
      return visit_continue;

   ir_variable *mat_var = ir->operands[0]->variable_referenced();
   if (!mat_var)
      return visit_continue;

   if (mvp_transpose &&
       strcmp(mat_var->name, "gl_ModelViewProjectionMatrix") == 0) {
      ir_dereference_variable *deref = ir->operands[0]->as_dereference_variable();
      assert(deref && deref->var == mat_var);

      void *mem_ctx = ralloc_parent(ir);

      ir->operands[0] = ir->operands[1];
      ir->operands[1] = new(mem_ctx) ir_dereference_variable(mvp_transpose);

      progress = true;
   } else if (texmat_transpose &&
              strcmp(mat_var->name, "gl_TextureMatrix") == 0) {
      ir_dereference_array *array_ref = ir->operands[0]->as_dereference_array();
      assert(array_ref != NULL);
      ir_dereference_variable *var_ref = array_ref->array->as_dereference_variable();
      assert(var_ref && var_ref->var == mat_var);

      ir->operands[0] = ir->operands[1];
      ir->operands[1] = array_ref;

      var_ref->var = texmat_transpose;

      texmat_transpose->max_array_access =
         MAX2(texmat_transpose->max_array_access, mat_var->max_array_access);

      progress = true;
   }

   return visit_continue;
}

bool
opt_flip_matrices(struct exec_list *instructions)
{
   matrix_flipper v(instructions);

   visit_list_elements(&v, instructions);

   return v.progress;
}
<commit_msg>glsl: Silence unused variable warning in the release build<commit_after>/*
 * Copyright © 2013 Intel Corporation
 *
 * 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 (including the next
 * paragraph) 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.
 */

/**
 * \file opt_flip_matrices.cpp
 *
 * Convert (matrix * vector) operations to (vector * matrixTranspose),
 * which can be done using dot products rather than multiplies and adds.
 * On some hardware, this is more efficient.
 *
 * This currently only does the conversion for built-in matrices which
 * already have transposed equivalents.  Namely, gl_ModelViewProjectionMatrix
 * and gl_TextureMatrix.
 */
#include "ir.h"
#include "ir_optimization.h"
#include "main/macros.h"

namespace {
class matrix_flipper : public ir_hierarchical_visitor {
public:
   matrix_flipper(exec_list *instructions)
   {
      progress = false;
      mvp_transpose = NULL;
      texmat_transpose = NULL;

      foreach_list(n, instructions) {
         ir_instruction *ir = (ir_instruction *) n;
         ir_variable *var = ir->as_variable();
         if (!var)
            continue;
         if (strcmp(var->name, "gl_ModelViewProjectionMatrixTranspose") == 0)
            mvp_transpose = var;
         if (strcmp(var->name, "gl_TextureMatrixTranspose") == 0)
            texmat_transpose = var;
      }
   }

   ir_visitor_status visit_enter(ir_expression *ir);

   bool progress;

private:
   ir_variable *mvp_transpose;
   ir_variable *texmat_transpose;
};
}

ir_visitor_status
matrix_flipper::visit_enter(ir_expression *ir)
{
   if (ir->operation != ir_binop_mul ||
       !ir->operands[0]->type->is_matrix() ||
       !ir->operands[1]->type->is_vector())
      return visit_continue;

   ir_variable *mat_var = ir->operands[0]->variable_referenced();
   if (!mat_var)
      return visit_continue;

   if (mvp_transpose &&
       strcmp(mat_var->name, "gl_ModelViewProjectionMatrix") == 0) {
#ifndef NDEBUG
      ir_dereference_variable *deref = ir->operands[0]->as_dereference_variable();
      assert(deref && deref->var == mat_var);
#endif

      void *mem_ctx = ralloc_parent(ir);

      ir->operands[0] = ir->operands[1];
      ir->operands[1] = new(mem_ctx) ir_dereference_variable(mvp_transpose);

      progress = true;
   } else if (texmat_transpose &&
              strcmp(mat_var->name, "gl_TextureMatrix") == 0) {
      ir_dereference_array *array_ref = ir->operands[0]->as_dereference_array();
      assert(array_ref != NULL);
      ir_dereference_variable *var_ref = array_ref->array->as_dereference_variable();
      assert(var_ref && var_ref->var == mat_var);

      ir->operands[0] = ir->operands[1];
      ir->operands[1] = array_ref;

      var_ref->var = texmat_transpose;

      texmat_transpose->max_array_access =
         MAX2(texmat_transpose->max_array_access, mat_var->max_array_access);

      progress = true;
   }

   return visit_continue;
}

bool
opt_flip_matrices(struct exec_list *instructions)
{
   matrix_flipper v(instructions);

   visit_list_elements(&v, instructions);

   return v.progress;
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_SCAL_FUN_BINOMIAL_COEFFICIENT_LOG_HPP
#define STAN_MATH_PRIM_SCAL_FUN_BINOMIAL_COEFFICIENT_LOG_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/fun/lgamma.hpp>
#include <stan/math/prim/fun/lbeta.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <stan/math/prim/err/check_nonnegative.hpp>
#include <stan/math/prim/err/check_greater_or_equal.hpp>
#include <limits>

namespace stan {
namespace math {

/**
 * Return the log of the binomial coefficient for the specified
 * arguments.
 *
 * The binomial coefficient, \f${N \choose n}\f$, read "N choose n", is
 * defined for \f$0 \leq n \leq N\f$ by
 *
 * \f${N \choose n} = \frac{N!}{n! (N-n)!}\f$.
 *
 * This function uses Gamma functions to define the log
 * and generalize the arguments to continuous N and n.
 *
 * \f$ \log {N \choose n}
 * = \log \ \Gamma(N+1) - \log \Gamma(n+1) - \log \Gamma(N-n+1)\f$.
 *
 *
   \f[
   \mbox{binomial\_coefficient\_log}(x, y) =
   \begin{cases}
     \textrm{error} & \mbox{if } y > x + 1 \textrm{ or } y < -1 \textrm{ or } x
 < -1\\
     \ln\Gamma(x+1) & \mbox{if } -1 < y < x + 1 \\
     \quad -\ln\Gamma(y+1)& \\
     \quad -\ln\Gamma(x-y+1)& \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{binomial\_coefficient\_log}(x, y)}{\partial x} =
   \begin{cases}
     \textrm{error} & \mbox{if } y > x + 1 \textrm{ or } y < -1 \textrm{ or } x
 < -1\\
     \Psi(x+1) & \mbox{if } 0\leq y \leq x \\
     \quad -\Psi(x-y+1)& \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{binomial\_coefficient\_log}(x, y)}{\partial y} =
   \begin{cases}
     \textrm{error} & \mbox{if } y > x + 1 \textrm{ or } y < -1 \textrm{ or } x
 < -1\\
     -\Psi(y+1) & \mbox{if } 0\leq y \leq x \\
     \quad +\Psi(x-y+1)& \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]
 *
 *  This function is numerically more stable than naive evaluation via lgamma.
 *
 * @param N total number of objects.
 * @param n number of objects chosen.
 * @return log (N choose n).
 */

template <typename T_N, typename T_n>
inline return_type_t<T_N, T_n> binomial_coefficient_log(const T_N N,
                                                        const T_n n) {
  if (is_nan(value_of_rec(N)) || is_nan(value_of_rec(n))) {
    return std::numeric_limits<double>::quiet_NaN();
  }

  // For some uses it is important this works even when N < 0 and therefore
  // it is before checks
  if (n == 0) {
    return 0;
  }
  const T_N N_plus_1 = N + 1;

  static const char* function = "binomial_coefficient_log";
  check_greater_or_equal(function, "first argument", N, -1);
  check_greater_or_equal(function, "second argument", n, -1);
  check_greater_or_equal(function, "(first argument - second argument + 1)",
                         N - n + 1, 0.0);

  if (N / 2 < n) {
    return binomial_coefficient_log(N, N - n);
  } else if (N_plus_1 < lgamma_stirling_diff_useful) {
    return lgamma(N_plus_1) - lgamma(n + 1) - lgamma(N_plus_1 - n);
  } else {
    return -lbeta(N - n + 1, n + 1) - log(N_plus_1);
  }
}

}  // namespace math
}  // namespace stan
#endif
<commit_msg>Mismatched define<commit_after>#ifndef STAN_MATH_PRIM_FUN_BINOMIAL_COEFFICIENT_LOG_HPP
#define STAN_MATH_PRIM_FUN_BINOMIAL_COEFFICIENT_LOG_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/fun/lgamma.hpp>
#include <stan/math/prim/fun/lbeta.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <stan/math/prim/err/check_nonnegative.hpp>
#include <stan/math/prim/err/check_greater_or_equal.hpp>
#include <limits>

namespace stan {
namespace math {

/**
 * Return the log of the binomial coefficient for the specified
 * arguments.
 *
 * The binomial coefficient, \f${N \choose n}\f$, read "N choose n", is
 * defined for \f$0 \leq n \leq N\f$ by
 *
 * \f${N \choose n} = \frac{N!}{n! (N-n)!}\f$.
 *
 * This function uses Gamma functions to define the log
 * and generalize the arguments to continuous N and n.
 *
 * \f$ \log {N \choose n}
 * = \log \ \Gamma(N+1) - \log \Gamma(n+1) - \log \Gamma(N-n+1)\f$.
 *
 *
   \f[
   \mbox{binomial\_coefficient\_log}(x, y) =
   \begin{cases}
     \textrm{error} & \mbox{if } y > x + 1 \textrm{ or } y < -1 \textrm{ or } x
 < -1\\
     \ln\Gamma(x+1) & \mbox{if } -1 < y < x + 1 \\
     \quad -\ln\Gamma(y+1)& \\
     \quad -\ln\Gamma(x-y+1)& \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{binomial\_coefficient\_log}(x, y)}{\partial x} =
   \begin{cases}
     \textrm{error} & \mbox{if } y > x + 1 \textrm{ or } y < -1 \textrm{ or } x
 < -1\\
     \Psi(x+1) & \mbox{if } 0\leq y \leq x \\
     \quad -\Psi(x-y+1)& \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{binomial\_coefficient\_log}(x, y)}{\partial y} =
   \begin{cases}
     \textrm{error} & \mbox{if } y > x + 1 \textrm{ or } y < -1 \textrm{ or } x
 < -1\\
     -\Psi(y+1) & \mbox{if } 0\leq y \leq x \\
     \quad +\Psi(x-y+1)& \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]
 *
 *  This function is numerically more stable than naive evaluation via lgamma.
 *
 * @param N total number of objects.
 * @param n number of objects chosen.
 * @return log (N choose n).
 */

template <typename T_N, typename T_n>
inline return_type_t<T_N, T_n> binomial_coefficient_log(const T_N N,
                                                        const T_n n) {
  if (is_nan(value_of_rec(N)) || is_nan(value_of_rec(n))) {
    return std::numeric_limits<double>::quiet_NaN();
  }

  // For some uses it is important this works even when N < 0 and therefore
  // it is before checks
  if (n == 0) {
    return 0;
  }
  const T_N N_plus_1 = N + 1;

  static const char* function = "binomial_coefficient_log";
  check_greater_or_equal(function, "first argument", N, -1);
  check_greater_or_equal(function, "second argument", n, -1);
  check_greater_or_equal(function, "(first argument - second argument + 1)",
                         N - n + 1, 0.0);

  if (N / 2 < n) {
    return binomial_coefficient_log(N, N - n);
  } else if (N_plus_1 < lgamma_stirling_diff_useful) {
    return lgamma(N_plus_1) - lgamma(n + 1) - lgamma(N_plus_1 - n);
  } else {
    return -lbeta(N - n + 1, n + 1) - log(N_plus_1);
  }
}

}  // namespace math
}  // namespace stan
#endif
<|endoftext|>


<commit_before>#include <cstring>
#include "halley/support/exception.h"
#include "halley/support/console.h"
#include "shader_opengl.h"
#include "halley/core/graphics/material/material_definition.h"
#include "gl_utils.h"
#include "halley_gl.h"

using namespace Halley;

static ShaderOpenGL* currentShader = nullptr;

#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif

ShaderOpenGL::ShaderOpenGL(const ShaderDefinition& definition)
{
	id = glCreateProgram();
	glCheckError();

	name = definition.name;
	setAttributes(definition.vertexAttributes);
	loadShaders(definition.shaders);
	compile();
}

ShaderOpenGL::~ShaderOpenGL()
{
	destroy();
}

void ShaderOpenGL::bind()
{
	if (this != currentShader) {
		if (!ready) {
			compile();
		}
		glUseProgram(id);
		glCheckError();
		currentShader = this;
	}
}

void ShaderOpenGL::unbind()
{
	currentShader = nullptr;
	glUseProgram(0);
	glCheckError();
}

static GLuint loadShader(const Bytes& src, GLenum type, String name)
{
	// Create shader
	GLuint shader = glCreateShader(type);
	glCheckError();

	// Load source
#ifdef WITH_OPENGL_ES3
    size_t startPos = 0;
    if (src.size() >= 13 && memcmp(src.data(), "#version 330", 12) == 0) {
        startPos = 12;
    }
    size_t len = src.size() + 15 - startPos;
    GLchar* buffer = new GLchar[len + 1];
    memcpy(buffer, "#version 300 es", 15);
    memcpy(buffer + 15, src.data() + startPos, len - 15);
    buffer[len] = 0;
#else
	size_t len = src.size();
	GLchar* buffer = new GLchar[len + 1];
	memcpy(buffer, src.data(), src.size());
	buffer[len] = 0;
#endif

	// Set source
    const char* cbuf = buffer;
	glShaderSource(shader, 1, &cbuf, nullptr);
	glCheckError();

	// Compile source
	glCompileShader(shader);
	glCheckError();

	delete[] buffer;

	// Check result
	// Seriously, GL? All this crap to retrieve an info log?
	int result;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
	glCheckError();
	if (result == GL_FALSE) {
		int infolen;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infolen);
		glCheckError();
		char* log = new char[infolen];
		glGetShaderInfoLog(shader, infolen, &infolen, log);
		String msg = String("Error compiling shader \"" + name + "\":\n") + log;
		delete[] log;
		glCheckError();
		throw Exception(msg, HalleyExceptions::VideoPlugin);
	}

	return shader;
}

void ShaderOpenGL::loadShaders(const std::map<ShaderType, Bytes>& sources)
{
	for (auto& s: sources) {
		auto type = s.first;
		int glType = 0;
		switch (type) {
		case ShaderType::Vertex:
			glType = GL_VERTEX_SHADER;
			break;
		case ShaderType::Pixel:
			glType = GL_FRAGMENT_SHADER;
			break;
#ifdef WITH_OPENGL
		case ShaderType::Geometry:
			glType = GL_GEOMETRY_SHADER;
			break;
#endif
		default:
			throw Exception("Unsupported shader type: " + toString(type), HalleyExceptions::VideoPlugin);
		}

		shaders.push_back(loadShader(s.second, glType, name + "/" + toString(type)));
	}
}

void ShaderOpenGL::compile()
{
	if (!ready) {
		// Create program
		for (size_t i = 0; i<shaders.size(); i++) {
			glAttachShader(id, shaders[i]);
			glCheckError();
		}
		glLinkProgram(id);
		glCheckError();

		// Collect log
		int infolen;
		glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infolen);
		glCheckError();
		char* logRaw = new char[infolen + 1];
		glGetProgramInfoLog(id, infolen, &infolen, logRaw);
		logRaw[infolen] = 0;
		String log = logRaw;
		delete[] logRaw;
		glCheckError();

		// Verify result
		int result;
		glGetProgramiv(id, GL_LINK_STATUS, &result);
		glCheckError();
		if (result == GL_FALSE) {
			throw Exception("Error loading shader: " + log, HalleyExceptions::VideoPlugin);
		} else if (infolen > 0) {
			std::cout << ConsoleColour(Console::YELLOW) << "\nIn shader \"" << name << "\":\n==========\n" << log << "\n==========" << ConsoleColour() << std::endl;
		}

		uniformLocations.clear();
		attributeLocations.clear();
		ready = true;
	}
}

void ShaderOpenGL::destroy()
{
	if (ready) {
		glCheckError();
		unbind();
		ready = false;

		for (size_t i = 0; i < shaders.size(); i++) {
			glDetachShader(id, shaders[i]);
			glCheckError();
			glDeleteShader(shaders[i]);
			glCheckError();
		}
	}

	if (id != 0) {
		glDeleteProgram(id);
		glCheckError();
		id = 0;
	}
}

void ShaderOpenGL::setUniformBlockBinding(unsigned int blockIndex, unsigned int binding)
{
	glUniformBlockBinding(id, blockIndex, binding);
	glCheckError();
}

int ShaderOpenGL::getUniformLocation(const String& name, ShaderType stage)
{
	if (stage != ShaderType::Combined) {
		// OpenGL doesn't support per-stage bindings
		return -1;
	}

	auto i = uniformLocations.find(name);
	if (i != uniformLocations.end()) {
		return int(i->second);
	}

	unsigned int result = glGetUniformLocation(id, name.c_str());
	glCheckError();

	uniformLocations[name] = result;
	return int(result);
}

int ShaderOpenGL::getBlockLocation(const String& name, ShaderType stage)
{
	if (stage != ShaderType::Combined) {
		// OpenGL doesn't support per-stage bindings
		return -1;
	}

	auto i = blockLocations.find(name);
	if (i != blockLocations.end()) {
		return int(i->second);
	}

	unsigned int result = glGetUniformBlockIndex(id, name.c_str());
	glCheckError();

	blockLocations[name] = result;
	return int(result);
}

int ShaderOpenGL::getAttributeLocation(const String& name)
{
	auto i = attributeLocations.find(name);
	if (i != attributeLocations.end()) {
		return int(i->second);
	}

	unsigned int result = glGetAttribLocation(id, name.c_str());
	glCheckError();

	attributeLocations[name] = result;
	return int(result);
}

void ShaderOpenGL::setAttributes(const Vector<MaterialAttribute>& attributes)
{
	for (auto& attribute : attributes) {
		glBindAttribLocation(id, attribute.location, attribute.name.c_str());
		glCheckError();
	}
}
<commit_msg>Disable attribute binding to name<commit_after>#include <cstring>
#include "halley/support/exception.h"
#include "halley/support/console.h"
#include "shader_opengl.h"
#include "halley/core/graphics/material/material_definition.h"
#include "gl_utils.h"
#include "halley_gl.h"

using namespace Halley;

static ShaderOpenGL* currentShader = nullptr;

#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif

ShaderOpenGL::ShaderOpenGL(const ShaderDefinition& definition)
{
	id = glCreateProgram();
	glCheckError();

	name = definition.name;
	//setAttributes(definition.vertexAttributes);
	loadShaders(definition.shaders);
	compile();
}

ShaderOpenGL::~ShaderOpenGL()
{
	destroy();
}

void ShaderOpenGL::bind()
{
	if (this != currentShader) {
		if (!ready) {
			compile();
		}
		glUseProgram(id);
		glCheckError();
		currentShader = this;
	}
}

void ShaderOpenGL::unbind()
{
	currentShader = nullptr;
	glUseProgram(0);
	glCheckError();
}

static GLuint loadShader(const Bytes& src, GLenum type, String name)
{
	// Create shader
	GLuint shader = glCreateShader(type);
	glCheckError();

	// Load source
#ifdef WITH_OPENGL_ES3
    size_t startPos = 0;
    if (src.size() >= 13 && memcmp(src.data(), "#version 330", 12) == 0) {
        startPos = 12;
    }
    size_t len = src.size() + 15 - startPos;
    GLchar* buffer = new GLchar[len + 1];
    memcpy(buffer, "#version 300 es", 15);
    memcpy(buffer + 15, src.data() + startPos, len - 15);
    buffer[len] = 0;
#else
	size_t len = src.size();
	GLchar* buffer = new GLchar[len + 1];
	memcpy(buffer, src.data(), src.size());
	buffer[len] = 0;
#endif

	// Set source
    const char* cbuf = buffer;
	glShaderSource(shader, 1, &cbuf, nullptr);
	glCheckError();

	// Compile source
	glCompileShader(shader);
	glCheckError();

	delete[] buffer;

	// Check result
	// Seriously, GL? All this crap to retrieve an info log?
	int result;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
	glCheckError();
	if (result == GL_FALSE) {
		int infolen;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infolen);
		glCheckError();
		char* log = new char[infolen];
		glGetShaderInfoLog(shader, infolen, &infolen, log);
		String msg = String("Error compiling shader \"" + name + "\":\n") + log;
		delete[] log;
		glCheckError();
		throw Exception(msg, HalleyExceptions::VideoPlugin);
	}

	return shader;
}

void ShaderOpenGL::loadShaders(const std::map<ShaderType, Bytes>& sources)
{
	for (auto& s: sources) {
		auto type = s.first;
		int glType = 0;
		switch (type) {
		case ShaderType::Vertex:
			glType = GL_VERTEX_SHADER;
			break;
		case ShaderType::Pixel:
			glType = GL_FRAGMENT_SHADER;
			break;
#ifdef WITH_OPENGL
		case ShaderType::Geometry:
			glType = GL_GEOMETRY_SHADER;
			break;
#endif
		default:
			throw Exception("Unsupported shader type: " + toString(type), HalleyExceptions::VideoPlugin);
		}

		shaders.push_back(loadShader(s.second, glType, name + "/" + toString(type)));
	}
}

void ShaderOpenGL::compile()
{
	if (!ready) {
		// Create program
		for (size_t i = 0; i<shaders.size(); i++) {
			glAttachShader(id, shaders[i]);
			glCheckError();
		}
		glLinkProgram(id);
		glCheckError();

		// Collect log
		int infolen;
		glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infolen);
		glCheckError();
		char* logRaw = new char[infolen + 1];
		glGetProgramInfoLog(id, infolen, &infolen, logRaw);
		logRaw[infolen] = 0;
		String log = logRaw;
		delete[] logRaw;
		glCheckError();

		// Verify result
		int result;
		glGetProgramiv(id, GL_LINK_STATUS, &result);
		glCheckError();
		if (result == GL_FALSE) {
			throw Exception("Error loading shader: " + log, HalleyExceptions::VideoPlugin);
		} else if (infolen > 0) {
			std::cout << ConsoleColour(Console::YELLOW) << "\nIn shader \"" << name << "\":\n==========\n" << log << "\n==========" << ConsoleColour() << std::endl;
		}

		uniformLocations.clear();
		attributeLocations.clear();
		ready = true;
	}
}

void ShaderOpenGL::destroy()
{
	if (ready) {
		glCheckError();
		unbind();
		ready = false;

		for (size_t i = 0; i < shaders.size(); i++) {
			glDetachShader(id, shaders[i]);
			glCheckError();
			glDeleteShader(shaders[i]);
			glCheckError();
		}
	}

	if (id != 0) {
		glDeleteProgram(id);
		glCheckError();
		id = 0;
	}
}

void ShaderOpenGL::setUniformBlockBinding(unsigned int blockIndex, unsigned int binding)
{
	glUniformBlockBinding(id, blockIndex, binding);
	glCheckError();
}

int ShaderOpenGL::getUniformLocation(const String& name, ShaderType stage)
{
	if (stage != ShaderType::Combined) {
		// OpenGL doesn't support per-stage bindings
		return -1;
	}

	auto i = uniformLocations.find(name);
	if (i != uniformLocations.end()) {
		return int(i->second);
	}

	unsigned int result = glGetUniformLocation(id, name.c_str());
	glCheckError();

	uniformLocations[name] = result;
	return int(result);
}

int ShaderOpenGL::getBlockLocation(const String& name, ShaderType stage)
{
	if (stage != ShaderType::Combined) {
		// OpenGL doesn't support per-stage bindings
		return -1;
	}

	auto i = blockLocations.find(name);
	if (i != blockLocations.end()) {
		return int(i->second);
	}

	unsigned int result = glGetUniformBlockIndex(id, name.c_str());
	glCheckError();

	blockLocations[name] = result;
	return int(result);
}

int ShaderOpenGL::getAttributeLocation(const String& name)
{
	auto i = attributeLocations.find(name);
	if (i != attributeLocations.end()) {
		return int(i->second);
	}

	unsigned int result = glGetAttribLocation(id, name.c_str());
	glCheckError();

	attributeLocations[name] = result;
	return int(result);
}

void ShaderOpenGL::setAttributes(const Vector<MaterialAttribute>& attributes)
{
	for (auto& attribute : attributes) {
		glBindAttribLocation(id, attribute.location, attribute.name.c_str());
		glCheckError();
	}
}
<|endoftext|>


<commit_before>// @(#)root/hist:$Name:  $:$Id: THStack.cxx,v 1.5 2001/12/10 15:03:18 rdm Exp $
// Author: Rene Brun   10/12/2001

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

#include "TROOT.h"
#include "THStack.h"
#include "TVirtualPad.h"
#include "TH2.h"

#include <fstream.h>
#include <iostream.h>

ClassImp(THStack)

//______________________________________________________________________________
//
//   A THStack is a collection of TH1 (or derived) objects
//   Use THStack::Add to add a new histogram to the list.
//   The THStack owns the objects in the list.
//   By default (if option "nostack" is not specified), histograms will be paint
//   stacked on top of each other.
//   Example;
//      THStack hs("hs","test stacked histograms");
//      TH1F *h1 = new TH1F("h1","test hstack",100,-4,4);
//      h1->FillRandom("gaus",20000);
//      h1->SetFillColor(kRed);
//      hs.Add(h1);
//      TH1F *h2 = new TH1F("h2","test hstack",100,-4,4);
//      h2->FillRandom("gaus",15000);
//      h2->SetFillColor(kBlue);
//      hs.Add(h2);
//      TH1F *h3 = new TH1F("h3","test hstack",100,-4,4);
//      h3->FillRandom("gaus",10000);
//      h3->SetFillColor(kGreen);
//      hs.Add(h3);
//      TCanvas c1("c1","stacked hists",10,10,700,900);
//      c1.Divide(1,2);
//      c1.cd(1);
//      hs.Draw();
//      c1.cd(2);
//      hs->Draw("nostack");
//
//  See a more complex example in $ROOTSYS/tutorials/hstack.C
//
//  Note that picking is supported for all drawing modes.

//______________________________________________________________________________
THStack::THStack(): TNamed()
{
// THStack default constructor

   fHists     = 0;
   fStack     = 0;
   fHistogram = 0;
   fMaximum   = -1111;
   fMinimum   = -1111;
}

//______________________________________________________________________________
THStack::THStack(const char *name, const char *title)
       : TNamed(name,title)
{
// constructor with name and title
   fHists     = 0;
   fStack     = 0;
   fHistogram = 0;
   fMaximum   = -1111;
   fMinimum   = -1111;
}

//______________________________________________________________________________
THStack::~THStack()
{
// THStack destructor

   if (!fHists) return;
   fHists->Delete();
   delete fHists;
   fHists = 0;
   if (fStack) {fStack->Delete(); delete fStack;}
   delete fHistogram;
   fHistogram = 0;
}

//______________________________________________________________________________
void THStack::Add(TH1 *h1)
{
   // add a new histogram to the list
   // Only 1-d histograms currently supported.
   // Note that all histograms in the list must have the same number
   // of channels and the same X axis.

   if (!h1) return;
   if (h1->GetDimension() > 2) {
      Error("Add","THStack supports only 1-d and 2-d histograms");
      return;
   }
   if (!fHists) fHists = new TObjArray();
   fHists->Add(h1);
}

//______________________________________________________________________________
void THStack::Browse(TBrowser *)
{
    Draw();
    gPad->Update();
}

//______________________________________________________________________________
void THStack::BuildStack()
{
//  build sum of all histograms
//  Build a separate list fStack containing the running sum of all histograms

   if (fStack) return;
   Int_t nhists = fHists->GetEntriesFast();
   fStack = new TObjArray(nhists);
   Bool_t add = TH1::AddDirectoryStatus();
   TH1::AddDirectory(kFALSE);
   TH1 *h = (TH1*)fHists->At(0)->Clone();
   fStack->Add(h);
   for (Int_t i=1;i<nhists;i++) {
      h = (TH1*)fHists->At(i)->Clone();
      h->Add((TH1*)fStack->At(i-1));
      fStack->AddAt(h,i);
   }
   TH1::AddDirectory(add);
}

//______________________________________________________________________________
Int_t THStack::DistancetoPrimitive(Int_t px, Int_t py)
{
// Compute distance from point px,py to each graph
//

//*-*- Are we on the axis?
   const Int_t kMaxDiff = 10;
   Int_t distance = 9999;
   if (fHistogram) {
      distance = fHistogram->DistancetoPrimitive(px,py);
      if (distance <= 0) {return distance;}
      if (distance <= 1) {gPad->SetSelected(fHistogram);return distance;}
   }

//*-*- Loop on the list of histograms
   if (!fHists) return distance;
   TH1 *h = 0;
   const char *doption = GetDrawOption();
   Int_t nhists = fHists->GetEntriesFast();
   for (Int_t i=0;i<nhists;i++) {
      h = (TH1*)fHists->At(i);
      if (fStack && !strstr(doption,"nostack")) h = (TH1*)fStack->At(i);
      Int_t dist = h->DistancetoPrimitive(px,py);
      if (dist < kMaxDiff) {
         gPad->SetSelected(fHists->At(i));
         gPad->SetCursor(kPointer);
         return dist;
      }
   }
   return distance;
}

//______________________________________________________________________________
void THStack::Draw(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this multihist with its current attributes*-*-*-*-*-*-*
//*-*                  ==========================================
//
//   Options to draw histograms  are described in THistPainter::Paint
// By default (if option "nostack" is not specified), histograms will be paint
// stacked on top of each other.

   TString opt = option;
   opt.ToLower();
   if (gPad) {
      if (!gPad->IsEditable()) (gROOT->GetMakeDefCanvas())();
      if (!opt.Contains("same")) {
         //the following statement is necessary in case one attempts to draw
         //a temporary histogram already in the current pad
         if (TestBit(kCanDelete)) gPad->GetListOfPrimitives()->Remove(this);
         gPad->Clear();
      }
   }
   AppendPad(opt.Data());
}

//______________________________________________________________________________
TH1 *THStack::GetHistogram() const
{
//    Returns a pointer to the histogram used to draw the axis
//    Takes into account the two following cases.
//       1- option 'A' was specified in THStack::Draw. Return fHistogram
//       2- user had called TPad::DrawFrame. return pointer to hframe histogram

   if (fHistogram) return fHistogram;
   if (!gPad) return 0;
   gPad->Modified();
   gPad->Update();
   if (fHistogram) return fHistogram;
   TH1 *h1 = (TH1*)gPad->FindObject("hframe");
   return h1;
}

//______________________________________________________________________________
Double_t THStack::GetMaximum(Option_t *option)
{
//  returns the maximum of all added histograms
//  returns the maximum of all histograms if option "nostack".

   TString opt = option;
   opt.ToLower();
   Double_t them=0, themax = -1e300;
   Int_t nhists = fHists->GetEntriesFast();
   TH1 *h;
   if (!opt.Contains("nostack")) {
       BuildStack();
       h = (TH1*)fStack->At(nhists-1);
       themax = h->GetMaximum();
       if (strstr(opt.Data(),"e1")) themax += TMath::Sqrt(TMath::Abs(themax));
   } else {
      for (Int_t i=0;i<nhists;i++) {
         h = (TH1*)fHists->At(i);
         them = h->GetMaximum();
         if (strstr(opt.Data(),"e1")) them += TMath::Sqrt(TMath::Abs(them));
         if (them > themax) themax = them;
      }
   }
   return themax;
}

//______________________________________________________________________________
Double_t THStack::GetMinimum(Option_t *option)
{
//  returns the minimum of all added histograms
//  returns the minimum of all histograms if option "nostack".

   TString opt = option;
   opt.ToLower();
   Double_t them=0, themin = 1e300;
   Int_t nhists = fHists->GetEntriesFast();
   TH1 *h;
   if (!opt.Contains("nostack")) {
       BuildStack();
       h = (TH1*)fStack->At(nhists-1);
       themin = h->GetMinimum();
   } else {
      for (Int_t i=0;i<nhists;i++) {
         h = (TH1*)fHists->At(i);
         them = h->GetMinimum();
         if (them < themin) themin = them;
      }
   }
   return themin;
}

//______________________________________________________________________________
TAxis *THStack::GetXaxis() const
{
   // Get x axis of the graph.

   if (!gPad) return 0;
   return GetHistogram()->GetXaxis();
}

//______________________________________________________________________________
TAxis *THStack::GetYaxis() const
{
   // Get y axis of the graph.

   if (!gPad) return 0;
   return GetHistogram()->GetYaxis();
}

//______________________________________________________________________________
void THStack::ls(Option_t *option) const
{
   // List histograms in the stack

   TROOT::IndentLevel();
   cout <<IsA()->GetName()
        <<" Name= "<<GetName()<<" Title= "<<GetTitle()<<" Option="<<option<<endl;
   TROOT::IncreaseDirLevel();
   if (fHists) fHists->ls(option);
   TROOT::DecreaseDirLevel();
}

//______________________________________________________________________________
void THStack::Modified()
{
// invalidate sum of histograms

   if (!fStack) return;
   fStack->Delete();
   delete fStack;
   fStack = 0;
}

//______________________________________________________________________________
void THStack::Paint(Option_t *option)
{
// paint the list of histograms
// By default, histograms are shown stacked.
//    -the first histogram is paint
//    -then the sum of the first and second, etc
// If option "nostack" is specified, histograms are all paint in the same pad
// as if the option "same" had been specified.
//
// See THistPainter::Paint for a list of valid options.

   TString opt = option;
   opt.ToLower();
   char loption[32];
   sprintf(loption,"%s",opt.Data());
   char *nostack = strstr(loption,"nostack");
   // do not delete the stack. Another pad may contain the same object
   // drawn in stack mode!
   //if (nostack && fStack) {fStack->Delete(); delete fStack; fStack = 0;}

   if (!opt.Contains("nostack")) BuildStack();

   Double_t themax,themin;
   if (fMaximum == -1111) themax = GetMaximum(option);
   else                   themax = fMaximum;
   if (fMinimum == -1111) themin = GetMinimum(option);
   else                   themin = fMinimum;
   if (!fHistogram) {
      Bool_t add = TH1::AddDirectoryStatus();
      TH1::AddDirectory(kFALSE);
      TH1 *h = (TH1*)fHists->At(0);
      TAxis *xaxis = h->GetXaxis();
      TAxis *yaxis = h->GetYaxis();
      if (h->GetDimension() > 1) {
         if (strlen(option) == 0) strcpy(loption,"lego1");
         fHistogram = new TH2F(GetName(),GetTitle(),
                               xaxis->GetNbins(),xaxis->GetXmin(),xaxis->GetXmax(),
                               yaxis->GetNbins(),yaxis->GetXmin(),yaxis->GetXmax());
      } else {
         fHistogram = new TH1F(GetName(),GetTitle(),xaxis->GetNbins(),xaxis->GetXmin(),xaxis->GetXmax());
      }
      fHistogram->SetStats(0);
      TH1::AddDirectory(add);
   }

   if (nostack) {*nostack = 0; strcat(nostack,nostack+7);}
   else fHistogram->GetPainter()->SetStack(fHists);

   fHistogram->SetMaximum(1.05*themax);
   fHistogram->SetMinimum(themin);
   fHistogram->Paint(loption);

   if (fHistogram->GetDimension() > 1) SetDrawOption(loption);
   if (strstr(loption,"lego")) return;

   Int_t nhists = fHists->GetEntriesFast();
   strcat(loption,"same");
   for (Int_t i=0;i<nhists;i++) {
      if (nostack) fHists->At(i)->Paint(loption);
      else         fStack->At(nhists-i-1)->Paint(loption);
   }
}

//______________________________________________________________________________
void THStack::Print(Option_t *option) const
{
// Print the list of histograms

   TH1 *h;
   if (fHists) {
     TIter   next(fHists);
     while ((h = (TH1*) next())) {
       h->Print(option);
     }
   }
}

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

   char quote = '"';
   out<<"   "<<endl;
   if (gROOT->ClassSaved(THStack::Class())) {
       out<<"   ";
   } else {
       out<<"   THStack *";
   }
   out<<"hstack = new THStack();"<<endl;
   out<<"   hstack->SetName("<<quote<<GetName()<<quote<<");"<<endl;
   out<<"   hstack->SetTitle("<<quote<<GetTitle()<<quote<<");"<<endl;

   TH1 *h;
   if (fHists) {
     TIter   next(fHists);
     while ((h = (TH1*) next())) {
       h->SavePrimitive(out,"nodraw");
       out<<"   hstack->Add("<<h->GetName()<<");"<<endl;
     }
   }
   out<<"   hstack->Draw("
      <<quote<<option<<quote<<");"<<endl;
}

//______________________________________________________________________________
void THStack::SetMaximum(Double_t maximum)
{
   fMaximum = maximum;
   if (fHistogram)  fHistogram->SetMaximum(maximum);
}

//______________________________________________________________________________
void THStack::SetMinimum(Double_t minimum)
{
   fMinimum = minimum;
   if (fHistogram) fHistogram->SetMinimum(minimum);
}
<commit_msg>In THStack::Paint, Use a default minimum=0 if the minimum is positive.<commit_after>// @(#)root/hist:$Name:  $:$Id: THStack.cxx,v 1.6 2001/12/21 13:41:09 brun Exp $
// Author: Rene Brun   10/12/2001

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

#include "TROOT.h"
#include "THStack.h"
#include "TVirtualPad.h"
#include "TH2.h"

#include <fstream.h>
#include <iostream.h>

ClassImp(THStack)

//______________________________________________________________________________
//
//   A THStack is a collection of TH1 (or derived) objects
//   Use THStack::Add to add a new histogram to the list.
//   The THStack owns the objects in the list.
//   By default (if option "nostack" is not specified), histograms will be paint
//   stacked on top of each other.
//   Example;
//      THStack hs("hs","test stacked histograms");
//      TH1F *h1 = new TH1F("h1","test hstack",100,-4,4);
//      h1->FillRandom("gaus",20000);
//      h1->SetFillColor(kRed);
//      hs.Add(h1);
//      TH1F *h2 = new TH1F("h2","test hstack",100,-4,4);
//      h2->FillRandom("gaus",15000);
//      h2->SetFillColor(kBlue);
//      hs.Add(h2);
//      TH1F *h3 = new TH1F("h3","test hstack",100,-4,4);
//      h3->FillRandom("gaus",10000);
//      h3->SetFillColor(kGreen);
//      hs.Add(h3);
//      TCanvas c1("c1","stacked hists",10,10,700,900);
//      c1.Divide(1,2);
//      c1.cd(1);
//      hs.Draw();
//      c1.cd(2);
//      hs->Draw("nostack");
//
//  See a more complex example in $ROOTSYS/tutorials/hstack.C
//
//  Note that picking is supported for all drawing modes.

//______________________________________________________________________________
THStack::THStack(): TNamed()
{
// THStack default constructor

   fHists     = 0;
   fStack     = 0;
   fHistogram = 0;
   fMaximum   = -1111;
   fMinimum   = -1111;
}

//______________________________________________________________________________
THStack::THStack(const char *name, const char *title)
       : TNamed(name,title)
{
// constructor with name and title
   fHists     = 0;
   fStack     = 0;
   fHistogram = 0;
   fMaximum   = -1111;
   fMinimum   = -1111;
}

//______________________________________________________________________________
THStack::~THStack()
{
// THStack destructor

   if (!fHists) return;
   fHists->Delete();
   delete fHists;
   fHists = 0;
   if (fStack) {fStack->Delete(); delete fStack;}
   delete fHistogram;
   fHistogram = 0;
}

//______________________________________________________________________________
void THStack::Add(TH1 *h1)
{
   // add a new histogram to the list
   // Only 1-d histograms currently supported.
   // Note that all histograms in the list must have the same number
   // of channels and the same X axis.

   if (!h1) return;
   if (h1->GetDimension() > 2) {
      Error("Add","THStack supports only 1-d and 2-d histograms");
      return;
   }
   if (!fHists) fHists = new TObjArray();
   fHists->Add(h1);
}

//______________________________________________________________________________
void THStack::Browse(TBrowser *)
{
    Draw();
    gPad->Update();
}

//______________________________________________________________________________
void THStack::BuildStack()
{
//  build sum of all histograms
//  Build a separate list fStack containing the running sum of all histograms

   if (fStack) return;
   Int_t nhists = fHists->GetEntriesFast();
   fStack = new TObjArray(nhists);
   Bool_t add = TH1::AddDirectoryStatus();
   TH1::AddDirectory(kFALSE);
   TH1 *h = (TH1*)fHists->At(0)->Clone();
   fStack->Add(h);
   for (Int_t i=1;i<nhists;i++) {
      h = (TH1*)fHists->At(i)->Clone();
      h->Add((TH1*)fStack->At(i-1));
      fStack->AddAt(h,i);
   }
   TH1::AddDirectory(add);
}

//______________________________________________________________________________
Int_t THStack::DistancetoPrimitive(Int_t px, Int_t py)
{
// Compute distance from point px,py to each graph
//

//*-*- Are we on the axis?
   const Int_t kMaxDiff = 10;
   Int_t distance = 9999;
   if (fHistogram) {
      distance = fHistogram->DistancetoPrimitive(px,py);
      if (distance <= 0) {return distance;}
      if (distance <= 1) {gPad->SetSelected(fHistogram);return distance;}
   }

//*-*- Loop on the list of histograms
   if (!fHists) return distance;
   TH1 *h = 0;
   const char *doption = GetDrawOption();
   Int_t nhists = fHists->GetEntriesFast();
   for (Int_t i=0;i<nhists;i++) {
      h = (TH1*)fHists->At(i);
      if (fStack && !strstr(doption,"nostack")) h = (TH1*)fStack->At(i);
      Int_t dist = h->DistancetoPrimitive(px,py);
      if (dist < kMaxDiff) {
         gPad->SetSelected(fHists->At(i));
         gPad->SetCursor(kPointer);
         return dist;
      }
   }
   return distance;
}

//______________________________________________________________________________
void THStack::Draw(Option_t *option)
{
//*-*-*-*-*-*-*-*-*-*-*Draw this multihist with its current attributes*-*-*-*-*-*-*
//*-*                  ==========================================
//
//   Options to draw histograms  are described in THistPainter::Paint
// By default (if option "nostack" is not specified), histograms will be paint
// stacked on top of each other.

   TString opt = option;
   opt.ToLower();
   if (gPad) {
      if (!gPad->IsEditable()) (gROOT->GetMakeDefCanvas())();
      if (!opt.Contains("same")) {
         //the following statement is necessary in case one attempts to draw
         //a temporary histogram already in the current pad
         if (TestBit(kCanDelete)) gPad->GetListOfPrimitives()->Remove(this);
         gPad->Clear();
      }
   }
   AppendPad(opt.Data());
}

//______________________________________________________________________________
TH1 *THStack::GetHistogram() const
{
//    Returns a pointer to the histogram used to draw the axis
//    Takes into account the two following cases.
//       1- option 'A' was specified in THStack::Draw. Return fHistogram
//       2- user had called TPad::DrawFrame. return pointer to hframe histogram

   if (fHistogram) return fHistogram;
   if (!gPad) return 0;
   gPad->Modified();
   gPad->Update();
   if (fHistogram) return fHistogram;
   TH1 *h1 = (TH1*)gPad->FindObject("hframe");
   return h1;
}

//______________________________________________________________________________
Double_t THStack::GetMaximum(Option_t *option)
{
//  returns the maximum of all added histograms
//  returns the maximum of all histograms if option "nostack".

   TString opt = option;
   opt.ToLower();
   Double_t them=0, themax = -1e300;
   Int_t nhists = fHists->GetEntriesFast();
   TH1 *h;
   if (!opt.Contains("nostack")) {
       BuildStack();
       h = (TH1*)fStack->At(nhists-1);
       themax = h->GetMaximum();
       if (strstr(opt.Data(),"e1")) themax += TMath::Sqrt(TMath::Abs(themax));
   } else {
      for (Int_t i=0;i<nhists;i++) {
         h = (TH1*)fHists->At(i);
         them = h->GetMaximum();
         if (strstr(opt.Data(),"e1")) them += TMath::Sqrt(TMath::Abs(them));
         if (them > themax) themax = them;
      }
   }
   return themax;
}

//______________________________________________________________________________
Double_t THStack::GetMinimum(Option_t *option)
{
//  returns the minimum of all added histograms
//  returns the minimum of all histograms if option "nostack".

   TString opt = option;
   opt.ToLower();
   Double_t them=0, themin = 1e300;
   Int_t nhists = fHists->GetEntriesFast();
   TH1 *h;
   if (!opt.Contains("nostack")) {
       BuildStack();
       h = (TH1*)fStack->At(nhists-1);
       themin = h->GetMinimum();
   } else {
      for (Int_t i=0;i<nhists;i++) {
         h = (TH1*)fHists->At(i);
         them = h->GetMinimum();
         if (them < themin) themin = them;
      }
   }
   return themin;
}

//______________________________________________________________________________
TAxis *THStack::GetXaxis() const
{
   // Get x axis of the graph.

   if (!gPad) return 0;
   return GetHistogram()->GetXaxis();
}

//______________________________________________________________________________
TAxis *THStack::GetYaxis() const
{
   // Get y axis of the graph.

   if (!gPad) return 0;
   return GetHistogram()->GetYaxis();
}

//______________________________________________________________________________
void THStack::ls(Option_t *option) const
{
   // List histograms in the stack

   TROOT::IndentLevel();
   cout <<IsA()->GetName()
        <<" Name= "<<GetName()<<" Title= "<<GetTitle()<<" Option="<<option<<endl;
   TROOT::IncreaseDirLevel();
   if (fHists) fHists->ls(option);
   TROOT::DecreaseDirLevel();
}

//______________________________________________________________________________
void THStack::Modified()
{
// invalidate sum of histograms

   if (!fStack) return;
   fStack->Delete();
   delete fStack;
   fStack = 0;
}

//______________________________________________________________________________
void THStack::Paint(Option_t *option)
{
// paint the list of histograms
// By default, histograms are shown stacked.
//    -the first histogram is paint
//    -then the sum of the first and second, etc
// If option "nostack" is specified, histograms are all paint in the same pad
// as if the option "same" had been specified.
//
// See THistPainter::Paint for a list of valid options.

   TString opt = option;
   opt.ToLower();
   char loption[32];
   sprintf(loption,"%s",opt.Data());
   char *nostack = strstr(loption,"nostack");
   // do not delete the stack. Another pad may contain the same object
   // drawn in stack mode!
   //if (nostack && fStack) {fStack->Delete(); delete fStack; fStack = 0;}

   if (!opt.Contains("nostack")) BuildStack();

   Double_t themax,themin;
   if (fMaximum == -1111) themax = GetMaximum(option);
   else                   themax = fMaximum;
   if (fMinimum == -1111) {themin = GetMinimum(option); if (themin > 0) themin = 0;}
   else                   themin = fMinimum;
   if (!fHistogram) {
      Bool_t add = TH1::AddDirectoryStatus();
      TH1::AddDirectory(kFALSE);
      TH1 *h = (TH1*)fHists->At(0);
      TAxis *xaxis = h->GetXaxis();
      TAxis *yaxis = h->GetYaxis();
      if (h->GetDimension() > 1) {
         if (strlen(option) == 0) strcpy(loption,"lego1");
         fHistogram = new TH2F(GetName(),GetTitle(),
                               xaxis->GetNbins(),xaxis->GetXmin(),xaxis->GetXmax(),
                               yaxis->GetNbins(),yaxis->GetXmin(),yaxis->GetXmax());
      } else {
         fHistogram = new TH1F(GetName(),GetTitle(),xaxis->GetNbins(),xaxis->GetXmin(),xaxis->GetXmax());
      }
      fHistogram->SetStats(0);
      TH1::AddDirectory(add);
   }

   if (nostack) {*nostack = 0; strcat(nostack,nostack+7);}
   else fHistogram->GetPainter()->SetStack(fHists);

   fHistogram->SetMaximum(1.05*themax);
   fHistogram->SetMinimum(themin);
   fHistogram->Paint(loption);

   if (fHistogram->GetDimension() > 1) SetDrawOption(loption);
   if (strstr(loption,"lego")) return;

   Int_t nhists = fHists->GetEntriesFast();
   strcat(loption,"same");
   for (Int_t i=0;i<nhists;i++) {
      if (nostack) fHists->At(i)->Paint(loption);
      else         fStack->At(nhists-i-1)->Paint(loption);
   }
}

//______________________________________________________________________________
void THStack::Print(Option_t *option) const
{
// Print the list of histograms

   TH1 *h;
   if (fHists) {
     TIter   next(fHists);
     while ((h = (TH1*) next())) {
       h->Print(option);
     }
   }
}

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

   char quote = '"';
   out<<"   "<<endl;
   if (gROOT->ClassSaved(THStack::Class())) {
       out<<"   ";
   } else {
       out<<"   THStack *";
   }
   out<<"hstack = new THStack();"<<endl;
   out<<"   hstack->SetName("<<quote<<GetName()<<quote<<");"<<endl;
   out<<"   hstack->SetTitle("<<quote<<GetTitle()<<quote<<");"<<endl;

   TH1 *h;
   if (fHists) {
     TIter   next(fHists);
     while ((h = (TH1*) next())) {
       h->SavePrimitive(out,"nodraw");
       out<<"   hstack->Add("<<h->GetName()<<");"<<endl;
     }
   }
   out<<"   hstack->Draw("
      <<quote<<option<<quote<<");"<<endl;
}

//______________________________________________________________________________
void THStack::SetMaximum(Double_t maximum)
{
   fMaximum = maximum;
   if (fHistogram)  fHistogram->SetMaximum(maximum);
}

//______________________________________________________________________________
void THStack::SetMinimum(Double_t minimum)
{
   fMinimum = minimum;
   if (fHistogram) fHistogram->SetMinimum(minimum);
}
<|endoftext|>


<commit_before>/*
 * (C) 2015 Augustin Cavalier
 * All rights reserved. Distributed under the terms of the MIT license.
 */
#include "Stack.h"

#include <iostream>

#include "util/StringUtil.h"

using std::cout;
using std::string;
using std::vector;

namespace Language {

std::map<std::string, Function> GlobalFunctions;

Stack::Stack()
{
	push();
}

void Stack::push()
{
	fStack.push_back(ObjectMap());
}

void Stack::pop()
{
	fStack.pop_back();
}

std::vector<ObjectMap>::size_type Stack::getPos(std::string variable)
{
	std::vector<ObjectMap>::size_type size = fStack.size() - 1;
	for (std::vector<ObjectMap>::size_type i = 0; i <= size; i++) {
		if (fStack[size - i].get_ptr(variable) != nullptr)
			return i;
	}
	return fStack.size() - 1;
}

Object Stack::get(const vector<string> variable)
{
	Object* ret = nullptr;
	if (variable[0][0] == '$') {
		// it's a superglobal
		string var = variable[0];
		var = var.substr(1);
		ret = fSuperglobalScope.get_ptr(var);
	} else
		ret = fStack[getPos(variable[0])].get_ptr(variable[0]);
	for (vector<string>::size_type i = 1; i < variable.size(); i++) {
		std::string what = string("referenced variable '")
			.append(StringUtil::join(variable, ".")).append("'");
		CoerceOrThrowPtr(what, ret, Type::Map);
		ret = ret->map->get_ptr(variable[i]);
	}

	if (ret != nullptr)
		return *ret;
	return Object();
}

void Stack::set(vector<string> variable, Object value)
{
	if (variable[0][0] == '$') {
		// it's a superglobal
		throw Exception(Exception::AccessViolation, "superglobals are read-only");
	}
	vector<ObjectMap>::size_type loc = getPos(variable[0]);
	if (variable.size() == 1) {
		fStack[loc].set(variable[0], value);
		return;
	}

	Object* res = fStack[loc].get_ptr(variable[0]);
	CoerceOrThrowPtr("referenced variable", res, Type::Map);
	for (vector<string>::size_type i = 1; i < variable.size() - 1; i++) {
		res = res->map->get_ptr(variable[i]);
		CoerceOrThrowPtr("referenced variable", res, Type::Map);
	}
	res->map->set(variable[variable.size() - 1], value);
}

void Stack::addSuperglobal(string variableName, Object value)
{
	fSuperglobalScope.set(variableName, value);
}

void Stack::print()
{
	cout << "-- VM STACK DUMP --" << std::endl;
	int tabs = 1;
	for (const ObjectMap& m : fStack) {
		for (ObjectMap::const_iterator it = m.begin(); it != m.end(); it++) {
			for (int i = 0; i < tabs; i++)
				cout << "    ";
			cout << it->first << ": " << it->second->asString() << std::endl;
		}
		tabs++;
	}
}

}
<commit_msg>Stack: Properly print referenced variable error.<commit_after>/*
 * (C) 2015 Augustin Cavalier
 * All rights reserved. Distributed under the terms of the MIT license.
 */
#include "Stack.h"

#include <iostream>

#include "util/StringUtil.h"

using std::cout;
using std::string;
using std::vector;

namespace Language {

std::map<std::string, Function> GlobalFunctions;

Stack::Stack()
{
	push();
}

void Stack::push()
{
	fStack.push_back(ObjectMap());
}

void Stack::pop()
{
	fStack.pop_back();
}

std::vector<ObjectMap>::size_type Stack::getPos(std::string variable)
{
	std::vector<ObjectMap>::size_type size = fStack.size() - 1;
	for (std::vector<ObjectMap>::size_type i = 0; i <= size; i++) {
		if (fStack[size - i].get_ptr(variable) != nullptr)
			return i;
	}
	return fStack.size() - 1;
}

Object Stack::get(const vector<string> variable)
{
	Object* ret = nullptr;
	if (variable[0][0] == '$') {
		// it's a superglobal
		string var = variable[0];
		var = var.substr(1);
		ret = fSuperglobalScope.get_ptr(var);
	} else
		ret = fStack[getPos(variable[0])].get_ptr(variable[0]);
	for (vector<string>::size_type i = 1; i < variable.size(); i++) {
		CoerceOrThrowPtr(string("referenced variable '")
			.append(variable[i - 1]).append("'"), ret, Type::Map);
		ret = ret->map->get_ptr(variable[i]);
	}

	if (ret != nullptr)
		return *ret;
	return Object();
}

void Stack::set(vector<string> variable, Object value)
{
	if (variable[0][0] == '$') {
		// it's a superglobal
		throw Exception(Exception::AccessViolation, "superglobals are read-only");
	}
	vector<ObjectMap>::size_type loc = getPos(variable[0]);
	if (variable.size() == 1) {
		fStack[loc].set(variable[0], value);
		return;
	}

	Object* res = fStack[loc].get_ptr(variable[0]);
	CoerceOrThrowPtr("referenced variable", res, Type::Map);
	for (vector<string>::size_type i = 1; i < variable.size() - 1; i++) {
		res = res->map->get_ptr(variable[i]);
		CoerceOrThrowPtr("referenced variable", res, Type::Map);
	}
	res->map->set(variable[variable.size() - 1], value);
}

void Stack::addSuperglobal(string variableName, Object value)
{
	fSuperglobalScope.set(variableName, value);
}

void Stack::print()
{
	cout << "-- VM STACK DUMP --" << std::endl;
	int tabs = 1;
	for (const ObjectMap& m : fStack) {
		for (ObjectMap::const_iterator it = m.begin(); it != m.end(); it++) {
			for (int i = 0; i < tabs; i++)
				cout << "    ";
			cout << it->first << ": " << it->second->asString() << std::endl;
		}
		tabs++;
	}
}

}
<|endoftext|>


<commit_before>#include "TwitchIrcServer.hpp"

#include "Application.hpp"
#include "common/Common.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/ChatroomChannel.hpp"
#include "providers/twitch/IrcMessageHandler.hpp"
#include "providers/twitch/PubsubClient.hpp"
#include "providers/twitch/TwitchAccount.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchHelpers.hpp"
#include "providers/twitch/TwitchMessageBuilder.hpp"
#include "util/PostToThread.hpp"

#include <IrcCommand>
#include <cassert>

// using namespace Communi;
using namespace std::chrono_literals;

namespace chatterino {

namespace {
    bool isChatroom(const QString &channel)
    {
        if (channel.left(10) == "chatrooms:")
        {
            auto reflist = channel.splitRef(':');
            if (reflist.size() == 3)
            {
                return true;
            }
        }
        return false;
    }
}  // namespace

TwitchIrcServer::TwitchIrcServer()
    : whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers))
    , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions))
    , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
{
    this->pubsub = new PubSub;

    // getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) {
    // this->connect(); },
    //                                                     this->signalHolder_,
    //                                                     false);
}

void TwitchIrcServer::initialize(Settings &settings, Paths &paths)
{
    getApp()->accounts->twitch.currentUserChanged.connect(
        [this]() { postToThread([this] { this->connect(); }); });

    this->twitchBadges.loadTwitchBadges();
    this->bttv.loadEmotes();
    this->ffz.loadEmotes();
}

void TwitchIrcServer::initializeConnection(IrcConnection *connection,
                                           ConnectionType type)
{
    std::shared_ptr<TwitchAccount> account =
        getApp()->accounts->twitch.getCurrent();

    qDebug() << "logging in as" << account->getUserName();

    QString username = account->getUserName();
    QString oauthToken = account->getOAuthToken();

    if (!oauthToken.startsWith("oauth:"))
    {
        oauthToken.prepend("oauth:");
    }

    connection->setUserName(username);
    connection->setNickName(username);
    connection->setRealName(username);

    if (!account->isAnon())
    {
        connection->setPassword(oauthToken);
    }

    connection->setSecure(true);

    // https://dev.twitch.tv/docs/irc/guide/#connecting-to-twitch-irc
    // SSL disabled: irc://irc.chat.twitch.tv:6667
    // SSL enabled: irc://irc.chat.twitch.tv:6697
    connection->setHost("irc.chat.twitch.tv");
    connection->setPort(6697);

    this->open(type);
}

std::shared_ptr<Channel> TwitchIrcServer::createChannel(
    const QString &channelName)
{
    std::shared_ptr<TwitchChannel> channel;
    if (isChatroom(channelName))
    {
        channel = std::static_pointer_cast<TwitchChannel>(
            std::shared_ptr<ChatroomChannel>(new ChatroomChannel(
                channelName, this->twitchBadges, this->bttv, this->ffz)));
    }
    else
    {
        channel = std::shared_ptr<TwitchChannel>(new TwitchChannel(
            channelName, this->twitchBadges, this->bttv, this->ffz));
    }
    channel->initialize();

    channel->sendMessageSignal.connect(
        [this, channel = channel.get()](auto &chan, auto &msg, bool &sent) {
            this->onMessageSendRequested(channel, msg, sent);
        });

    return std::shared_ptr<Channel>(channel);
}

void TwitchIrcServer::privateMessageReceived(
    Communi::IrcPrivateMessage *message)
{
    IrcMessageHandler::getInstance().handlePrivMessage(message, *this);
}

void TwitchIrcServer::readConnectionMessageReceived(
    Communi::IrcMessage *message)
{
    AbstractIrcServer::readConnectionMessageReceived(message);

    if (message->type() == Communi::IrcMessage::Type::Private)
    {
        // We already have a handler for private messages
        return;
    }

    const QString &command = message->command();

    auto &handler = IrcMessageHandler::getInstance();

    // Below commands enabled through the twitch.tv/membership CAP REQ
    if (command == "MODE")
    {
        handler.handleModeMessage(message);
    }
    else if (command == "JOIN")
    {
        handler.handleJoinMessage(message);
    }
    else if (command == "PART")
    {
        handler.handlePartMessage(message);
    }
    else if (command == "USERSTATE")
    {
        // Received USERSTATE upon JOINing a channel
        handler.handleUserStateMessage(message);
    }
}

void TwitchIrcServer::writeConnectionMessageReceived(
    Communi::IrcMessage *message)
{
    const QString &command = message->command();

    auto &handler = IrcMessageHandler::getInstance();

    // Below commands enabled through the twitch.tv/commands CAP REQ
    if (command == "USERSTATE")
    {
        // Received USERSTATE upon PRIVMSGing
        handler.handleUserStateMessage(message);
    }
    else if (command == "WHISPER")
    {
        handler.handleWhisperMessage(message);
    }
    else if (command == "USERNOTICE")
    {
        handler.handleUserNoticeMessage(message, *this);
    }
    else if (command == "ROOMSTATE")
    {
        handler.handleRoomStateMessage(message);
    }
    else if (command == "CLEARCHAT")
    {
        handler.handleClearChatMessage(message);
    }
    else if (command == "CLEARMSG")
    {
        handler.handleClearMessageMessage(message);
    }
    else if (command == "NOTICE")
    {
        handler.handleNoticeMessage(
            static_cast<Communi::IrcNoticeMessage *>(message));
    }
}

void TwitchIrcServer::onReadConnected(IrcConnection *connection)
{
    // twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
    // twitch.tv/membership enables the JOIN/PART/MODE/NAMES commands. See https://dev.twitch.tv/docs/irc/membership/
    // twitch.tv/commands enables a bunch of miscellaneous command capabilities. See https://dev.twitch.tv/docs/irc/commands/
    //                    This is enabled here so we receive USERSTATE messages when joining channels
    connection->sendRaw(
        "CAP REQ :twitch.tv/tags twitch.tv/membership twitch.tv/commands");

    AbstractIrcServer::onReadConnected(connection);
}

void TwitchIrcServer::onWriteConnected(IrcConnection *connection)
{
    // twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
    // twitch.tv/commands enables a bunch of miscellaneous command capabilities. See https://dev.twitch.tv/docs/irc/commands/
    //                    This is enabled here so we receive USERSTATE messages when typing messages, along with the other command capabilities
    connection->sendRaw("CAP REQ :twitch.tv/tags twitch.tv/commands");

    AbstractIrcServer::onWriteConnected(connection);
}

std::shared_ptr<Channel> TwitchIrcServer::getCustomChannel(
    const QString &channelName)
{
    if (channelName == "/whispers")
    {
        return this->whispersChannel;
    }

    if (channelName == "/mentions")
    {
        return this->mentionsChannel;
    }

    if (channelName == "$$$")
    {
        static auto channel =
            std::make_shared<Channel>("$$$", chatterino::Channel::Type::Misc);
        static auto getTimer = [&] {
            for (auto i = 0; i < 1000; i++)
            {
                channel->addMessage(makeSystemMessage(QString::number(i + 1)));
            }

            auto timer = new QTimer;
            QObject::connect(timer, &QTimer::timeout, [] {
                channel->addMessage(
                    makeSystemMessage(QTime::currentTime().toString()));
            });
            timer->start(500);
            return timer;
        }();

        return channel;
    }

    return nullptr;
}

void TwitchIrcServer::forEachChannelAndSpecialChannels(
    std::function<void(ChannelPtr)> func)
{
    this->forEachChannel(func);

    func(this->whispersChannel);
    func(this->mentionsChannel);
}

std::shared_ptr<Channel> TwitchIrcServer::getChannelOrEmptyByID(
    const QString &channelId)
{
    std::lock_guard<std::mutex> lock(this->channelMutex);

    for (const auto &weakChannel : this->channels)
    {
        auto channel = weakChannel.lock();
        if (!channel)
            continue;

        auto twitchChannel = std::dynamic_pointer_cast<TwitchChannel>(channel);
        if (!twitchChannel)
            continue;

        if (twitchChannel->roomId() == channelId &&
            twitchChannel->getName().splitRef(":").size() < 3)
        {
            return twitchChannel;
        }
    }

    return Channel::getEmpty();
}

QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName)
{
    if (dirtyChannelName.startsWith('#'))
        return dirtyChannelName.mid(1).toLower();
    else
        return dirtyChannelName.toLower();
}

bool TwitchIrcServer::hasSeparateWriteConnection() const
{
    return true;
    // return getSettings()->twitchSeperateWriteConnection;
}

void TwitchIrcServer::onMessageSendRequested(TwitchChannel *channel,
                                             const QString &message, bool &sent)
{
    sent = false;

    {
        std::lock_guard<std::mutex> guard(this->lastMessageMutex_);

        //        std::queue<std::chrono::steady_clock::time_point>
        auto &lastMessage = channel->hasHighRateLimit()
                                ? this->lastMessageMod_
                                : this->lastMessagePleb_;
        size_t maxMessageCount = channel->hasHighRateLimit() ? 99 : 19;
        auto minMessageOffset = (channel->hasHighRateLimit() ? 100ms : 1100ms);

        auto now = std::chrono::steady_clock::now();

        // check if you are sending messages too fast
        if (!lastMessage.empty() && lastMessage.back() + minMessageOffset > now)
        {
            if (this->lastErrorTimeSpeed_ + 30s < now)
            {
                auto errorMessage =
                    makeSystemMessage("sending messages too fast");

                channel->addMessage(errorMessage);

                this->lastErrorTimeSpeed_ = now;
            }
            return;
        }

        // remove messages older than 30 seconds
        while (!lastMessage.empty() && lastMessage.front() + 32s < now)
        {
            lastMessage.pop();
        }

        // check if you are sending too many messages
        if (lastMessage.size() >= maxMessageCount)
        {
            if (this->lastErrorTimeAmount_ + 30s < now)
            {
                auto errorMessage =
                    makeSystemMessage("sending too many messages");

                channel->addMessage(errorMessage);

                this->lastErrorTimeAmount_ = now;
            }
            return;
        }

        lastMessage.push(now);
    }

    this->sendMessage(channel->getName(), message);
    sent = true;
}

const BttvEmotes &TwitchIrcServer::getBttvEmotes() const
{
    return this->bttv;
}
const FfzEmotes &TwitchIrcServer::getFfzEmotes() const
{
    return this->ffz;
}

}  // namespace chatterino
<commit_msg>handle ROOMSTATE in read connection too<commit_after>#include "TwitchIrcServer.hpp"

#include "Application.hpp"
#include "common/Common.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/ChatroomChannel.hpp"
#include "providers/twitch/IrcMessageHandler.hpp"
#include "providers/twitch/PubsubClient.hpp"
#include "providers/twitch/TwitchAccount.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "providers/twitch/TwitchHelpers.hpp"
#include "providers/twitch/TwitchMessageBuilder.hpp"
#include "util/PostToThread.hpp"

#include <IrcCommand>
#include <cassert>

// using namespace Communi;
using namespace std::chrono_literals;

namespace chatterino {

namespace {
    bool isChatroom(const QString &channel)
    {
        if (channel.left(10) == "chatrooms:")
        {
            auto reflist = channel.splitRef(':');
            if (reflist.size() == 3)
            {
                return true;
            }
        }
        return false;
    }
}  // namespace

TwitchIrcServer::TwitchIrcServer()
    : whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers))
    , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions))
    , watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
{
    this->pubsub = new PubSub;

    // getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) {
    // this->connect(); },
    //                                                     this->signalHolder_,
    //                                                     false);
}

void TwitchIrcServer::initialize(Settings &settings, Paths &paths)
{
    getApp()->accounts->twitch.currentUserChanged.connect(
        [this]() { postToThread([this] { this->connect(); }); });

    this->twitchBadges.loadTwitchBadges();
    this->bttv.loadEmotes();
    this->ffz.loadEmotes();
}

void TwitchIrcServer::initializeConnection(IrcConnection *connection,
                                           ConnectionType type)
{
    std::shared_ptr<TwitchAccount> account =
        getApp()->accounts->twitch.getCurrent();

    qDebug() << "logging in as" << account->getUserName();

    QString username = account->getUserName();
    QString oauthToken = account->getOAuthToken();

    if (!oauthToken.startsWith("oauth:"))
    {
        oauthToken.prepend("oauth:");
    }

    connection->setUserName(username);
    connection->setNickName(username);
    connection->setRealName(username);

    if (!account->isAnon())
    {
        connection->setPassword(oauthToken);
    }

    connection->setSecure(true);

    // https://dev.twitch.tv/docs/irc/guide/#connecting-to-twitch-irc
    // SSL disabled: irc://irc.chat.twitch.tv:6667
    // SSL enabled: irc://irc.chat.twitch.tv:6697
    connection->setHost("irc.chat.twitch.tv");
    connection->setPort(6697);

    this->open(type);
}

std::shared_ptr<Channel> TwitchIrcServer::createChannel(
    const QString &channelName)
{
    std::shared_ptr<TwitchChannel> channel;
    if (isChatroom(channelName))
    {
        channel = std::static_pointer_cast<TwitchChannel>(
            std::shared_ptr<ChatroomChannel>(new ChatroomChannel(
                channelName, this->twitchBadges, this->bttv, this->ffz)));
    }
    else
    {
        channel = std::shared_ptr<TwitchChannel>(new TwitchChannel(
            channelName, this->twitchBadges, this->bttv, this->ffz));
    }
    channel->initialize();

    channel->sendMessageSignal.connect(
        [this, channel = channel.get()](auto &chan, auto &msg, bool &sent) {
            this->onMessageSendRequested(channel, msg, sent);
        });

    return std::shared_ptr<Channel>(channel);
}

void TwitchIrcServer::privateMessageReceived(
    Communi::IrcPrivateMessage *message)
{
    IrcMessageHandler::getInstance().handlePrivMessage(message, *this);
}

void TwitchIrcServer::readConnectionMessageReceived(
    Communi::IrcMessage *message)
{
    AbstractIrcServer::readConnectionMessageReceived(message);

    if (message->type() == Communi::IrcMessage::Type::Private)
    {
        // We already have a handler for private messages
        return;
    }

    const QString &command = message->command();

    auto &handler = IrcMessageHandler::getInstance();

    // Below commands enabled through the twitch.tv/membership CAP REQ
    if (command == "MODE")
    {
        handler.handleModeMessage(message);
    }
    else if (command == "JOIN")
    {
        handler.handleJoinMessage(message);
    }
    else if (command == "PART")
    {
        handler.handlePartMessage(message);
    }
    else if (command == "USERSTATE")
    {
        // Received USERSTATE upon JOINing a channel
        handler.handleUserStateMessage(message);
    }
    else if (command == "ROOMSTATE")
    {
        // Received ROOMSTATE upon JOINing a channel
        handler.handleRoomStateMessage(message);
    }
}

void TwitchIrcServer::writeConnectionMessageReceived(
    Communi::IrcMessage *message)
{
    const QString &command = message->command();

    auto &handler = IrcMessageHandler::getInstance();

    // Below commands enabled through the twitch.tv/commands CAP REQ
    if (command == "USERSTATE")
    {
        // Received USERSTATE upon PRIVMSGing
        handler.handleUserStateMessage(message);
    }
    else if (command == "WHISPER")
    {
        handler.handleWhisperMessage(message);
    }
    else if (command == "USERNOTICE")
    {
        handler.handleUserNoticeMessage(message, *this);
    }
    else if (command == "ROOMSTATE")
    {
        handler.handleRoomStateMessage(message);
    }
    else if (command == "CLEARCHAT")
    {
        handler.handleClearChatMessage(message);
    }
    else if (command == "CLEARMSG")
    {
        handler.handleClearMessageMessage(message);
    }
    else if (command == "NOTICE")
    {
        handler.handleNoticeMessage(
            static_cast<Communi::IrcNoticeMessage *>(message));
    }
}

void TwitchIrcServer::onReadConnected(IrcConnection *connection)
{
    // twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
    // twitch.tv/membership enables the JOIN/PART/MODE/NAMES commands. See https://dev.twitch.tv/docs/irc/membership/
    // twitch.tv/commands enables a bunch of miscellaneous command capabilities. See https://dev.twitch.tv/docs/irc/commands/
    //                    This is enabled here so we receive USERSTATE messages when joining channels
    connection->sendRaw(
        "CAP REQ :twitch.tv/tags twitch.tv/membership twitch.tv/commands");

    AbstractIrcServer::onReadConnected(connection);
}

void TwitchIrcServer::onWriteConnected(IrcConnection *connection)
{
    // twitch.tv/tags enables IRCv3 tags on messages. See https://dev.twitch.tv/docs/irc/tags/
    // twitch.tv/commands enables a bunch of miscellaneous command capabilities. See https://dev.twitch.tv/docs/irc/commands/
    //                    This is enabled here so we receive USERSTATE messages when typing messages, along with the other command capabilities
    connection->sendRaw("CAP REQ :twitch.tv/tags twitch.tv/commands");

    AbstractIrcServer::onWriteConnected(connection);
}

std::shared_ptr<Channel> TwitchIrcServer::getCustomChannel(
    const QString &channelName)
{
    if (channelName == "/whispers")
    {
        return this->whispersChannel;
    }

    if (channelName == "/mentions")
    {
        return this->mentionsChannel;
    }

    if (channelName == "$$$")
    {
        static auto channel =
            std::make_shared<Channel>("$$$", chatterino::Channel::Type::Misc);
        static auto getTimer = [&] {
            for (auto i = 0; i < 1000; i++)
            {
                channel->addMessage(makeSystemMessage(QString::number(i + 1)));
            }

            auto timer = new QTimer;
            QObject::connect(timer, &QTimer::timeout, [] {
                channel->addMessage(
                    makeSystemMessage(QTime::currentTime().toString()));
            });
            timer->start(500);
            return timer;
        }();

        return channel;
    }

    return nullptr;
}

void TwitchIrcServer::forEachChannelAndSpecialChannels(
    std::function<void(ChannelPtr)> func)
{
    this->forEachChannel(func);

    func(this->whispersChannel);
    func(this->mentionsChannel);
}

std::shared_ptr<Channel> TwitchIrcServer::getChannelOrEmptyByID(
    const QString &channelId)
{
    std::lock_guard<std::mutex> lock(this->channelMutex);

    for (const auto &weakChannel : this->channels)
    {
        auto channel = weakChannel.lock();
        if (!channel)
            continue;

        auto twitchChannel = std::dynamic_pointer_cast<TwitchChannel>(channel);
        if (!twitchChannel)
            continue;

        if (twitchChannel->roomId() == channelId &&
            twitchChannel->getName().splitRef(":").size() < 3)
        {
            return twitchChannel;
        }
    }

    return Channel::getEmpty();
}

QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName)
{
    if (dirtyChannelName.startsWith('#'))
        return dirtyChannelName.mid(1).toLower();
    else
        return dirtyChannelName.toLower();
}

bool TwitchIrcServer::hasSeparateWriteConnection() const
{
    return true;
    // return getSettings()->twitchSeperateWriteConnection;
}

void TwitchIrcServer::onMessageSendRequested(TwitchChannel *channel,
                                             const QString &message, bool &sent)
{
    sent = false;

    {
        std::lock_guard<std::mutex> guard(this->lastMessageMutex_);

        //        std::queue<std::chrono::steady_clock::time_point>
        auto &lastMessage = channel->hasHighRateLimit()
                                ? this->lastMessageMod_
                                : this->lastMessagePleb_;
        size_t maxMessageCount = channel->hasHighRateLimit() ? 99 : 19;
        auto minMessageOffset = (channel->hasHighRateLimit() ? 100ms : 1100ms);

        auto now = std::chrono::steady_clock::now();

        // check if you are sending messages too fast
        if (!lastMessage.empty() && lastMessage.back() + minMessageOffset > now)
        {
            if (this->lastErrorTimeSpeed_ + 30s < now)
            {
                auto errorMessage =
                    makeSystemMessage("sending messages too fast");

                channel->addMessage(errorMessage);

                this->lastErrorTimeSpeed_ = now;
            }
            return;
        }

        // remove messages older than 30 seconds
        while (!lastMessage.empty() && lastMessage.front() + 32s < now)
        {
            lastMessage.pop();
        }

        // check if you are sending too many messages
        if (lastMessage.size() >= maxMessageCount)
        {
            if (this->lastErrorTimeAmount_ + 30s < now)
            {
                auto errorMessage =
                    makeSystemMessage("sending too many messages");

                channel->addMessage(errorMessage);

                this->lastErrorTimeAmount_ = now;
            }
            return;
        }

        lastMessage.push(now);
    }

    this->sendMessage(channel->getName(), message);
    sent = true;
}

const BttvEmotes &TwitchIrcServer::getBttvEmotes() const
{
    return this->bttv;
}
const FfzEmotes &TwitchIrcServer::getFfzEmotes() const
{
    return this->ffz;
}

}  // namespace chatterino
<|endoftext|>