<commit_before>/*
 * Copyright © 2010 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 ir_function_inlining.cpp
 *
 * Replaces calls to functions with the body of the function.
 */

#include <inttypes.h>
#include "ir.h"
#include "ir_visitor.h"
#include "ir_function_inlining.h"
#include "ir_expression_flattening.h"
#include "glsl_types.h"
#include "program/hash_table.h"

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref);

class ir_function_inlining_visitor : public ir_hierarchical_visitor {
public:
   ir_function_inlining_visitor()
   {
      progress = false;
   }

   virtual ~ir_function_inlining_visitor()
   {
      /* empty */
   }

   virtual ir_visitor_status visit_enter(ir_expression *);
   virtual ir_visitor_status visit_enter(ir_call *);
   virtual ir_visitor_status visit_enter(ir_assignment *);
   virtual ir_visitor_status visit_enter(ir_return *);
   virtual ir_visitor_status visit_enter(ir_texture *);
   virtual ir_visitor_status visit_enter(ir_swizzle *);

   bool progress;
};

bool
automatic_inlining_predicate(ir_instruction *ir)
{
   ir_call *call = ir->as_call();

   if (call && can_inline(call))
      return true;

   return false;
}

bool
do_function_inlining(exec_list *instructions)
{
   ir_function_inlining_visitor v;

   do_expression_flattening(instructions, automatic_inlining_predicate);

   v.run(instructions);

   return v.progress;
}

static void
replace_return_with_assignment(ir_instruction *ir, void *data)
{
   void *ctx = talloc_parent(ir);
   ir_variable *retval = (ir_variable *)data;
   ir_return *ret = ir->as_return();

   if (ret) {
      if (ret->value) {
	 ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
	 ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
      } else {
	 /* un-valued return has to be the last return, or we shouldn't
	  * have reached here. (see can_inline()).
	  */
	 assert(ret->next->is_tail_sentinel());
	 ret->remove();
      }
   }
}

ir_rvalue *
ir_call::generate_inline(ir_instruction *next_ir)
{
   void *ctx = talloc_parent(this);
   ir_variable **parameters;
   int num_parameters;
   int i;
   ir_variable *retval = NULL;
   struct hash_table *ht;

   ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);

   num_parameters = 0;
   foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
      num_parameters++;

   parameters = new ir_variable *[num_parameters];

   /* Generate storage for the return value. */
   if (this->callee->return_type) {
      retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val",
				    ir_var_auto);
      next_ir->insert_before(retval);
   }

   /* Generate the declarations for the parameters to our inlined code,
    * and set up the mapping of real function body variables to ours.
    */
   i = 0;
   exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
   exec_list_iterator param_iter = this->actual_parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
      ir_rvalue *param = (ir_rvalue *) param_iter.get();

      /* Generate a new variable for the parameter. */
      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 /* For samplers, we want the inlined sampler references
	  * referencing the passed in sampler variable, since that
	  * will have the location information, which an assignment of
	  * a sampler wouldn't.  Fix it up below.
	  */
	 parameters[i] = NULL;
      } else {
	 parameters[i] = sig_param->clone(ctx, ht);
	 parameters[i]->mode = ir_var_auto;
	 next_ir->insert_before(parameters[i]);
      }

      /* Move the actual param into our param variable if it's an 'in' type. */
      if (parameters[i] && (sig_param->mode == ir_var_in ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
					 param, NULL);
	 next_ir->insert_before(assign);
      }

      sig_param_iter.next();
      param_iter.next();
   }

   exec_list new_instructions;

   /* Generate the inlined body of the function to a new list */
   foreach_iter(exec_list_iterator, iter, callee->body) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      ir_instruction *new_ir = ir->clone(ctx, ht);

      new_instructions.push_tail(new_ir);
      visit_tree(new_ir, replace_return_with_assignment, retval);
   }

   /* If any samplers were passed in, replace any deref of the sampler
    * with a deref of the sampler argument.
    */
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();

      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 ir_dereference *deref = param->as_dereference();

	 assert(deref);
	 do_sampler_replacement(&new_instructions, sig_param, deref);
      }
      param_iter.next();
      sig_param_iter.next();
   }

   /* Now push those new instructions in. */
   foreach_iter(exec_list_iterator, iter, new_instructions) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      next_ir->insert_before(ir);
   }

   /* Copy back the value of any 'out' parameters from the function body
    * variables to our own.
    */
   i = 0;
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();

      /* Move our param variable into the actual param if it's an 'out' type. */
      if (parameters[i] && (sig_param->mode == ir_var_out ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
					 new(ctx) ir_dereference_variable(parameters[i]),
					 NULL);
	 next_ir->insert_before(assign);
      }

      param_iter.next();
      sig_param_iter.next();
   }

   delete [] parameters;

   hash_table_dtor(ht);

   if (retval)
      return new(ctx) ir_dereference_variable(retval);
   else
      return NULL;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_expression *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_return *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_texture *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_call *ir)
{
   if (can_inline(ir)) {
      /* If the call was part of some tree, then it should have been
       * flattened out or we shouldn't have seen it because of a
       * visit_continue_with_parent in this visitor.
       */
      assert(ir == base_ir);

      (void) ir->generate_inline(ir);
      ir->remove();
      this->progress = true;
   }

   return visit_continue;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
{
   ir_call *call = ir->rhs->as_call();
   if (!call || !can_inline(call))
      return visit_continue;

   /* generates the parameter setup, function body, and returns the return
    * value of the function
    */
   ir_rvalue *rhs = call->generate_inline(ir);
   assert(rhs);

   ir->rhs = rhs;
   this->progress = true;

   return visit_continue;
}

/**
 * Replaces references to the "sampler" variable with a clone of "deref."
 *
 * From the spec, samplers can appear in the tree as function
 * (non-out) parameters and as the result of array indexing and
 * structure field selection.  In our builtin implementation, they
 * also appear in the sampler field of an ir_tex instruction.
 */

class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
public:
   ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
   {
      this->sampler = sampler;
      this->deref = deref;
   }

   virtual ~ir_sampler_replacement_visitor()
   {
   }

   virtual ir_visitor_status visit_leave(ir_call *);
   virtual ir_visitor_status visit_leave(ir_dereference_array *);
   virtual ir_visitor_status visit_leave(ir_dereference_record *);
   virtual ir_visitor_status visit_leave(ir_texture *);

   void replace_deref(ir_dereference **deref);
   void replace_rvalue(ir_rvalue **rvalue);

   ir_variable *sampler;
   ir_dereference *deref;
};

void
ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
{
   ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
   if (deref_var && deref_var->var == this->sampler) {
      *deref = this->deref->clone(talloc_parent(*deref), NULL);
   }
}

void
ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
{
   if (!*rvalue)
      return;

   ir_dereference *deref = (*rvalue)->as_dereference();

   if (!deref)
      return;

   replace_deref(&deref);
   *rvalue = deref;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
{
   replace_deref(&ir->sampler);

   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
{
   replace_rvalue(&ir->array);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
{
   replace_rvalue(&ir->record);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
{
   foreach_iter(exec_list_iterator, iter, *ir) {
      ir_rvalue *param = (ir_rvalue *)iter.get();
      ir_rvalue *new_param = param;
      replace_rvalue(&new_param);

      if (new_param != param) {
	 param->replace_with(new_param);
      }
   }
   return visit_continue;
}

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref)
{
   ir_sampler_replacement_visitor v(sampler, deref);

   visit_list_elements(&v, instructions);
}
<commit_msg>glsl: Remove const decoration from inlined function parameters<commit_after>/*
 * Copyright © 2010 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 ir_function_inlining.cpp
 *
 * Replaces calls to functions with the body of the function.
 */

#include <inttypes.h>
#include "ir.h"
#include "ir_visitor.h"
#include "ir_function_inlining.h"
#include "ir_expression_flattening.h"
#include "glsl_types.h"
#include "program/hash_table.h"

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref);

class ir_function_inlining_visitor : public ir_hierarchical_visitor {
public:
   ir_function_inlining_visitor()
   {
      progress = false;
   }

   virtual ~ir_function_inlining_visitor()
   {
      /* empty */
   }

   virtual ir_visitor_status visit_enter(ir_expression *);
   virtual ir_visitor_status visit_enter(ir_call *);
   virtual ir_visitor_status visit_enter(ir_assignment *);
   virtual ir_visitor_status visit_enter(ir_return *);
   virtual ir_visitor_status visit_enter(ir_texture *);
   virtual ir_visitor_status visit_enter(ir_swizzle *);

   bool progress;
};

bool
automatic_inlining_predicate(ir_instruction *ir)
{
   ir_call *call = ir->as_call();

   if (call && can_inline(call))
      return true;

   return false;
}

bool
do_function_inlining(exec_list *instructions)
{
   ir_function_inlining_visitor v;

   do_expression_flattening(instructions, automatic_inlining_predicate);

   v.run(instructions);

   return v.progress;
}

static void
replace_return_with_assignment(ir_instruction *ir, void *data)
{
   void *ctx = talloc_parent(ir);
   ir_variable *retval = (ir_variable *)data;
   ir_return *ret = ir->as_return();

   if (ret) {
      if (ret->value) {
	 ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
	 ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
      } else {
	 /* un-valued return has to be the last return, or we shouldn't
	  * have reached here. (see can_inline()).
	  */
	 assert(ret->next->is_tail_sentinel());
	 ret->remove();
      }
   }
}

ir_rvalue *
ir_call::generate_inline(ir_instruction *next_ir)
{
   void *ctx = talloc_parent(this);
   ir_variable **parameters;
   int num_parameters;
   int i;
   ir_variable *retval = NULL;
   struct hash_table *ht;

   ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);

   num_parameters = 0;
   foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
      num_parameters++;

   parameters = new ir_variable *[num_parameters];

   /* Generate storage for the return value. */
   if (this->callee->return_type) {
      retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val",
				    ir_var_auto);
      next_ir->insert_before(retval);
   }

   /* Generate the declarations for the parameters to our inlined code,
    * and set up the mapping of real function body variables to ours.
    */
   i = 0;
   exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
   exec_list_iterator param_iter = this->actual_parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
      ir_rvalue *param = (ir_rvalue *) param_iter.get();

      /* Generate a new variable for the parameter. */
      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 /* For samplers, we want the inlined sampler references
	  * referencing the passed in sampler variable, since that
	  * will have the location information, which an assignment of
	  * a sampler wouldn't.  Fix it up below.
	  */
	 parameters[i] = NULL;
      } else {
	 parameters[i] = sig_param->clone(ctx, ht);
	 parameters[i]->mode = ir_var_auto;

	 /* Remove the read-only decoration becuase we're going to write
	  * directly to this variable.  If the cloned variable is left
	  * read-only and the inlined function is inside a loop, the loop
	  * analysis code will get confused.
	  */
	 parameters[i]->read_only = false;
	 next_ir->insert_before(parameters[i]);
      }

      /* Move the actual param into our param variable if it's an 'in' type. */
      if (parameters[i] && (sig_param->mode == ir_var_in ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
					 param, NULL);
	 next_ir->insert_before(assign);
      }

      sig_param_iter.next();
      param_iter.next();
   }

   exec_list new_instructions;

   /* Generate the inlined body of the function to a new list */
   foreach_iter(exec_list_iterator, iter, callee->body) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      ir_instruction *new_ir = ir->clone(ctx, ht);

      new_instructions.push_tail(new_ir);
      visit_tree(new_ir, replace_return_with_assignment, retval);
   }

   /* If any samplers were passed in, replace any deref of the sampler
    * with a deref of the sampler argument.
    */
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      ir_variable *sig_param = (ir_variable *) sig_param_iter.get();

      if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
	 ir_dereference *deref = param->as_dereference();

	 assert(deref);
	 do_sampler_replacement(&new_instructions, sig_param, deref);
      }
      param_iter.next();
      sig_param_iter.next();
   }

   /* Now push those new instructions in. */
   foreach_iter(exec_list_iterator, iter, new_instructions) {
      ir_instruction *ir = (ir_instruction *)iter.get();
      next_ir->insert_before(ir);
   }

   /* Copy back the value of any 'out' parameters from the function body
    * variables to our own.
    */
   i = 0;
   param_iter = this->actual_parameters.iterator();
   sig_param_iter = this->callee->parameters.iterator();
   for (i = 0; i < num_parameters; i++) {
      ir_instruction *const param = (ir_instruction *) param_iter.get();
      const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();

      /* Move our param variable into the actual param if it's an 'out' type. */
      if (parameters[i] && (sig_param->mode == ir_var_out ||
			    sig_param->mode == ir_var_inout)) {
	 ir_assignment *assign;

	 assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
					 new(ctx) ir_dereference_variable(parameters[i]),
					 NULL);
	 next_ir->insert_before(assign);
      }

      param_iter.next();
      sig_param_iter.next();
   }

   delete [] parameters;

   hash_table_dtor(ht);

   if (retval)
      return new(ctx) ir_dereference_variable(retval);
   else
      return NULL;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_expression *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_return *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_texture *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
{
   (void) ir;
   return visit_continue_with_parent;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_call *ir)
{
   if (can_inline(ir)) {
      /* If the call was part of some tree, then it should have been
       * flattened out or we shouldn't have seen it because of a
       * visit_continue_with_parent in this visitor.
       */
      assert(ir == base_ir);

      (void) ir->generate_inline(ir);
      ir->remove();
      this->progress = true;
   }

   return visit_continue;
}

ir_visitor_status
ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
{
   ir_call *call = ir->rhs->as_call();
   if (!call || !can_inline(call))
      return visit_continue;

   /* generates the parameter setup, function body, and returns the return
    * value of the function
    */
   ir_rvalue *rhs = call->generate_inline(ir);
   assert(rhs);

   ir->rhs = rhs;
   this->progress = true;

   return visit_continue;
}

/**
 * Replaces references to the "sampler" variable with a clone of "deref."
 *
 * From the spec, samplers can appear in the tree as function
 * (non-out) parameters and as the result of array indexing and
 * structure field selection.  In our builtin implementation, they
 * also appear in the sampler field of an ir_tex instruction.
 */

class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
public:
   ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
   {
      this->sampler = sampler;
      this->deref = deref;
   }

   virtual ~ir_sampler_replacement_visitor()
   {
   }

   virtual ir_visitor_status visit_leave(ir_call *);
   virtual ir_visitor_status visit_leave(ir_dereference_array *);
   virtual ir_visitor_status visit_leave(ir_dereference_record *);
   virtual ir_visitor_status visit_leave(ir_texture *);

   void replace_deref(ir_dereference **deref);
   void replace_rvalue(ir_rvalue **rvalue);

   ir_variable *sampler;
   ir_dereference *deref;
};

void
ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
{
   ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
   if (deref_var && deref_var->var == this->sampler) {
      *deref = this->deref->clone(talloc_parent(*deref), NULL);
   }
}

void
ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
{
   if (!*rvalue)
      return;

   ir_dereference *deref = (*rvalue)->as_dereference();

   if (!deref)
      return;

   replace_deref(&deref);
   *rvalue = deref;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
{
   replace_deref(&ir->sampler);

   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
{
   replace_rvalue(&ir->array);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
{
   replace_rvalue(&ir->record);
   return visit_continue;
}

ir_visitor_status
ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
{
   foreach_iter(exec_list_iterator, iter, *ir) {
      ir_rvalue *param = (ir_rvalue *)iter.get();
      ir_rvalue *new_param = param;
      replace_rvalue(&new_param);

      if (new_param != param) {
	 param->replace_with(new_param);
      }
   }
   return visit_continue;
}

static void
do_sampler_replacement(exec_list *instructions,
		       ir_variable *sampler,
		       ir_dereference *deref)
{
   ir_sampler_replacement_visitor v(sampler, deref);

   visit_list_elements(&v, instructions);
}
<|endoftext|>


<commit_before>/*
    Copyright 2015 Ahnaf Siddiqui

    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 "D_SDLRenderObj2D.h"

#include "D_SDLTexture.h"

Diamond::SDLRenderObj2D::SDLRenderObj2D(Texture *texture,
                                        transform2_id transform,
                                        float scale)
    : RenderObj2D(transform), flip(SDL_FLIP_NONE), clip(nullptr) {
	setTexture(texture, scale);
}

Diamond::SDLRenderObj2D::~SDLRenderObj2D() {
	if (clip)	delete clip;
}

Diamond::SDLRenderObj2D::SDLRenderObj2D(const SDLRenderObj2D &other)
    : RenderObj2D(other), texture(other.texture), flip(other.flip), clip(nullptr) {
    if (other.clip) {
        clip = new SDL_Rect;
        *clip = *(other.clip);
    }

    size = other.size; // put this after clip copy for exception safety
}

Diamond::SDLRenderObj2D::SDLRenderObj2D(SDLRenderObj2D &&other)
    : RenderObj2D(other), texture(other.texture), flip(other.flip), size(other.size) {
    clip = other.clip;

    other.texture = nullptr;
    other.clip = nullptr;
}

Diamond::SDLRenderObj2D &Diamond::SDLRenderObj2D::operator=(const SDLRenderObj2D &other) {
    RenderObj2D::operator=(other);
    if (this != &other) {
        texture = other.texture;
        flip = other.flip;
        size = other.size;

        if (other.clip) {
            if (!clip)   clip = new SDL_Rect;
            *clip = *(other.clip);
        }
        else
            clip = nullptr;
    }
    return *this;
}

Diamond::SDLRenderObj2D &Diamond::SDLRenderObj2D::operator=(SDLRenderObj2D &&other) {
    RenderObj2D::operator=(other);
    if (this != &other) {
        texture = other.texture;
        flip = other.flip;
        size = other.size;

        other.texture = nullptr;
        other.clip = nullptr;
    }
    return *this;
}

void Diamond::SDLRenderObj2D::setTexture(Texture *texture, float scale) {
	this->texture = dynamic_cast<SDLTexture*>(texture);
	applyScale(scale);
}

void Diamond::SDLRenderObj2D::applyScale(float scale) {
	if (clip) {
		size.x = clip->w * scale;
		size.y = clip->h * scale;
	}
	else {
		size.x = texture->width * scale;
		size.y = texture->height * scale;
	}
}

void Diamond::SDLRenderObj2D::flipX() {
	flip = (SDL_RendererFlip)(flip ^ SDL_FLIP_HORIZONTAL);
}

void Diamond::SDLRenderObj2D::flipY() {
	flip = (SDL_RendererFlip)(flip ^ SDL_FLIP_VERTICAL);
}

int Diamond::SDLRenderObj2D::isFlippedX() const {
	return flip & SDL_FLIP_HORIZONTAL;
}

int Diamond::SDLRenderObj2D::isFlippedY() const {
	return flip & SDL_FLIP_VERTICAL;
}

void Diamond::SDLRenderObj2D::initClip() {
	if (!clip)	clip = new SDL_Rect;
	clip->x = 0;
	clip->y = 0;
	clip->w = texture->width;
	clip->h = texture->height;
}

void Diamond::SDLRenderObj2D::setClip(int x, int y, int w, int h) {
    clip->x = x;
    clip->y = y;

    size.x *= (float)w / clip->w;
    size.y *= (float)h / clip->h;

    clip->w = w;
    clip->h = h;
}

void Diamond::SDLRenderObj2D::setClip(int x, int y) {
    clip->x = x;
    clip->y = y;
}

bool Diamond::SDLRenderObj2D::getClipDim(Vector2<int> &dim) const {
	if (!clip)	return false;
	dim.x = clip->w;
	dim.y = clip->h;
	return true;
}
<commit_msg>Fixed sdlrenderobjs rvalue assign op<commit_after>/*
    Copyright 2015 Ahnaf Siddiqui

    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 "D_SDLRenderObj2D.h"

#include "D_SDLTexture.h"

Diamond::SDLRenderObj2D::SDLRenderObj2D(Texture *texture,
                                        transform2_id transform,
                                        float scale)
    : RenderObj2D(transform), flip(SDL_FLIP_NONE), clip(nullptr) {
	setTexture(texture, scale);
}

Diamond::SDLRenderObj2D::~SDLRenderObj2D() {
	if (clip)	delete clip;
}

Diamond::SDLRenderObj2D::SDLRenderObj2D(const SDLRenderObj2D &other)
    : RenderObj2D(other), texture(other.texture), flip(other.flip), clip(nullptr) {
    if (other.clip) {
        clip = new SDL_Rect;
        *clip = *(other.clip);
    }

    size = other.size; // put this after clip copy for exception safety
}

Diamond::SDLRenderObj2D::SDLRenderObj2D(SDLRenderObj2D &&other)
    : RenderObj2D(other), texture(other.texture), flip(other.flip), size(other.size) {
    clip = other.clip;

    other.texture = nullptr;
    other.clip = nullptr;
}

Diamond::SDLRenderObj2D &Diamond::SDLRenderObj2D::operator=(const SDLRenderObj2D &other) {
    RenderObj2D::operator=(other);
    if (this != &other) {
        texture = other.texture;
        flip = other.flip;
        size = other.size;

        if (other.clip) {
            if (!clip)   clip = new SDL_Rect;
            *clip = *(other.clip);
        }
        else
            clip = nullptr;
    }
    return *this;
}

Diamond::SDLRenderObj2D &Diamond::SDLRenderObj2D::operator=(SDLRenderObj2D &&other) {
    RenderObj2D::operator=(other);
    if (this != &other) {
        texture = other.texture;
        flip = other.flip;
        size = other.size;
        clip = other.clip;

        other.texture = nullptr;
        other.clip = nullptr;
    }
    return *this;
}

void Diamond::SDLRenderObj2D::setTexture(Texture *texture, float scale) {
	this->texture = dynamic_cast<SDLTexture*>(texture);
	applyScale(scale);
}

void Diamond::SDLRenderObj2D::applyScale(float scale) {
	if (clip) {
		size.x = clip->w * scale;
		size.y = clip->h * scale;
	}
	else {
		size.x = texture->width * scale;
		size.y = texture->height * scale;
	}
}

void Diamond::SDLRenderObj2D::flipX() {
	flip = (SDL_RendererFlip)(flip ^ SDL_FLIP_HORIZONTAL);
}

void Diamond::SDLRenderObj2D::flipY() {
	flip = (SDL_RendererFlip)(flip ^ SDL_FLIP_VERTICAL);
}

int Diamond::SDLRenderObj2D::isFlippedX() const {
	return flip & SDL_FLIP_HORIZONTAL;
}

int Diamond::SDLRenderObj2D::isFlippedY() const {
	return flip & SDL_FLIP_VERTICAL;
}

void Diamond::SDLRenderObj2D::initClip() {
	if (!clip)	clip = new SDL_Rect;
	clip->x = 0;
	clip->y = 0;
	clip->w = texture->width;
	clip->h = texture->height;
}

void Diamond::SDLRenderObj2D::setClip(int x, int y, int w, int h) {
    clip->x = x;
    clip->y = y;

    size.x *= (float)w / clip->w;
    size.y *= (float)h / clip->h;

    clip->w = w;
    clip->h = h;
}

void Diamond::SDLRenderObj2D::setClip(int x, int y) {
    clip->x = x;
    clip->y = y;
}

bool Diamond::SDLRenderObj2D::getClipDim(Vector2<int> &dim) const {
	if (!clip)	return false;
	dim.x = clip->w;
	dim.y = clip->h;
	return true;
}
<|endoftext|>


<commit_before>#include <i2pd/I2CP.h>
#include <i2pd/Log.h>
#include <i2pd/RouterContext.h>
#include <random>
#include <mutex>
#include <condition_variable>

std::condition_variable done;
std::mutex done_mtx;

struct DropSettings
{
	std::function<bool(void)> Drop;
	uint64_t Latency = 500;
	uint64_t Jitter = 40;
};

class DroppyI2CPServer : public i2p::client::I2CPServer
{
public:
	DroppyI2CPServer(const std::string & addr, int port, DropSettings drop) :
		i2p::client::I2CPServer(addr, port),
		m_Settings(drop)
	{
	}

	bool ShouldDrop(i2p::client::DropEvent & ev)
	{

		ev.Delay = m_Settings.Latency;
		if (m_Settings.Jitter > 0)
		{
			ev.Delay += rand() % m_Settings.Jitter;
		}
		if(m_Settings.Drop)
		{
			return m_Settings.Drop();
		}
		return false;
	}

private:

	DropSettings m_Settings;

};

DroppyI2CPServer * server = nullptr;

void handle_sig(int signal)
{
	done.notify_all();
}

int main(int argc, char * argv[])
{

	auto & log = i2p::log::Logger();
	log.Start();
	log.SetLogLevel("debug");

	if(argc == 4)
	{

		std::string addr = argv[1];
		int port = std::atoi(argv[2]);
		double drop = std::atof(argv[3]);

		std::random_device rd;
		std::mt19937 gen(rd());

		std::bernoulli_distribution dist(drop);

		DropSettings settings;
		settings.Drop = [&] () -> bool {
			return dist(gen);
		};

		i2p::context.Init();

		DroppyI2CPServer server(addr, port, settings);

		signal(SIGINT, handle_sig);
		signal(SIGTERM, handle_sig);

		server.Start();
		LogPrint(eLogInfo, "i2cp server started");
		{
			std::unique_lock<std::mutex> lock(done_mtx);
			done.wait(lock);
		}
		server.Stop();
	}
	else
		LogPrint(eLogError, "usage ", argv[0], " addr port drop_prob");
	log.Stop();
	return 0;
}
<commit_msg>works<commit_after>#include <i2pd/I2CP.h>
#include <i2pd/Log.h>
#include <i2pd/RouterContext.h>
#include <random>
#include <mutex>
#include <condition_variable>

std::condition_variable done;
std::mutex done_mtx;

struct DropSettings
{
	std::function<bool(void)> Drop;
	uint64_t Latency = 500;
	uint64_t Jitter = 40;
};

class DroppyI2CPServer : public i2p::client::I2CPServer
{
public:
	DroppyI2CPServer(const std::string & addr, int port, DropSettings drop) :
		i2p::client::I2CPServer(addr, port),
		m_Settings(drop)
	{
	}

	bool ShouldDrop(i2p::client::DropEvent & ev)
	{

		ev.Delay = m_Settings.Latency;
		if (m_Settings.Jitter > 0)
		{
			ev.Delay += rand() % m_Settings.Jitter;
		}
		if(m_Settings.Drop)
		{
			return m_Settings.Drop();
		}
		return false;
	}

private:

	DropSettings m_Settings;

};

DroppyI2CPServer * server = nullptr;

void handle_sig(int signal)
{
	done.notify_all();
}

int main(int argc, char * argv[])
{

	auto & log = i2p::log::Logger();
	log.Start();
	log.SetLogLevel("debug");

	if(argc == 5)
	{

		std::string addr = argv[1];
		int port = std::atoi(argv[2]);
		double drop = std::atof(argv[3]);

		std::random_device rd;
		std::mt19937 gen(rd());

		std::bernoulli_distribution dist(drop);

		DropSettings settings;
		settings.Drop = [&] () -> bool {
			return dist(gen);
		};

		settings.Latency = std::atoi(argv[4]);

		i2p::context.Init();

		DroppyI2CPServer server(addr, port, settings);

		signal(SIGINT, handle_sig);
		signal(SIGTERM, handle_sig);

		server.Start();
		LogPrint(eLogInfo, "i2cp server started");
		{
			std::unique_lock<std::mutex> lock(done_mtx);
			done.wait(lock);
		}
		server.Stop();
	}
	else
		LogPrint(eLogError, "usage ", argv[0], " addr port drop_prob");
	log.Stop();
	return 0;
}
<|endoftext|>


<commit_before>/****************************************************************************
 *   Copyright (C) 2014-2015 by Savoir-Faire Linux                          *
 *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
 *                                                                          *
 *   This library is free software; you can redistribute it and/or          *
 *   modify it under the terms of the GNU Lesser General Public             *
 *   License as published by the Free Software Foundation; either           *
 *   version 2.1 of the License, or (at your option) any later version.     *
 *                                                                          *
 *   This library is distributed in the hope that it will be useful,        *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      *
 *   Lesser General Public License for more details.                        *
 *                                                                          *
 *   You should have received a copy of the GNU General Public License      *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
 ***************************************************************************/

template <class T>
class CollectionManagerInterfacePrivate
{
public:
   ///All manager should be QAbstractItemModel, this wont compile (on purpose) if the aren't
   CollectionManagerInterfacePrivate(QAbstractItemModel* p2, CollectionManagerInterface<T>* p) :
   m_pMediator(nullptr),q_ptr(p2),i_ptr(p)
   {}
   ~CollectionManagerInterfacePrivate();

   QVector< CollectionInterface* > m_lCollections;
   QVector< CollectionInterface* > m_lEnabledCollections;
   mutable CollectionMediator<T>*  m_pMediator;
   QAbstractItemModel*             q_ptr;
   CollectionManagerInterface<T>*  i_ptr;

   CollectionMediator<T>* itemMediator() const;
   inline const QVector< CollectionInterface* > filterCollections(QVector< CollectionInterface* > in, FlagPack<CollectionInterface::SupportedFeatures> features) const;
};

template<class T>
CollectionMediator<T>* CollectionManagerInterfacePrivate<T>::itemMediator() const
{
   if (!m_pMediator) {
      m_pMediator = new CollectionMediator<T>(i_ptr,q_ptr);
   }
   return m_pMediator;
}

template<class T>
CollectionManagerInterfacePrivate<T>::~CollectionManagerInterfacePrivate()
{
   if (m_pMediator)
      delete m_pMediator;
}

template<class T>
template <class T2, typename ...Ts>
T2* CollectionManagerInterface<T>::addCollection(Ts... args, const LoadOptions options)
{
   T2* collection = new T2(d_ptr->itemMediator(),args...);

   //This will force the T2 to be a CollectionInterface subclass
   CollectionInterface* b = collection;
   d_ptr->m_lCollections << b;

   //This is the last time we have the class type (T2), so create a lambda
   //to keep track of the configurator type while we still can
   setCollectionConfigurator(collection,[this]() {
      return registerConfigarator<T2>();
   });

   if (options & LoadOptions::FORCE_ENABLED) { //TODO check is the collection is checked

      //Some collections can fail to load directly
      //eventually it will necessary to add an async version of this
      //to load the collection only when it is loaded
      if (collection->load())
         d_ptr->m_lEnabledCollections << collection;
   }

   registerToModel(collection);

   return collection;
}

template<class T>
template <class T2>
CollectionCreationInterface* CollectionManagerInterface<T>::registerCreator(CollectionCreationInterface* creator)
{
   static CollectionCreationInterface* cfg = nullptr;
   if (creator) {
      cfg = creator;
      addCreatorToList(creator);
   }
   return cfg;
}

template<class T>
template <class T2>
CollectionConfigurationInterface* CollectionManagerInterface<T>::registerConfigarator(CollectionConfigurationInterface* configurator)
{
   static CollectionConfigurationInterface* cfg = nullptr;
   if (configurator) {
      cfg = configurator;
      addConfiguratorToList(configurator);
   }
   return cfg;
}

template<class T>
CollectionManagerInterface<T>::CollectionManagerInterface(QAbstractItemModel* self) : d_ptr(new CollectionManagerInterfacePrivate<T>(self,this))
{

}

template<class T>
const QVector< CollectionInterface* > CollectionManagerInterfacePrivate<T>::filterCollections(QVector< CollectionInterface* > in, FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   QVector< CollectionInterface* > out;
   for (CollectionInterface* col : in) {
      if ((col->supportedFeatures() & features) == features)
         out << col;
   }
   return out;
}

/**
 * Return the list of all collections associated with this manager
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
const QVector< CollectionInterface* > CollectionManagerInterface<T>::collections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lCollections, features);
   else
      return d_ptr->m_lCollections;
}

/**
 * Return the list of all enabled collections associated with this manager
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
const QVector< CollectionInterface* > CollectionManagerInterface<T>::enabledCollections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lEnabledCollections, features);
   else
      return d_ptr->m_lEnabledCollections;
}

/**
 * Return if the manager has enabled collections
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
bool CollectionManagerInterface<T>::hasEnabledCollections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lEnabledCollections, features).size();
   else
      return d_ptr->m_lEnabledCollections.size();
}

/**
 * Return the list of all collections associated with this manager
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
bool CollectionManagerInterface<T>::hasCollections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lCollections, features).size();
   else
      return d_ptr->m_lCollections.size();
}

template<class T>
bool CollectionManagerInterface<T>::clearAllCollections() const
{
   return false;
}

template<class T>
void CollectionManagerInterface<T>::collectionAddedCallback(CollectionInterface* collection)
{
   Q_UNUSED(collection)
}

template<class T>
bool CollectionManagerInterface<T>::deleteItem(T* item)
{
   if (item->collection()) {
      if (item->collection()->model() != reinterpret_cast<QAbstractItemModel*>(this))
         qWarning() << "Item not deleted by its parent model";
      if (item->collection()->supportedFeatures() & CollectionInterface::SupportedFeatures::REMOVE) {
         item->collection()->remove(item);
         return true;
      }
      else
         qDebug() << item << "cannot be deleted, the collection doesn't support removing items";
   }
   else
      qDebug() << item << "cannot be deleted, it has no collection";
   return false;
}

template<class T>
bool CollectionManagerInterface<T>::enableCollection( CollectionInterface*  collection, bool enabled)
{
   Q_UNUSED(enabled) //TODO implement it
   collection->load();
   return true;
}
<commit_msg>collectionmanager: Allow recursive insertions<commit_after>/****************************************************************************
 *   Copyright (C) 2014-2015 by Savoir-Faire Linux                          *
 *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
 *                                                                          *
 *   This library is free software; you can redistribute it and/or          *
 *   modify it under the terms of the GNU Lesser General Public             *
 *   License as published by the Free Software Foundation; either           *
 *   version 2.1 of the License, or (at your option) any later version.     *
 *                                                                          *
 *   This library is distributed in the hope that it will be useful,        *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      *
 *   Lesser General Public License for more details.                        *
 *                                                                          *
 *   You should have received a copy of the GNU General Public License      *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
 ***************************************************************************/

template <class T>
class CollectionManagerInterfacePrivate
{
public:
   ///All manager should be QAbstractItemModel, this wont compile (on purpose) if the aren't
   CollectionManagerInterfacePrivate(QAbstractItemModel* p2, CollectionManagerInterface<T>* p) :
   m_pMediator(nullptr),q_ptr(p2),i_ptr(p)
   {}
   ~CollectionManagerInterfacePrivate();

   QVector< CollectionInterface* > m_lCollections;
   QVector< CollectionInterface* > m_lEnabledCollections;
   mutable CollectionMediator<T>*  m_pMediator;
   QAbstractItemModel*             q_ptr;
   CollectionManagerInterface<T>*  i_ptr;

   CollectionMediator<T>* itemMediator() const;
   inline const QVector< CollectionInterface* > filterCollections(QVector< CollectionInterface* > in, FlagPack<CollectionInterface::SupportedFeatures> features) const;
};

template<class T>
CollectionMediator<T>* CollectionManagerInterfacePrivate<T>::itemMediator() const
{
   if (!m_pMediator) {
      m_pMediator = new CollectionMediator<T>(i_ptr,q_ptr);
   }
   return m_pMediator;
}

template<class T>
CollectionManagerInterfacePrivate<T>::~CollectionManagerInterfacePrivate()
{
   if (m_pMediator)
      delete m_pMediator;
}

template<class T>
template <class T2, typename ...Ts>
T2* CollectionManagerInterface<T>::addCollection(Ts... args, const LoadOptions options)
{
   T2* collection = new T2(d_ptr->itemMediator(),args...);

   //This will force the T2 to be a CollectionInterface subclass
   CollectionInterface* b = collection;
   d_ptr->m_lCollections << b;

   //This is the last time we have the class type (T2), so create a lambda
   //to keep track of the configurator type while we still can
   setCollectionConfigurator(collection,[this]() {
      return registerConfigarator<T2>();
   });

   if (options & LoadOptions::FORCE_ENABLED) { //TODO check is the collection is checked

      //Some collections can fail to load directly
      //eventually it will necessary to add an async version of this
      //to load the collection only when it is loaded
      if (collection->load())
         d_ptr->m_lEnabledCollections << collection;
   }

   registerToModel(collection);

   return collection;
}

template<class T>
template <class T2>
CollectionCreationInterface* CollectionManagerInterface<T>::registerCreator(CollectionCreationInterface* creator)
{
   static CollectionCreationInterface* cfg = nullptr;
   if (creator) {
      cfg = creator;
      addCreatorToList(creator);
   }
   return cfg;
}

template<class T>
template <class T2>
CollectionConfigurationInterface* CollectionManagerInterface<T>::registerConfigarator(CollectionConfigurationInterface* configurator)
{
   static CollectionConfigurationInterface* cfg = nullptr;
   if (configurator) {
      cfg = configurator;
      addConfiguratorToList(configurator);
   }
   return cfg;
}

template<class T>
CollectionManagerInterface<T>::CollectionManagerInterface(QAbstractItemModel* self) : d_ptr(new CollectionManagerInterfacePrivate<T>(self,this)),
m_InsertionMutex(QMutex::Recursive)
{

}

template<class T>
const QVector< CollectionInterface* > CollectionManagerInterfacePrivate<T>::filterCollections(QVector< CollectionInterface* > in, FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   QVector< CollectionInterface* > out;
   for (CollectionInterface* col : in) {
      if ((col->supportedFeatures() & features) == features)
         out << col;
   }
   return out;
}

/**
 * Return the list of all collections associated with this manager
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
const QVector< CollectionInterface* > CollectionManagerInterface<T>::collections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lCollections, features);
   else
      return d_ptr->m_lCollections;
}

/**
 * Return the list of all enabled collections associated with this manager
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
const QVector< CollectionInterface* > CollectionManagerInterface<T>::enabledCollections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lEnabledCollections, features);
   else
      return d_ptr->m_lEnabledCollections;
}

/**
 * Return if the manager has enabled collections
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
bool CollectionManagerInterface<T>::hasEnabledCollections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lEnabledCollections, features).size();
   else
      return d_ptr->m_lEnabledCollections.size();
}

/**
 * Return the list of all collections associated with this manager
 * @param features An optional set of required features
 * @note Please note that this method complexity is O(N) when "features" is set
 */
template<class T>
bool CollectionManagerInterface<T>::hasCollections(FlagPack<CollectionInterface::SupportedFeatures> features) const
{
   if (features != CollectionInterface::SupportedFeatures::NONE)
      return d_ptr->filterCollections(d_ptr->m_lCollections, features).size();
   else
      return d_ptr->m_lCollections.size();
}

template<class T>
bool CollectionManagerInterface<T>::clearAllCollections() const
{
   return false;
}

template<class T>
void CollectionManagerInterface<T>::collectionAddedCallback(CollectionInterface* collection)
{
   Q_UNUSED(collection)
}

template<class T>
bool CollectionManagerInterface<T>::deleteItem(T* item)
{
   if (item->collection()) {
      if (item->collection()->model() != reinterpret_cast<QAbstractItemModel*>(this))
         qWarning() << "Item not deleted by its parent model";
      if (item->collection()->supportedFeatures() & CollectionInterface::SupportedFeatures::REMOVE) {
         item->collection()->remove(item);
         return true;
      }
      else
         qDebug() << item << "cannot be deleted, the collection doesn't support removing items";
   }
   else
      qDebug() << item << "cannot be deleted, it has no collection";
   return false;
}

template<class T>
bool CollectionManagerInterface<T>::enableCollection( CollectionInterface*  collection, bool enabled)
{
   Q_UNUSED(enabled) //TODO implement it
   collection->load();
   return true;
}
<|endoftext|>


<commit_before>//===- not.cpp - The 'not' testing tool -----------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Usage:
//   not cmd
//     Will return true if cmd doesn't crash and returns false.
//   not --crash cmd
//     Will return true if cmd crashes (e.g. for testing crash reporting).

#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

int main(int argc, const char **argv) {
  bool ExpectCrash = false;

  ++argv;
  --argc;

  if (argc > 0 && StringRef(argv[0]) == "--crash") {
    ++argv;
    --argc;
    ExpectCrash = true;
  }

  if (argc == 0)
    return 1;

  auto Program = sys::findProgramByName(argv[0]);
  if (!Program) {
    errs() << "Error: Unable to find `" << argv[0]
           << "' in PATH: " << Program.getError().message() << "\n";
    return 1;
  }

  std::vector<StringRef> Argv;
  Argv.reserve(argc);
  for (int i = 0; i < argc; ++i)
    Argv.push_back(argv[i]);
  std::string ErrMsg;
  int Result = sys::ExecuteAndWait(*Program, Argv, None, {}, 0, 0, &ErrMsg);
#ifdef _WIN32
  // Handle abort() in msvcrt -- It has exit code as 3.  abort(), aka
  // unreachable, should be recognized as a crash.  However, some binaries use
  // exit code 3 on non-crash failure paths, so only do this if we expect a
  // crash.
  if (ExpectCrash && Result == 3)
    Result = -3;
#endif
  if (Result < 0) {
    errs() << "Error: " << ErrMsg << "\n";
    if (ExpectCrash)
      return 0;
    return 1;
  }

  if (ExpectCrash)
    return 1;

  return Result == 0;
}
<commit_msg>[not] Improve error reporting consistency.<commit_after>//===- not.cpp - The 'not' testing tool -----------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Usage:
//   not cmd
//     Will return true if cmd doesn't crash and returns false.
//   not --crash cmd
//     Will return true if cmd crashes (e.g. for testing crash reporting).

#include "llvm/Support/Program.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

int main(int argc, const char **argv) {
  bool ExpectCrash = false;

  ++argv;
  --argc;

  if (argc > 0 && StringRef(argv[0]) == "--crash") {
    ++argv;
    --argc;
    ExpectCrash = true;
  }

  if (argc == 0)
    return 1;

  auto Program = sys::findProgramByName(argv[0]);
  if (!Program) {
    WithColor::error() << "unable to find `" << argv[0]
                       << "' in PATH: " << Program.getError().message() << "\n";
    return 1;
  }

  std::vector<StringRef> Argv;
  Argv.reserve(argc);
  for (int i = 0; i < argc; ++i)
    Argv.push_back(argv[i]);
  std::string ErrMsg;
  int Result = sys::ExecuteAndWait(*Program, Argv, None, {}, 0, 0, &ErrMsg);
#ifdef _WIN32
  // Handle abort() in msvcrt -- It has exit code as 3.  abort(), aka
  // unreachable, should be recognized as a crash.  However, some binaries use
  // exit code 3 on non-crash failure paths, so only do this if we expect a
  // crash.
  if (ExpectCrash && Result == 3)
    Result = -3;
#endif
  if (Result < 0) {
    WithColor::error() << ErrMsg << "\n";
    if (ExpectCrash)
      return 0;
    return 1;
  }

  if (ExpectCrash)
    return 1;

  return Result == 0;
}
<|endoftext|>


<commit_before>#include "BVS.h"

BVS* bvs;
BVSLogger logger("BVSDaemon");

int setup(int argc, char** arcv);
int testLogger();
int testConfig();

int main(int argc, char** argv)
{
    setup(argc, argv);

    //testLogger();
    //testConfig();

    LOG(2, "loading Modules!");
    bvs->loadModules();

    LOG(2, "run!");
    bvs->run();

    bvs->close();

    return 0;
}

int setup(int argc, char** argv)
{
    // start BVS
    bvs = new BVS(argc, argv);
    //bvs.enableLogFile("BVSLog.txt");
    //bvs.enableLogConsole();

    LOG(2, "starting!");

    return 0;
}

int testLogger()
{
    LOG(0, "to CLI FILE");
    bvs->disableLogConsole();
    LOG(0, "to FILE only");
    BVSLogger file("FILE LOG", 3, BVSLogger::TO_FILE);
    file.out(0) << "FILE ONLY" << std::endl;
    file.endl();
    bvs->enableLogConsole();
    BVSLogger cli("CLI LOG", 3, BVSLogger::TO_CLI);
    cli.out(0) << "CLI ONLY" << std::endl;
    cli.endl();
    bvs->disableLogConsole();
    bvs->disableLogFile();
    LOG(0, "NOOP");
    bvs->enableLogConsole();
    LOG(0, "to CLI");
    bvs->disableLogConsole();
    bvs->enableLogFile("BVSLog.txt", true);
    LOG(0, "to FILE AGAIN");
    bvs->enableLogConsole();
    BVSLogger both("to BOTH", 0, BVSLogger::TO_CLI_AND_FILE);
    both.out(0) << "to CLI AND FILE" << std::endl;
    both.endl();

    return 0;
}

int testConfig()
{
    LOG(0, "dump all config options to std::cout!");
    bvs->config.showOptionStore();
    LOG(0, "testing...");

    int i;
    std::string s;
    bool b;
    bvs->config.getValue("BVSLogger.All", i, 0)
        .getValue("BVS.logFile", s, std::string("default"))
        .getValue("BVS.logSystem", b, false);

    LOG(0, "Getting int: " << i);
    LOG(0, "Getting string: " << s);
    LOG(0, "Getting bool: " << b);

    i = bvs->config.getValue<int>("BVSLogger.All", 0);
    s = bvs->config.getValue<std::string>("BVS.logFile", std::string("default"));
    b = bvs->config.getValue<bool>("BVS.logSystem", false);
    LOG(0, "Getting int directly: " << i);
    LOG(0, "Getting string directly: " << s);
    LOG(0, "Getting bool directly: " << b);

    std::string empty;
    bvs->config.getValue("this.option.does.not.exist", empty, std::string("empty"));
    LOG(0, "This should be 'empty': " << empty);

    std::vector<std::string> list;
    bvs->config.getValue("BVS.modules", list);
    LOG(0, "List:");
    int count = 0;
    for (auto it : list)
    {
        LOG(0, count++ << ": " << it);
    }

    return 0;
}
<commit_msg>daemon: rename close to quit<commit_after>#include "BVS.h"

BVS* bvs;
BVSLogger logger("BVSDaemon");

int setup(int argc, char** arcv);
int testLogger();
int testConfig();

int main(int argc, char** argv)
{
    setup(argc, argv);

    //testLogger();
    //testConfig();

    LOG(2, "loading Modules!");
    bvs->loadModules();

    LOG(2, "run!");
    bvs->run();

    bvs->quit();

    return 0;
}

int setup(int argc, char** argv)
{
    // start BVS
    bvs = new BVS(argc, argv);
    //bvs.enableLogFile("BVSLog.txt");
    //bvs.enableLogConsole();

    LOG(2, "starting!");

    return 0;
}

int testLogger()
{
    LOG(0, "to CLI FILE");
    bvs->disableLogConsole();
    LOG(0, "to FILE only");
    BVSLogger file("FILE LOG", 3, BVSLogger::TO_FILE);
    file.out(0) << "FILE ONLY" << std::endl;
    file.endl();
    bvs->enableLogConsole();
    BVSLogger cli("CLI LOG", 3, BVSLogger::TO_CLI);
    cli.out(0) << "CLI ONLY" << std::endl;
    cli.endl();
    bvs->disableLogConsole();
    bvs->disableLogFile();
    LOG(0, "NOOP");
    bvs->enableLogConsole();
    LOG(0, "to CLI");
    bvs->disableLogConsole();
    bvs->enableLogFile("BVSLog.txt", true);
    LOG(0, "to FILE AGAIN");
    bvs->enableLogConsole();
    BVSLogger both("to BOTH", 0, BVSLogger::TO_CLI_AND_FILE);
    both.out(0) << "to CLI AND FILE" << std::endl;
    both.endl();

    return 0;
}

int testConfig()
{
    LOG(0, "dump all config options to std::cout!");
    bvs->config.showOptionStore();
    LOG(0, "testing...");

    int i;
    std::string s;
    bool b;
    bvs->config.getValue("BVSLogger.All", i, 0)
        .getValue("BVS.logFile", s, std::string("default"))
        .getValue("BVS.logSystem", b, false);

    LOG(0, "Getting int: " << i);
    LOG(0, "Getting string: " << s);
    LOG(0, "Getting bool: " << b);

    i = bvs->config.getValue<int>("BVSLogger.All", 0);
    s = bvs->config.getValue<std::string>("BVS.logFile", std::string("default"));
    b = bvs->config.getValue<bool>("BVS.logSystem", false);
    LOG(0, "Getting int directly: " << i);
    LOG(0, "Getting string directly: " << s);
    LOG(0, "Getting bool directly: " << b);

    std::string empty;
    bvs->config.getValue("this.option.does.not.exist", empty, std::string("empty"));
    LOG(0, "This should be 'empty': " << empty);

    std::vector<std::string> list;
    bvs->config.getValue("BVS.modules", list);
    LOG(0, "List:");
    int count = 0;
    for (auto it : list)
    {
        LOG(0, count++ << ": " << it);
    }

    return 0;
}
<|endoftext|>


<commit_before>#ifndef SKSAT_WINDOW_HPP
#define SKSAT_WINDOW_HPP

#include <sksat/common.hpp>
#include <sksat/platform.hpp>

namespace sksat {

class window_base {
public:
	window_base() : opend(false), xsize(default_xsize), ysize(default_ysize), xpos(default_xpos), ypos(default_ypos) {}
	window_base(size_t x, size_t y) : opend(false), xsize(x), ysize(y), xpos(default_xpos), ypos(default_ypos) {}
	window_base(sksat::string &t, size_t x, size_t y) : opend(false), title(t), xsize(x), ysize(y), xpos(default_xpos), ypos(default_ypos) {}

	void open(){ opend = api_open(); }
	void open(size_t x, size_t y){ open(); set_size(x,y); }
	void open(sksat::string &t){ set_title(t); open(); }
	void open(sksat::string &t, size_t x, size_t y){ set_title(t); open(x,y); }

	void close(){ api_close(); opend=false; }

	void show(){ api_show(); }

	void set_title(sksat::string &t){ title = t; set_title(t.c_str()); }
	void set_title(const char *t){ title = t; api_set_title(t);  }
	sksat::string get_title() const { return title; }

	virtual void set_size(size_t x, size_t y){
		xsize=x;
		ysize=y;
		if(opend)
			api_set_size(x,y);
	}
	void set_xsize(size_t x){ set_size(x, ysize); }
	void set_ysize(size_t y){ set_size(xsize, y); }
	size_t get_xsize() const { return xsize; }
	size_t get_ysize() const { return ysize; }

	operator bool () const { return opend; }
protected:
	virtual bool api_open() = 0;
	virtual void api_close() = 0;
	virtual void api_show() = 0;
	virtual void api_set_title(const char *t) = 0;
	virtual void api_set_size(size_t x, size_t y) = 0;
public:
	static size_t default_xsize, default_ysize;
	static size_t default_xpos, default_ypos;
protected:
	bool opend;
	size_t xsize, ysize;
	size_t xpos, ypos;
	sksat::string title;
};

size_t window_base::default_xsize = 100;
size_t window_base::default_ysize = 100;
size_t window_base::default_xpos  = 0;
size_t window_base::default_ypos  = 0;

}

#if defined(OS_WIN32)
	#include <sksat/win32/window.hpp>
	namespace sksat{ using sksat::win32::window; }
#elif defined(OS_LINUX)
	#include <sksat/linux/window.hpp>
	namespace sksat{ using sksat::linux::window; }
#else
	#error not implemented.
#endif

#endif
<commit_msg>[ADD] draw func(comment)<commit_after>#ifndef SKSAT_WINDOW_HPP
#define SKSAT_WINDOW_HPP

#include <sksat/common.hpp>
#include <sksat/platform.hpp>

namespace sksat {

class window_base {
public:
	window_base() : opend(false), xsize(default_xsize), ysize(default_ysize), xpos(default_xpos), ypos(default_ypos) {}
	window_base(size_t x, size_t y) : opend(false), xsize(x), ysize(y), xpos(default_xpos), ypos(default_ypos) {}
	window_base(sksat::string &t, size_t x, size_t y) : opend(false), title(t), xsize(x), ysize(y), xpos(default_xpos), ypos(default_ypos) {}

	void open(){ opend = api_open(); }
	void open(size_t x, size_t y){ open(); set_size(x,y); }
	void open(sksat::string &t){ set_title(t); open(); }
	void open(sksat::string &t, size_t x, size_t y){ set_title(t); open(x,y); }

	void close(){ api_close(); opend=false; }

	void show(){ api_show(); }

	void set_title(sksat::string &t){ title = t; set_title(t.c_str()); }
	void set_title(const char *t){ title = t; api_set_title(t);  }
	sksat::string get_title() const { return title; }

	virtual void set_size(size_t x, size_t y){
		xsize=x;
		ysize=y;
		if(opend)
			api_set_size(x,y);
	}
	void set_xsize(size_t x){ set_size(x, ysize); }
	void set_ysize(size_t y){ set_size(xsize, y); }
	size_t get_xsize() const { return xsize; }
	size_t get_ysize() const { return ysize; }

	operator bool () const { return opend; }

	// 描画関数
/*
	void draw_point(sksat::color &col, size_t x, size_t y);
	void draw_line(sksat::color &col, size_t x0, size_t y0, size_t x1, size_t y1);
	void draw_rect(sksat::color &col, size_t x0, size_t y0, size_t x1, size_t y1, bool fill);

	// set_color()でセットした色
	void draw_point(size_t x, size_t y);
	void draw_line(size_t x0, size_t y0, size_t x1, size_t y1);
	void draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, bool fill);

	void fill_rect(size_t x0, size_t y0, size_t x1, size_t y1){ draw_rect(x0,y0,x1,y1,true); }
*/
protected:
	virtual bool api_open() = 0;
	virtual void api_close() = 0;
	virtual void api_show() = 0;
	virtual void api_set_title(const char *t) = 0;
	virtual void api_set_size(size_t x, size_t y) = 0;
public:
	static size_t default_xsize, default_ysize;
	static size_t default_xpos, default_ypos;
protected:
	bool opend;
	size_t xsize, ysize;
	size_t xpos, ypos;
	sksat::string title;
};

size_t window_base::default_xsize = 100;
size_t window_base::default_ysize = 100;
size_t window_base::default_xpos  = 0;
size_t window_base::default_ypos  = 0;

}

#if defined(OS_WIN32)
	#include <sksat/win32/window.hpp>
	namespace sksat{ using sksat::win32::window; }
#elif defined(OS_LINUX)
	#include <sksat/linux/window.hpp>
	namespace sksat{ using sksat::linux::window; }
#else
	#error not implemented.
#endif

#endif
<|endoftext|>


<commit_before>// Copyright (C) 2009-2011, James Webber.
// Distributed under a 3-clause BSD license.  See COPYING.

#include "error_reporter.hpp"

#include <boost/utility.hpp>

namespace config {
  class pipeline_config;

  //! \ingroup grp_config
  //! Parsing context used by the lexer and parser.
  class parse_context : boost::noncopyable {
    public:
    explicit parse_context(pipeline_config &pc) : output_(pc) {
    }

    error_reporter &reporter() { return reporter_; }
    pipeline_config &output() { return output_; }

    private:
    pipeline_config &output_;
    error_reporter reporter_;
  };
}
<commit_msg>Add state managment to the parse_context.<commit_after>// Copyright (C) 2009-2011, James Webber.
// Distributed under a 3-clause BSD license.  See COPYING.

#include "error_reporter.hpp"
#include "pipeline_configs.hpp"
#include "../plugin-proto/asserts.hpp"

#include <boost/utility.hpp>

namespace config {
  class pipeline_config;

  //! \ingroup grp_config
  //! Parsing context used by the lexer and parser.
  class parse_context : boost::noncopyable {
    public:
    explicit parse_context(pipeline_config &pc) : output_(pc) {
    }

    error_reporter &reporter() { return reporter_; }
    pipeline_config &output() { return output_; }

    //! \name Parser actions
    //! These maintain state within a parse.
    //@{

    void new_job() { current.job = &(output_.new_job()); }
    void new_section() { current.section = &NERVE_CHECK_PTR(current.job)->new_section(); }
    void new_stage() { current.stage = &NERVE_CHECK_PTR(current.section)->new_stage(); }

    void end_job() { current.reset(); }
    void end_section() { current.stage = NULL; current.section = NULL; }
    void end_stage() { current.stage = NULL; }

    stage_config &this_stage() { return *NERVE_CHECK_PTR(current.stage); }
    section_config &this_section() { return *NERVE_CHECK_PTR(current.section); }
    job_config &this_job() { return *NERVE_CHECK_PTR(current.job); }

    //@}

    private:
    pipeline_config &output_;

    // Differ depending on where we are in the parse.
    struct output_pointers {
      job_config *job;
      section_config *section;
      stage_config *stage;

      output_pointers() { reset(); }
      void reset() { job = NULL; section = NULL; stage = NULL; }
    } current;

    error_reporter reporter_;
  };
}
<|endoftext|>


<commit_before>/*
 * DesktopWebPage.cpp
 *
 * Copyright (C) 2009-18 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "DesktopWebPage.hpp"

#include <boost/algorithm/string.hpp>

#include <core/Thread.hpp>

#include <QFileDialog>
#include <QWebEngineSettings>

#include "DesktopDownloadItemHelper.hpp"
#include "DesktopMainWindow.hpp"
#include "DesktopSatelliteWindow.hpp"
#include "DesktopSecondaryWindow.hpp"
#include "DesktopWebProfile.hpp"
#include "DesktopWindowTracker.hpp"

using namespace rstudio::core;

extern QString sharedSecret;

namespace rstudio {
namespace desktop {

namespace {

WindowTracker s_windowTracker;
std::mutex s_mutex;

void onDownloadRequested(QWebEngineDownloadItem* downloadItem)
{
   // request directory from user
   QString downloadPath = QFileDialog::getSaveFileName(
            nullptr,
            QStringLiteral("Save File"),
            downloadItem->path());

   if (downloadPath.isEmpty())
      return;

   DownloadHelper::manageDownload(downloadItem, downloadPath);
}

} // anonymous namespace

WebPage::WebPage(QUrl baseUrl, QWidget *parent, bool allowExternalNavigate) :
      QWebEnginePage(new WebProfile(baseUrl), parent),
      baseUrl_(baseUrl),
      allowExternalNav_(allowExternalNavigate)
{
   settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
   settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
   settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);

   defaultSaveDir_ = QDir::home();
   connect(this, SIGNAL(windowCloseRequested()), SLOT(closeRequested()));
   connect(profile(), &QWebEngineProfile::downloadRequested, onDownloadRequested);
}

void WebPage::setBaseUrl(const QUrl& baseUrl)
{
   const WebProfile* pProfile = dynamic_cast<const WebProfile*>(profile());
   if (pProfile != nullptr)
   {
      // if we're using our own WebProfile implementation, update its base URL
      const_cast<WebProfile*>(pProfile)->setBaseUrl(baseUrl);
   }

   baseUrl_ = baseUrl;
}

void WebPage::activateWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::raiseAndActivateWindow(pWindow);
}

void WebPage::closeWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::closeWindow(pWindow);
}

void WebPage::prepareForWindow(const PendingWindow& pendingWindow)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   pendingWindows_.push(pendingWindow);
}

QWebEnginePage* WebPage::createWindow(QWebEnginePage::WebWindowType type)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   QString name;
   bool isSatellite = false;
   bool showToolbar = true;
   bool allowExternalNavigate = false;
   int width = 0;
   int height = 0;
   int x = -1;
   int y = -1;
   MainWindow* pMainWindow = nullptr;
   BrowserWindow* pWindow = nullptr;
   bool show = true;

   // check if this is target="_blank";
   if (type == QWebEnginePage::WebWindowType::WebBrowserTab)
   {
      // QtWebEngine behavior is to open a new browser window and send it the
      // acceptNavigationRequest we use to redirect to system browser; don't want
      // that to be visible
      show = false;
      name = tr("_blank_redirector");

      // check for an existing hidden window
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         return pWindow->webView()->webPage();
      }
   }

   // check if we have a satellite window waiting to come up
   if (!pendingWindows_.empty())
   {
      // retrieve the window
      PendingWindow pendingWindow = pendingWindows_.front();
      pendingWindows_.pop();

      // capture pending window params then clear them (one time only)
      name = pendingWindow.name;
      isSatellite = pendingWindow.isSatellite;
      showToolbar = pendingWindow.showToolbar;
      pMainWindow = pendingWindow.pMainWindow;
      allowExternalNavigate = pendingWindow.allowExternalNavigate;

      // get width and height, and adjust for high DPI
      double dpiZoomScaling = getDpiZoomScaling();
      width = pendingWindow.width * dpiZoomScaling;
      height = pendingWindow.height * dpiZoomScaling;
      x = pendingWindow.x;
      y = pendingWindow.y;

      // check for an existing window of this name
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         // activate the browser then return NULL to indicate
         // we didn't create a new WebView
         desktop::raiseAndActivateWindow(pWindow);
         return nullptr;
      }
   }

   if (isSatellite)
   {
      // create and size
      pWindow = new SatelliteWindow(pMainWindow, name, this);
      pWindow->resize(width, height);

      if (x >= 0 && y >= 0)
      {
         // if the window specified its location, use it
         pWindow->move(x, y);
      }
      else if (name != QString::fromUtf8("pdf"))
      {
         // window location was left for us to determine; try to tile the window
         // (but leave pdf window alone since it is so large)

         // calculate location to move to

         // y always attempts to be 25 pixels above then faults back
         // to 25 pixels below if that would be offscreen
         const int OVERLAP = 25;
         int moveY = pMainWindow->y() - OVERLAP;
         if (moveY < 0)
            moveY = pMainWindow->y() + OVERLAP;

         // x is based on centering over main window
         int moveX = pMainWindow->x() +
               (pMainWindow->width() / 2) -
               (width / 2);

         // perform move
         pWindow->move(moveX, moveY);
      }
   }
   else
   {
      pWindow = new SecondaryWindow(showToolbar, name, baseUrl_, nullptr, this,
                                    allowExternalNavigate);
   }

   // if we have a name set, start tracking this window
   if (!name.isEmpty())
   {
      s_windowTracker.addWindow(name, pWindow);
   }

   // show and return the browser
   if (show)
      pWindow->show();
   return pWindow->webView()->webPage();
}

void WebPage::closeRequested()
{
   // invoked when close is requested via script (i.e. window.close()); honor
   // this request by closing the window in which the view is hosted
   view()->window()->close();
}

bool WebPage::shouldInterruptJavaScript()
{
   return false;
}

void WebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/, const QString& message,
                                       int /*lineNumber*/, const QString& /*sourceID*/)
{
   qDebug() << message;
}

// NOTE: NavigationType is unreliable, as per:
//
//    https://bugreports.qt.io/browse/QTBUG-56805
//
// so we avoid querying it here.
bool WebPage::acceptNavigationRequest(const QUrl &url,
                                      NavigationType /* navType*/,
                                      bool isMainFrame)
{
   if (url.toString() == QStringLiteral("about:blank"))
      return true;

   if (url.scheme() != QStringLiteral("http") &&
       url.scheme() != QStringLiteral("https") &&
       url.scheme() != QStringLiteral("mailto") &&
       url.scheme() != QStringLiteral("data"))
   {
      qDebug() << url.toString();
      return false;
   }

   // determine if this is a local request (handle internally only if local)
   std::string host = url.host().toStdString();
   bool isLocal = host == "localhost" || host == "127.0.0.1";

   bool isSubFrame = false;
   WebProfile* webProfile = dynamic_cast<WebProfile*>(profile());
   if (webProfile)
   {
      isSubFrame = webProfile->resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubFrame;
   }

   if ((baseUrl_.isEmpty() && isLocal) ||
       (url.scheme() == baseUrl_.scheme() &&
        url.host() == baseUrl_.host() &&
        url.port() == baseUrl_.port()))
   {
      return true;
   }
   // allow viewer urls to be handled internally by Qt. note that the client is responsible for
   // ensuring that non-local viewer urls are appropriately sandboxed.
   else if (!viewerUrl().isEmpty() &&
            url.toString().startsWith(viewerUrl()))
   {
      return true;
   }
   // allow shiny dialog urls to be handled internally by Qt
   else if (isLocal && !shinyDialogUrl_.isEmpty() &&
            url.toString().startsWith(shinyDialogUrl_))
   {
      return true;
   }
   else if (isSubFrame)
   {
      return true;
   }
   else
   {
      bool navigated = false;

      if (allowExternalNav_)
      {
         // if allowing external navigation, follow this (even if a link click)
         return true;
      }
      else if (boost::algorithm::ends_with(host, ".youtube.com") ||
               boost::algorithm::ends_with(host, ".vimeo.com")   ||
               boost::algorithm::ends_with(host, ".c9.ms"))
      {
         return true;
      }
      else
      {
         // when not allowing external navigation, open an external browser
         // to view the URL
         desktop::openUrl(url);
         navigated = true;
      }

      if (!navigated)
         this->view()->window()->deleteLater();

      return false;
   }
}

void WebPage::setViewerUrl(const QString& viewerUrl)
{
   // record about:blank literally
   if (viewerUrl == QString::fromUtf8("about:blank"))
   {
      viewerUrl_ = viewerUrl;
      return;
   }

   // extract the authority (domain and port) from the URL; we'll agree to
   // serve requests for the viewer pane that match this prefix.
   QUrl url(viewerUrl);
   viewerUrl_ = url.scheme() + QString::fromUtf8("://") +
                url.authority() + QString::fromUtf8("/");
}

QString WebPage::viewerUrl()
{
   if (viewerUrl_.isEmpty())
   {
      // if we don't know the viewer URL ourselves but we're a child window, ask our parent
      BrowserWindow *parent = dynamic_cast<BrowserWindow*>(view()->window());
      if (parent != nullptr && parent->opener() != nullptr)
      {
         return parent->opener()->viewerUrl();
      }
   }

   // return our own viewer URL
   return viewerUrl_;
}

void WebPage::setShinyDialogUrl(const QString &shinyDialogUrl)
{
   shinyDialogUrl_ = shinyDialogUrl;
}

void WebPage::triggerAction(WebAction action, bool checked)
{
   QWebEnginePage::triggerAction(action, checked);
}

} // namespace desktop
} // namespace rstudio
<commit_msg>disallow loading of subframes<commit_after>/*
 * DesktopWebPage.cpp
 *
 * Copyright (C) 2009-18 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "DesktopWebPage.hpp"

#include <boost/algorithm/string.hpp>

#include <core/Thread.hpp>

#include <QFileDialog>
#include <QWebEngineSettings>

#include "DesktopDownloadItemHelper.hpp"
#include "DesktopMainWindow.hpp"
#include "DesktopSatelliteWindow.hpp"
#include "DesktopSecondaryWindow.hpp"
#include "DesktopWebProfile.hpp"
#include "DesktopWindowTracker.hpp"

using namespace rstudio::core;

extern QString sharedSecret;

namespace rstudio {
namespace desktop {

namespace {

WindowTracker s_windowTracker;
std::mutex s_mutex;

void onDownloadRequested(QWebEngineDownloadItem* downloadItem)
{
   // request directory from user
   QString downloadPath = QFileDialog::getSaveFileName(
            nullptr,
            QStringLiteral("Save File"),
            downloadItem->path());

   if (downloadPath.isEmpty())
      return;

   DownloadHelper::manageDownload(downloadItem, downloadPath);
}

} // anonymous namespace

WebPage::WebPage(QUrl baseUrl, QWidget *parent, bool allowExternalNavigate) :
      QWebEnginePage(new WebProfile(baseUrl), parent),
      baseUrl_(baseUrl),
      allowExternalNav_(allowExternalNavigate)
{
   settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
   settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
   settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
   settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);

   defaultSaveDir_ = QDir::home();
   connect(this, SIGNAL(windowCloseRequested()), SLOT(closeRequested()));
   connect(profile(), &QWebEngineProfile::downloadRequested, onDownloadRequested);
}

void WebPage::setBaseUrl(const QUrl& baseUrl)
{
   const WebProfile* pProfile = dynamic_cast<const WebProfile*>(profile());
   if (pProfile != nullptr)
   {
      // if we're using our own WebProfile implementation, update its base URL
      const_cast<WebProfile*>(pProfile)->setBaseUrl(baseUrl);
   }

   baseUrl_ = baseUrl;
}

void WebPage::activateWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::raiseAndActivateWindow(pWindow);
}

void WebPage::closeWindow(QString name)
{
   BrowserWindow* pWindow = s_windowTracker.getWindow(name);
   if (pWindow)
      desktop::closeWindow(pWindow);
}

void WebPage::prepareForWindow(const PendingWindow& pendingWindow)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   pendingWindows_.push(pendingWindow);
}

QWebEnginePage* WebPage::createWindow(QWebEnginePage::WebWindowType type)
{
   std::lock_guard<std::mutex> lock(s_mutex);

   QString name;
   bool isSatellite = false;
   bool showToolbar = true;
   bool allowExternalNavigate = false;
   int width = 0;
   int height = 0;
   int x = -1;
   int y = -1;
   MainWindow* pMainWindow = nullptr;
   BrowserWindow* pWindow = nullptr;
   bool show = true;

   // check if this is target="_blank";
   if (type == QWebEnginePage::WebWindowType::WebBrowserTab)
   {
      // QtWebEngine behavior is to open a new browser window and send it the
      // acceptNavigationRequest we use to redirect to system browser; don't want
      // that to be visible
      show = false;
      name = tr("_blank_redirector");

      // check for an existing hidden window
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         return pWindow->webView()->webPage();
      }
   }

   // check if we have a satellite window waiting to come up
   if (!pendingWindows_.empty())
   {
      // retrieve the window
      PendingWindow pendingWindow = pendingWindows_.front();
      pendingWindows_.pop();

      // capture pending window params then clear them (one time only)
      name = pendingWindow.name;
      isSatellite = pendingWindow.isSatellite;
      showToolbar = pendingWindow.showToolbar;
      pMainWindow = pendingWindow.pMainWindow;
      allowExternalNavigate = pendingWindow.allowExternalNavigate;

      // get width and height, and adjust for high DPI
      double dpiZoomScaling = getDpiZoomScaling();
      width = pendingWindow.width * dpiZoomScaling;
      height = pendingWindow.height * dpiZoomScaling;
      x = pendingWindow.x;
      y = pendingWindow.y;

      // check for an existing window of this name
      pWindow = s_windowTracker.getWindow(name);
      if (pWindow)
      {
         // activate the browser then return NULL to indicate
         // we didn't create a new WebView
         desktop::raiseAndActivateWindow(pWindow);
         return nullptr;
      }
   }

   if (isSatellite)
   {
      // create and size
      pWindow = new SatelliteWindow(pMainWindow, name, this);
      pWindow->resize(width, height);

      if (x >= 0 && y >= 0)
      {
         // if the window specified its location, use it
         pWindow->move(x, y);
      }
      else if (name != QString::fromUtf8("pdf"))
      {
         // window location was left for us to determine; try to tile the window
         // (but leave pdf window alone since it is so large)

         // calculate location to move to

         // y always attempts to be 25 pixels above then faults back
         // to 25 pixels below if that would be offscreen
         const int OVERLAP = 25;
         int moveY = pMainWindow->y() - OVERLAP;
         if (moveY < 0)
            moveY = pMainWindow->y() + OVERLAP;

         // x is based on centering over main window
         int moveX = pMainWindow->x() +
               (pMainWindow->width() / 2) -
               (width / 2);

         // perform move
         pWindow->move(moveX, moveY);
      }
   }
   else
   {
      pWindow = new SecondaryWindow(showToolbar, name, baseUrl_, nullptr, this,
                                    allowExternalNavigate);
   }

   // if we have a name set, start tracking this window
   if (!name.isEmpty())
   {
      s_windowTracker.addWindow(name, pWindow);
   }

   // show and return the browser
   if (show)
      pWindow->show();
   return pWindow->webView()->webPage();
}

void WebPage::closeRequested()
{
   // invoked when close is requested via script (i.e. window.close()); honor
   // this request by closing the window in which the view is hosted
   view()->window()->close();
}

bool WebPage::shouldInterruptJavaScript()
{
   return false;
}

void WebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/, const QString& message,
                                       int /*lineNumber*/, const QString& /*sourceID*/)
{
   qDebug() << message;
}

// NOTE: NavigationType is unreliable, as per:
//
//    https://bugreports.qt.io/browse/QTBUG-56805
//
// so we avoid querying it here.
bool WebPage::acceptNavigationRequest(const QUrl &url,
                                      NavigationType /* navType*/,
                                      bool isMainFrame)
{
   if (url.toString() == QStringLiteral("about:blank"))
      return true;

   if (url.scheme() != QStringLiteral("http") &&
       url.scheme() != QStringLiteral("https") &&
       url.scheme() != QStringLiteral("mailto") &&
       url.scheme() != QStringLiteral("data"))
   {
      qDebug() << url.toString();
      return false;
   }

   // determine if this is a local request (handle internally only if local)
   std::string host = url.host().toStdString();
   bool isLocal = host == "localhost" || host == "127.0.0.1";

   bool isSubFrame = false;
   WebProfile* webProfile = dynamic_cast<WebProfile*>(profile());
   if (webProfile)
   {
      isSubFrame = webProfile->resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubFrame;
   }

   if ((baseUrl_.isEmpty() && isLocal) ||
       (url.scheme() == baseUrl_.scheme() &&
        url.host() == baseUrl_.host() &&
        url.port() == baseUrl_.port()))
   {
      return true;
   }
   // allow viewer urls to be handled internally by Qt. note that the client is responsible for
   // ensuring that non-local viewer urls are appropriately sandboxed.
   else if (!viewerUrl().isEmpty() &&
            url.toString().startsWith(viewerUrl()))
   {
      return true;
   }
   // allow shiny dialog urls to be handled internally by Qt
   else if (isLocal && !shinyDialogUrl_.isEmpty() &&
            url.toString().startsWith(shinyDialogUrl_))
   {
      return true;
   }
   else
   {
      bool navigated = false;

      if (allowExternalNav_)
      {
         // if allowing external navigation, follow this (even if a link click)
         return true;
      }
      else if (boost::algorithm::ends_with(host, ".youtube.com") ||
               boost::algorithm::ends_with(host, ".vimeo.com")   ||
               boost::algorithm::ends_with(host, ".c9.ms"))
      {
         return true;
      }
      else if (!isSubFrame)
      {
         // when not allowing external navigation, open an external browser
         // to view the URL
         desktop::openUrl(url);
         navigated = true;
      }

      if (!navigated)
         this->view()->window()->deleteLater();

      return false;
   }
}

void WebPage::setViewerUrl(const QString& viewerUrl)
{
   // record about:blank literally
   if (viewerUrl == QString::fromUtf8("about:blank"))
   {
      viewerUrl_ = viewerUrl;
      return;
   }

   // extract the authority (domain and port) from the URL; we'll agree to
   // serve requests for the viewer pane that match this prefix.
   QUrl url(viewerUrl);
   viewerUrl_ = url.scheme() + QString::fromUtf8("://") +
                url.authority() + QString::fromUtf8("/");
}

QString WebPage::viewerUrl()
{
   if (viewerUrl_.isEmpty())
   {
      // if we don't know the viewer URL ourselves but we're a child window, ask our parent
      BrowserWindow *parent = dynamic_cast<BrowserWindow*>(view()->window());
      if (parent != nullptr && parent->opener() != nullptr)
      {
         return parent->opener()->viewerUrl();
      }
   }

   // return our own viewer URL
   return viewerUrl_;
}

void WebPage::setShinyDialogUrl(const QString &shinyDialogUrl)
{
   shinyDialogUrl_ = shinyDialogUrl;
}

void WebPage::triggerAction(WebAction action, bool checked)
{
   QWebEnginePage::triggerAction(action, checked);
}

} // namespace desktop
} // namespace rstudio
<|endoftext|>