<commit_before>// $Id$
// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// Local includes
#include "elem.h"
#include "fe.h"
#include "fe_macro.h"

namespace libMesh
{

// ------------------------------------------------------------
// Hierarchic-specific implementations
template <unsigned int Dim, FEFamily T>
void FE<Dim,T>::nodal_soln(const Elem* elem,
			   const Order order,
			   const std::vector<Number>& elem_soln,
			   std::vector<Number>&       nodal_soln)
{
  const unsigned int n_nodes = elem->n_nodes();

  const ElemType type = elem->type();

  nodal_soln.resize(n_nodes);

  const Order totalorder = static_cast<Order>(order + elem->p_level());

  switch (totalorder)
    {
      // Constant shape functions
    case CONSTANT:
      {
	libmesh_assert (elem_soln.size() == 1);

	const Number val = elem_soln[0];

	for (unsigned int n=0; n<n_nodes; n++)
	  nodal_soln[n] = val;

	return;
      }

      // For other bases do interpolation at the nodes
      // explicitly.
    default:
      {

	const unsigned int n_sf =
	  FE<Dim,T>::n_shape_functions(type, totalorder);

	for (unsigned int n=0; n<n_nodes; n++)
	  {
	    const Point mapped_point = FE<Dim,T>::inverse_map(elem,
							      elem->point(n));

	    libmesh_assert (elem_soln.size() == n_sf);

	    // Zero before summation
	    nodal_soln[n] = 0;

	    // u_i = Sum (alpha_i phi_i)
	    for (unsigned int i=0; i<n_sf; i++)
	      nodal_soln[n] += elem_soln[i]*FE<Dim,T>::shape(elem,
							     order,
							     i,
							     mapped_point);
	  }

	return;
      }
    }
}

template <unsigned int Dim, FEFamily T>
unsigned int FE<Dim,T>::n_dofs(const ElemType t, const Order o)
{
  libmesh_assert (o > 0);
  switch (t)
    {
    case NODEELEM:
      return 1;
    case EDGE2:
    case EDGE3:
      return (o+1);
    case QUAD4:
      libmesh_assert(o < 2);
    case QUAD8:
    case QUAD9:
      return ((o+1)*(o+1));
    case HEX8:
      libmesh_assert(o < 2);
    case HEX20:
      libmesh_assert(o < 2);
    case HEX27:
      return ((o+1)*(o+1)*(o+1));
    case TRI6:
      return ((o+1)*(o+2)/2);
    default:
      libmesh_error();
    }

  libmesh_error();
  return 0;
}

template <unsigned int Dim, FEFamily T>
unsigned int FE<Dim,T>::n_dofs_at_node(const ElemType t,
				       const Order o,
				       const unsigned int n)
{
  libmesh_assert (o > 0);
  switch (t)
    {
    case NODEELEM:
      return 1;
    case EDGE2:
    case EDGE3:
      switch (n)
        {
	case 0:
	case 1:
	  return 1;
        // Internal DoFs are associated with the elem, not its nodes
	case 2:
	  return 0;
	default:
	  libmesh_error();
	}
    case TRI6:
      switch (n)
	{
	case 0:
	case 1:
	case 2:
	  return 1;

	case 3:
	case 4:
	case 5:
	  return (o-1);

        // Internal DoFs are associated with the elem, not its nodes
	default:
	  libmesh_error();
	}
    case QUAD4:
      libmesh_assert (n < 4);
      libmesh_assert (o < 2);
    case QUAD8:
    case QUAD9:
      switch (n)
	{
	case 0:
	case 1:
	case 2:
	case 3:
	  return 1;

	case 4:
	case 5:
	case 6:
	case 7:
	  return (o-1);

        // Internal DoFs are associated with the elem, not its nodes
	case 8:
          return 0;

	default:
	  libmesh_error();
	}
    case HEX8:
      libmesh_assert (n < 8);
      libmesh_assert (o < 2);
    case HEX20:
      libmesh_assert (n < 20);
      libmesh_assert (o < 2);
    case HEX27:
      switch (n)
	{
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	  return 1;

	case 8:
	case 9:
	case 10:
	case 11:
	case 12:
	case 13:
	case 14:
	case 15:
	case 16:
	case 17:
	case 18:
	case 19:
	  return (o-1);

	case 20:
	case 21:
	case 22:
	case 23:
	case 24:
	case 25:
	  return ((o-1)*(o-1));

        // Internal DoFs are associated with the elem, not its nodes
	case 26:
	  return 0;
	default:
	  libmesh_error();
        }
    default:
#ifdef DEBUG
      libMesh::err << "ERROR: Bad ElemType = " << t
		    << std::endl;
#endif
      libmesh_error();
    }

  libmesh_error();

  return 0;
}

template <unsigned int Dim, FEFamily T>
unsigned int FE<Dim,T>::n_dofs_per_elem(const ElemType t,
					const Order o)
{
  libmesh_assert (o > 0);
  switch (t)
    {
    case NODEELEM:
      return 0;
    case EDGE2:
    case EDGE3:
      return (o-1);
    case TRI3:
    case QUAD4:
      return 0;
    case TRI6:
      return ((o-1)*(o-2)/2);
    case QUAD8:
    case QUAD9:
      return ((o-1)*(o-1));
    case HEX8:
    case HEX20:
      libmesh_assert(o < 2);
      return 0;
    case HEX27:
      return ((o-1)*(o-1)*(o-1));
    default:
#ifdef DEBUG
      libMesh::err << "ERROR: Bad ElemType = " << t
		    << std::endl;
#endif
      libmesh_error();
    }

  // Will never get here...
  libmesh_error();
  return 0;
}

template <unsigned int Dim, FEFamily T>
FEContinuity FE<Dim,T>::get_continuity() const
{
  return C_ZERO;
}

template <unsigned int Dim, FEFamily T>
bool FE<Dim,T>::is_hierarchic() const
{
  return true;
}

#ifdef LIBMESH_ENABLE_AMR
template <unsigned int Dim, FEFamily T>
void FE<Dim,T>::compute_constraints (DofConstraints &constraints,
				     DofMap &dof_map,
				     const unsigned int variable_number,
				     const Elem* elem)
{
  compute_proj_constraints(constraints, dof_map, variable_number, elem);
}
#endif // #ifdef LIBMESH_ENABLE_AMR

template <unsigned int Dim, FEFamily T>
bool FE<Dim,T>::shapes_need_reinit() const
{
  return true;
}

//--------------------------------------------------------------
// Explicit instantiation of member functions
INSTANTIATE_MBRF(0,HIERARCHIC);
INSTANTIATE_MBRF(1,HIERARCHIC);
INSTANTIATE_MBRF(2,HIERARCHIC);
INSTANTIATE_MBRF(3,HIERARCHIC);

#ifdef LIBMESH_ENABLE_AMR
template void FE<2,HIERARCHIC>::compute_constraints(DofConstraints&, DofMap&,
						    const unsigned int,
						    const Elem*);
template void FE<3,HIERARCHIC>::compute_constraints(DofConstraints&, DofMap&,
						    const unsigned int,
						    const Elem*);
#endif // #ifdef LIBMESH_ENABLE_AMR

} // namespace libMesh
<commit_msg>Refactoring fe_hierarchic.C functions to use specialization instead of instantiation.<commit_after>// $Id$
// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// Local includes
#include "elem.h"
#include "fe.h"
#include "fe_macro.h"
#include "fe_interface.h"

namespace libMesh
{

  // ------------------------------------------------------------
  // Hierarchic-specific implementations

  // Anonymous namespace for local helper functions
  namespace {

    void hierarchic_nodal_soln(const Elem* elem,
			       const Order order,
			       const std::vector<Number>& elem_soln,
			       std::vector<Number>&       nodal_soln,
			       unsigned Dim)
    {
      const unsigned int n_nodes = elem->n_nodes();

      const ElemType elem_type = elem->type();

      nodal_soln.resize(n_nodes);

      const Order totalorder = static_cast<Order>(order + elem->p_level());

      // FEType object to be passed to various FEInterface functions below.
      FEType fe_type(totalorder, HIERARCHIC);

      switch (totalorder)
	{
	  // Constant shape functions
	case CONSTANT:
	  {
	    libmesh_assert (elem_soln.size() == 1);

	    const Number val = elem_soln[0];

	    for (unsigned int n=0; n<n_nodes; n++)
	      nodal_soln[n] = val;

	    return;
	  }

	  // For other orders do interpolation at the nodes
	  // explicitly.
	default:
	  {

	    const unsigned int n_sf =
	      // FE<Dim,T>::n_shape_functions(elem_type, totalorder);
	      FEInterface::n_shape_functions(Dim, fe_type, elem_type);

	    for (unsigned int n=0; n<n_nodes; n++)
	      {
		const Point mapped_point =
		  // FE<Dim,T>::inverse_map(elem, elem->point(n));
		  FEInterface::inverse_map(Dim, fe_type, elem, elem->point(n));

		libmesh_assert (elem_soln.size() == n_sf);

		// Zero before summation
		nodal_soln[n] = 0;

		// u_i = Sum (alpha_i phi_i)
		for (unsigned int i=0; i<n_sf; i++)
		  nodal_soln[n] += elem_soln[i] *
		    // FE<Dim,T>::shape(elem, order, i, mapped_point);
		    FEInterface::shape(Dim, fe_type, elem, i, mapped_point);
	      }

	    return;
	  }
	}
    } // hierarchic_nodal_soln()

    unsigned int hierarchic_n_dofs(const ElemType t, const Order o)
    {
      libmesh_assert (o > 0);
      switch (t)
	{
	case NODEELEM:
	  return 1;
	case EDGE2:
	case EDGE3:
	  return (o+1);
	case QUAD4:
	  libmesh_assert(o < 2);
	case QUAD8:
	case QUAD9:
	  return ((o+1)*(o+1));
	case HEX8:
	  libmesh_assert(o < 2);
	case HEX20:
	  libmesh_assert(o < 2);
	case HEX27:
	  return ((o+1)*(o+1)*(o+1));
	case TRI6:
	  return ((o+1)*(o+2)/2);
	default:
	  libmesh_error();
	}

      libmesh_error();
      return 0;
    } // hierarchic_n_dofs()

    unsigned int hierarchic_n_dofs_at_node(const ElemType t,
					   const Order o,
					   const unsigned int n)
    {
      libmesh_assert (o > 0);
      switch (t)
	{
	case NODEELEM:
	  return 1;
	case EDGE2:
	case EDGE3:
	  switch (n)
	    {
	    case 0:
	    case 1:
	      return 1;
	      // Internal DoFs are associated with the elem, not its nodes
	    case 2:
	      return 0;
	    default:
	      libmesh_error();
	    }
	case TRI6:
	  switch (n)
	    {
	    case 0:
	    case 1:
	    case 2:
	      return 1;

	    case 3:
	    case 4:
	    case 5:
	      return (o-1);

	      // Internal DoFs are associated with the elem, not its nodes
	    default:
	      libmesh_error();
	    }
	case QUAD4:
	  libmesh_assert (n < 4);
	  libmesh_assert (o < 2);
	case QUAD8:
	case QUAD9:
	  switch (n)
	    {
	    case 0:
	    case 1:
	    case 2:
	    case 3:
	      return 1;

	    case 4:
	    case 5:
	    case 6:
	    case 7:
	      return (o-1);

	      // Internal DoFs are associated with the elem, not its nodes
	    case 8:
	      return 0;

	    default:
	      libmesh_error();
	    }
	case HEX8:
	  libmesh_assert (n < 8);
	  libmesh_assert (o < 2);
	case HEX20:
	  libmesh_assert (n < 20);
	  libmesh_assert (o < 2);
	case HEX27:
	  switch (n)
	    {
	    case 0:
	    case 1:
	    case 2:
	    case 3:
	    case 4:
	    case 5:
	    case 6:
	    case 7:
	      return 1;

	    case 8:
	    case 9:
	    case 10:
	    case 11:
	    case 12:
	    case 13:
	    case 14:
	    case 15:
	    case 16:
	    case 17:
	    case 18:
	    case 19:
	      return (o-1);

	    case 20:
	    case 21:
	    case 22:
	    case 23:
	    case 24:
	    case 25:
	      return ((o-1)*(o-1));

	      // Internal DoFs are associated with the elem, not its nodes
	    case 26:
	      return 0;
	    default:
	      libmesh_error();
	    }
	default:
#ifdef DEBUG
	  libMesh::err << "ERROR: Bad ElemType = " << t
		       << std::endl;
#endif
	  libmesh_error();
	}

      libmesh_error();

      return 0;
    } // hierarchic_n_dofs_at_node()

    unsigned int hierarchic_n_dofs_per_elem(const ElemType t,
					    const Order o)
    {
      libmesh_assert (o > 0);
      switch (t)
	{
	case NODEELEM:
	  return 0;
	case EDGE2:
	case EDGE3:
	  return (o-1);
	case TRI3:
	case QUAD4:
	  return 0;
	case TRI6:
	  return ((o-1)*(o-2)/2);
	case QUAD8:
	case QUAD9:
	  return ((o-1)*(o-1));
	case HEX8:
	case HEX20:
	  libmesh_assert(o < 2);
	  return 0;
	case HEX27:
	  return ((o-1)*(o-1)*(o-1));
	default:
#ifdef DEBUG
	  libMesh::err << "ERROR: Bad ElemType = " << t
		       << std::endl;
#endif
	  libmesh_error();
	}

      // Will never get here...
      libmesh_error();
      return 0;
    } // hierarchic_n_dofs_per_elem()

  } // anonymous namespace

  // Do full-specialization of nodal_soln() function for every
  // dimension, instead of explicit instantiation at the end of this
  // file.
  // This could be macro-ified so that it fits on one line...
  template <>
  void FE<0,HIERARCHIC>::nodal_soln(const Elem* elem,
				  const Order order,
				  const std::vector<Number>& elem_soln,
				  std::vector<Number>& nodal_soln)
  { hierarchic_nodal_soln(elem, order, elem_soln, nodal_soln, /*Dim=*/0); }

  template <>
  void FE<1,HIERARCHIC>::nodal_soln(const Elem* elem,
				  const Order order,
				  const std::vector<Number>& elem_soln,
				  std::vector<Number>& nodal_soln)
  { hierarchic_nodal_soln(elem, order, elem_soln, nodal_soln, /*Dim=*/1); }

  template <>
  void FE<2,HIERARCHIC>::nodal_soln(const Elem* elem,
				  const Order order,
				  const std::vector<Number>& elem_soln,
				  std::vector<Number>& nodal_soln)
  { hierarchic_nodal_soln(elem, order, elem_soln, nodal_soln, /*Dim=*/2); }

  template <>
  void FE<3,HIERARCHIC>::nodal_soln(const Elem* elem,
				  const Order order,
				  const std::vector<Number>& elem_soln,
				  std::vector<Number>& nodal_soln)
  { hierarchic_nodal_soln(elem, order, elem_soln, nodal_soln, /*Dim=*/3); }

  // Full specialization of n_dofs() function for every dimension
  template <> unsigned int FE<0,HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return hierarchic_n_dofs(t, o); }
  template <> unsigned int FE<1,HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return hierarchic_n_dofs(t, o); }
  template <> unsigned int FE<2,HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return hierarchic_n_dofs(t, o); }
  template <> unsigned int FE<3,HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return hierarchic_n_dofs(t, o); }

  // Full specialization of n_dofs_at_node() function for every dimension.
  template <> unsigned int FE<0,HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return hierarchic_n_dofs_at_node(t, o, n); }
  template <> unsigned int FE<1,HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return hierarchic_n_dofs_at_node(t, o, n); }
  template <> unsigned int FE<2,HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return hierarchic_n_dofs_at_node(t, o, n); }
  template <> unsigned int FE<3,HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return hierarchic_n_dofs_at_node(t, o, n); }

  // Full specialization of n_dofs_per_elem() function for every dimension.
  template <> unsigned int FE<0,HIERARCHIC>::n_dofs_per_elem(const ElemType t, const Order o) { return hierarchic_n_dofs_per_elem(t, o); }
  template <> unsigned int FE<1,HIERARCHIC>::n_dofs_per_elem(const ElemType t, const Order o) { return hierarchic_n_dofs_per_elem(t, o); }
  template <> unsigned int FE<2,HIERARCHIC>::n_dofs_per_elem(const ElemType t, const Order o) { return hierarchic_n_dofs_per_elem(t, o); }
  template <> unsigned int FE<3,HIERARCHIC>::n_dofs_per_elem(const ElemType t, const Order o) { return hierarchic_n_dofs_per_elem(t, o); }

  // Hierarchic FEMs are C^0 continuous
  template <> FEContinuity FE<0,HIERARCHIC>::get_continuity() const { return C_ZERO; }
  template <> FEContinuity FE<1,HIERARCHIC>::get_continuity() const { return C_ZERO; }
  template <> FEContinuity FE<2,HIERARCHIC>::get_continuity() const { return C_ZERO; }
  template <> FEContinuity FE<3,HIERARCHIC>::get_continuity() const { return C_ZERO; }

  // Hierarchic FEMs are hierarchic (duh!)
  template <> bool FE<0,HIERARCHIC>::is_hierarchic() const { return true; }
  template <> bool FE<1,HIERARCHIC>::is_hierarchic() const { return true; }
  template <> bool FE<2,HIERARCHIC>::is_hierarchic() const { return true; }
  template <> bool FE<3,HIERARCHIC>::is_hierarchic() const { return true; }

#ifdef LIBMESH_ENABLE_AMR
  // compute_constraints() specializations are only needed for 2 and 3D
  template <>
  void FE<2,HIERARCHIC>::compute_constraints (DofConstraints &constraints,
					      DofMap &dof_map,
					      const unsigned int variable_number,
					      const Elem* elem)
  { compute_proj_constraints(constraints, dof_map, variable_number, elem); }

  template <>
  void FE<3,HIERARCHIC>::compute_constraints (DofConstraints &constraints,
					      DofMap &dof_map,
					      const unsigned int variable_number,
					      const Elem* elem)
  { compute_proj_constraints(constraints, dof_map, variable_number, elem); }
#endif // #ifdef LIBMESH_ENABLE_AMR

  // Hierarchic FEM shapes need reinit
  template <> bool FE<0,HIERARCHIC>::shapes_need_reinit() const { return true; }
  template <> bool FE<1,HIERARCHIC>::shapes_need_reinit() const { return true; }
  template <> bool FE<2,HIERARCHIC>::shapes_need_reinit() const { return true; }
  template <> bool FE<3,HIERARCHIC>::shapes_need_reinit() const { return true; }

} // namespace libMesh
<|endoftext|>


<commit_before>// Halide tutorial lesson 20: Cloning Funcs

// This lesson demonstrates how to use Func::clone_in to create a clone of
// a Func.

// On linux, you can compile and run it like so:
// g++ lesson_20*.cpp -g -I ../include -L ../bin -lHalide -lpthread -ldl -o lesson_20 -std=c++11
// LD_LIBRARY_PATH=../bin ./lesson_20

// On os x:
// g++ lesson_20*.cpp -g -I ../include -L ../bin -lHalide -o lesson_20 -std=c++11
// DYLD_LIBRARY_PATH=../bin ./lesson_20

// If you have the entire Halide source tree, you can also build it by
// running:
//    make tutorial_lesson_20_cloning_funcs
// in a shell at the top of the halide source tree.

// The only Halide header file you need is Halide.h. It includes all of Halide.
#include "Halide.h"

// We'll also include stdio for printf.
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    // First we'll declare some Vars to use below.
    Var x("x"), y("y"), xo("xo"), yo("yo"), xi("xi"), yi("yi");

    // This lesson will be about cloning a Func using the Func::clone_in
    // directive
    {
        // Consider a simple two-stage pipeline:
        Func f("f_single"), g("g_single"), h("h_single");
        f(x, y) = x + y;
        g(x, y) = 2 * f(x, y) + 3;
        h(x, y) = f(x, y) + g(x, y) + 10;

        f.compute_root();
        g.compute_root();
        h.compute_root();

        // This produces the following loop nests:
        // for y:
        //   for x:
        //     f(x, y) = x + y
        // for y:
        //   for x:
        //     g(x, y) = 2 * f(x, y) + 3
        // for y:
        //   for x:
        //     h(x, y) = f(x, y) + g(x, y) + 10

        // Using Func::clone, we can replace calls to 'f' inside 'g' with
        // a clone of 'f' using the schedule alone:
        Func f_clone_in_g = f.clone_in(g);
        f_clone_in_g.compute_root();

        // Equivalently, we could also chain the schedules like so:
        // f.clone_in(g).compute_root();

        // This produces the following loop nests:
        // for y:
        //   for x:
        //     f(x, y) = x + y
        // for y:
        //   for x:
        //     f_clone_in_g(x, y) = x + y
        // for y:
        //   for x:
        //     g(x, y) = 2 * f_clone_in_g(x, y) + 3
        // for y:
        //   for x:
        //     h(x, y) = f(x, y) + g(x, y) + 10

        h.realize(5, 5);

        // The schedule directive f.clone_in(g) replaces all calls to 'f'
        // inside 'g' with a clone of 'f' and then returns that clone.
        // Essentially, it rewrites the original pipeline above into the
        // following:
        {
            Func f_clone_in_g("f_clone_in_g"), f("f"), g("g"), h("h");
            f(x, y) = x + y;
            f_clone_in_g(x, y) = x + y;
            g(x, y) = 2 * f_clone_in_g(x, y) + 3;
            h(x, y) = f(x, y) + g(x, y) + 10;

            f.compute_root();
            f_clone_in_g.compute_root();
            g.compute_root();
            h.compute_root();
        }
    }

    {
        // In the schedule above, only the calls to 'f' made by 'g' are
        // replaced. Other calls made to 'f' would still call 'f' directly
        // (i.e. 'h' still calls 'f' and not the clone). If we wish to
        // replace all calls to 'f' made by both 'g' and 'h' with a single
        // clone, we simply say f.clone_in({g, h}).

        // Consider a three stage pipeline, with two consumers of f:
        Func f("f_group"), g("g_group"), h("h_group"), out("out_group");
        f(x, y) = x + y;
        g(x, y) = 2 * f(x, y);
        h(x, y) = f(x, y) + 10;
        out(x, y) = f(x, y) + g(x, y) + h(x, y);

        f.compute_root();
        g.compute_root();
        h.compute_root();
        out.compute_root();

        // We will replace all calls to 'f' inside both 'g' and 'h'
        // with calls to a single clone:
        f.clone_in({g, h}).compute_root();

        // The equivalent loop nests are:
        // for y:
        //   for x:
        //     f(x, y) = x + y
        // for y:
        //   for x:
        //     f_clone(x, y) = x + y
        // for y:
        //   for x:
        //     g(x, y) = 2 * f_clone(x, y)
        // for y:
        //   for x:
        //     h(x, y) = f_clone(x, y) + 10
        // for y:
        //   for x:
        //     out(x, y) = f(x, y) + g(x, y) + h(x, y)

        out.realize(5, 5);
    }

    {
        // One use case of Func::clone_in() is when two consumers of a producer
        // consume regions of the producer that are very disjoint. Consider
        // the following case for example:
        Func f("f"), g("g"), h("h");
        f(x) = x;
        g(x) = 2 * f(0);
        h(x) = f(99) + 10;

        // Let's schedule 'f' to be computed at root.
        f.compute_root();
        // Since both 'g' and 'h' consumes 'f', the region required of 'f'
        // in the x-dimension is [0, 99]. The equivalent loop nests are:
        // for x = 0 to 99
        //   f(x) = x
        // for x:
        //   g(x) = 2 * f(0)
        // for x:
        //   h(x) = f(99) + 10

        // If 'f' had been very expensive to compute, we might be beter off
        // with having two copies of 'f' for each consumer, 'g' and 'h', to
        // avoid unnecessary computations. To create separate copies of 'f'
        // for each consumer, we can do the following:
        f.clone_in(g).compute_root();

        // The equivalent loop nests are:
        // f(0) = x
        // f_clone(99) = x
        // for x:
        //   g(x) = 2 * f_clone(0)
        // for x:
        //   h(x) = f(99) + 10
    }

    printf("Success!\n");

    return 0;
}
<commit_msg>Fix typos in clone_in tutorial<commit_after>// Halide tutorial lesson 20: Cloning Funcs

// This lesson demonstrates how to use Func::clone_in to create a clone of
// a Func.

// On linux, you can compile and run it like so:
// g++ lesson_20*.cpp -g -I ../include -L ../bin -lHalide -lpthread -ldl -o lesson_20 -std=c++11
// LD_LIBRARY_PATH=../bin ./lesson_20

// On os x:
// g++ lesson_20*.cpp -g -I ../include -L ../bin -lHalide -o lesson_20 -std=c++11
// DYLD_LIBRARY_PATH=../bin ./lesson_20

// If you have the entire Halide source tree, you can also build it by
// running:
//    make tutorial_lesson_20_cloning_funcs
// in a shell at the top of the halide source tree.

// The only Halide header file you need is Halide.h. It includes all of Halide.
#include "Halide.h"

// We'll also include stdio for printf.
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    // First we'll declare some Vars to use below.
    Var x("x"), y("y"), xo("xo"), yo("yo"), xi("xi"), yi("yi");

    // This lesson will be about cloning a Func using the Func::clone_in
    // directive
    {
        // Consider a simple two-stage pipeline:
        Func f("f_single"), g("g_single"), h("h_single");
        f(x, y) = x + y;
        g(x, y) = 2 * f(x, y) + 3;
        h(x, y) = f(x, y) + g(x, y) + 10;

        f.compute_root();
        g.compute_root();
        h.compute_root();

        // This produces the following loop nests:
        // for y:
        //   for x:
        //     f(x, y) = x + y
        // for y:
        //   for x:
        //     g(x, y) = 2 * f(x, y) + 3
        // for y:
        //   for x:
        //     h(x, y) = f(x, y) + g(x, y) + 10

        // Using Func::clone, we can replace calls to 'f' inside 'g' with
        // a clone of 'f' using the schedule alone:
        Func f_clone_in_g = f.clone_in(g);
        f_clone_in_g.compute_root();

        // Equivalently, we could also chain the schedules like so:
        // f.clone_in(g).compute_root();

        // This produces the following loop nests:
        // for y:
        //   for x:
        //     f(x, y) = x + y
        // for y:
        //   for x:
        //     f_clone_in_g(x, y) = x + y
        // for y:
        //   for x:
        //     g(x, y) = 2 * f_clone_in_g(x, y) + 3
        // for y:
        //   for x:
        //     h(x, y) = f(x, y) + g(x, y) + 10

        h.realize(5, 5);

        // The schedule directive f.clone_in(g) replaces all calls to 'f'
        // inside 'g' with a clone of 'f' and then returns that clone.
        // Essentially, it rewrites the original pipeline above into the
        // following:
        {
            Func f_clone_in_g("f_clone_in_g"), f("f"), g("g"), h("h");
            f(x, y) = x + y;
            f_clone_in_g(x, y) = x + y;
            g(x, y) = 2 * f_clone_in_g(x, y) + 3;
            h(x, y) = f(x, y) + g(x, y) + 10;

            f.compute_root();
            f_clone_in_g.compute_root();
            g.compute_root();
            h.compute_root();
        }
    }

    {
        // In the schedule above, only the calls to 'f' made by 'g' are
        // replaced. Other calls made to 'f' would still call 'f' directly
        // (i.e. 'h' still calls 'f' and not the clone). If we wish to
        // replace all calls to 'f' made by both 'g' and 'h' with a single
        // clone, we simply say f.clone_in({g, h}).

        // Consider a three stage pipeline, with two consumers of f:
        Func f("f_group"), g("g_group"), h("h_group"), out("out_group");
        f(x, y) = x + y;
        g(x, y) = 2 * f(x, y);
        h(x, y) = f(x, y) + 10;
        out(x, y) = f(x, y) + g(x, y) + h(x, y);

        f.compute_root();
        g.compute_root();
        h.compute_root();
        out.compute_root();

        // We will replace all calls to 'f' inside both 'g' and 'h'
        // with calls to a single clone:
        f.clone_in({g, h}).compute_root();

        // The equivalent loop nests are:
        // for y:
        //   for x:
        //     f(x, y) = x + y
        // for y:
        //   for x:
        //     f_clone(x, y) = x + y
        // for y:
        //   for x:
        //     g(x, y) = 2 * f_clone(x, y)
        // for y:
        //   for x:
        //     h(x, y) = f_clone(x, y) + 10
        // for y:
        //   for x:
        //     out(x, y) = f(x, y) + g(x, y) + h(x, y)

        out.realize(5, 5);
    }

    {
        // One use case of Func::clone_in() is when two consumers of a producer
        // consume regions of the producer that are very disjoint. Consider
        // the following case for example:
        Func f("f"), g("g"), h("h");
        f(x) = x;
        g(x) = 2 * f(0);
        h(x) = f(99) + 10;

        // Let's schedule 'f' to be computed at root.
        f.compute_root();
        // Since both 'g' and 'h' consume 'f', the region required of 'f'
        // in the x-dimension is [0, 99]. The equivalent loop nests are:
        // for x = 0 to 99
        //   f(x) = x
        // for x:
        //   g(x) = 2 * f(0)
        // for x:
        //   h(x) = f(99) + 10

        // If 'f' is very expensive to compute, we might be better off with
        // having distinct copies of 'f' for each consumer, 'g' and 'h', to
        // avoid unnecessary computations. To create separate copies of 'f'
        // for each consumer, we can do the following:
        f.clone_in(g).compute_root();

        // The equivalent loop nests are:
        // f(0) = x
        // f_clone(99) = x
        // for x:
        //   g(x) = 2 * f_clone(0)
        // for x:
        //   h(x) = f(99) + 10
    }

    printf("Success!\n");

    return 0;
}
<|endoftext|>


<commit_before>// ======================================================================== //
// Copyright 2018-2019 Intel Corporation                                    //
//                                                                          //
// 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 <iterator>
#include <memory>
#include <random>
#include "GLFWOSPRayWindow.h"

#include "ospray_testing.h"

#include <imgui.h>

using namespace ospcommon;

OSPGeometry createGroundPlaneGeometry()
{
  OSPGeometry planeGeometry = ospNewGeometry("quads");

  struct Vertex
  {
    vec3f position;
    vec3f normal;
    vec4f color;
  };

  struct QuadIndex
  {
    int x;
    int y;
    int z;
    int w;
  };

  std::vector<Vertex> vertices;
  std::vector<QuadIndex> quadIndices;

  // ground plane
  int startingIndex = vertices.size();

  // extent of plane in the (x, z) directions
  const float planeExtent = 1.5f;

  const vec3f up   = vec3f{0.f, 1.f, 0.f};
  const vec4f gray = vec4f{0.9f, 0.9f, 0.9f, 0.75f};

  vertices.push_back(Vertex{vec3f{-planeExtent, -1.f, -planeExtent}, up, gray});
  vertices.push_back(Vertex{vec3f{planeExtent, -1.f, -planeExtent}, up, gray});
  vertices.push_back(Vertex{vec3f{planeExtent, -1.f, planeExtent}, up, gray});
  vertices.push_back(Vertex{vec3f{-planeExtent, -1.f, planeExtent}, up, gray});

  quadIndices.push_back(QuadIndex{
      startingIndex, startingIndex + 1, startingIndex + 2, startingIndex + 3});

  // stripes on ground plane
  const float stripeWidth  = 0.025f;
  const float paddedExtent = planeExtent + stripeWidth;
  const size_t numStripes  = 10;

  const vec4f stripeColor = vec4f{1.0f, 0.1f, 0.1f, 1.f};

  for (size_t i = 0; i < numStripes; i++) {
    // the center coordinate of the stripe, either in the x or z direction
    const float coord =
        -planeExtent + float(i) / float(numStripes - 1) * 2.f * planeExtent;

    // offset the stripes by an epsilon above the ground plane
    const float yLevel = -1.f + 1e-3f;

    // x-direction stripes
    startingIndex = vertices.size();

    vertices.push_back(Vertex{
        vec3f{-paddedExtent, yLevel, coord - stripeWidth}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{paddedExtent, yLevel, coord - stripeWidth}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{paddedExtent, yLevel, coord + stripeWidth}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{-paddedExtent, yLevel, coord + stripeWidth}, up, stripeColor});

    quadIndices.push_back(QuadIndex{startingIndex,
                                    startingIndex + 1,
                                    startingIndex + 2,
                                    startingIndex + 3});

    // z-direction stripes
    startingIndex = vertices.size();

    vertices.push_back(Vertex{
        vec3f{coord - stripeWidth, yLevel, -paddedExtent}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{coord + stripeWidth, yLevel, -paddedExtent}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{coord + stripeWidth, yLevel, paddedExtent}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{coord - stripeWidth, yLevel, paddedExtent}, up, stripeColor});

    quadIndices.push_back(QuadIndex{startingIndex,
                                    startingIndex + 1,
                                    startingIndex + 2,
                                    startingIndex + 3});
  }

  // create OSPRay data objects
  std::vector<vec3f> positionVector;
  std::vector<vec3f> normalVector;
  std::vector<vec4f> colorVector;

  std::transform(vertices.begin(),
                 vertices.end(),
                 std::back_inserter(positionVector),
                 [](Vertex const &v) { return v.position; });
  std::transform(vertices.begin(),
                 vertices.end(),
                 std::back_inserter(normalVector),
                 [](Vertex const &v) { return v.normal; });
  std::transform(vertices.begin(),
                 vertices.end(),
                 std::back_inserter(colorVector),
                 [](Vertex const &v) { return v.color; });

  OSPData positionData =
      ospNewData(vertices.size(), OSP_FLOAT3, positionVector.data());
  OSPData normalData =
      ospNewData(vertices.size(), OSP_FLOAT3, normalVector.data());
  OSPData colorData =
      ospNewData(vertices.size(), OSP_FLOAT4, colorVector.data());
  OSPData indexData =
      ospNewData(quadIndices.size(), OSP_INT4, quadIndices.data());

  // set vertex / index data on the geometry
  ospSetData(planeGeometry, "vertex", positionData);
  ospSetData(planeGeometry, "vertex.normal", normalData);
  ospSetData(planeGeometry, "vertex.color", colorData);
  ospSetData(planeGeometry, "index", indexData);

  // create and assign a material to the geometry
  OSPMaterial material = ospNewMaterial2("scivis", "OBJMaterial");
  ospCommit(material);

  ospSetMaterial(planeGeometry, material);

  // finally, commit the geometry
  ospCommit(planeGeometry);

  // release handles we no longer need
  ospRelease(positionData);
  ospRelease(normalData);
  ospRelease(colorData);
  ospRelease(indexData);
  ospRelease(material);

  return planeGeometry;
}

int main(int argc, const char **argv)
{
  // initialize OSPRay; OSPRay parses (and removes) its commandline parameters,
  // e.g. "--osp:debug"
  OSPError initError = ospInit(&argc, argv);

  if (initError != OSP_NO_ERROR)
    return initError;

  // set an error callback to catch any OSPRay errors and exit the application
  ospDeviceSetErrorFunc(
      ospGetCurrentDevice(), [](OSPError error, const char *errorDetails) {
        std::cerr << "OSPRay error: " << errorDetails << std::endl;
        exit(error);
      });

  // create the "world" model which will contain all of our geometries
  OSPModel world = ospNewModel();

  // add in subdivision geometry
  OSPTestingGeometry subdivisionGeometry =
      ospTestingNewGeometry("subdivision_cube", "scivis");
  ospAddGeometry(world, subdivisionGeometry.geometry);
  ospRelease(subdivisionGeometry.geometry);

  // add in a ground plane geometry
  ospAddGeometry(world, createGroundPlaneGeometry());

  // commit the world model
  ospCommit(world);

  // create OSPRay renderer
  OSPRenderer renderer = ospNewRenderer("scivis");

  OSPData lightsData = ospTestingNewLights("ambient_and_directional");
  ospSetData(renderer, "lights", lightsData);
  ospRelease(lightsData);

  ospCommit(renderer);

  // create a GLFW OSPRay window: this object will create and manage the OSPRay
  // frame buffer and camera directly
  auto glfwOSPRayWindow =
      std::unique_ptr<GLFWOSPRayWindow>(new GLFWOSPRayWindow(
          vec2i{1024, 768}, box3f(vec3f(-1.f), vec3f(1.f)), world, renderer));

  glfwOSPRayWindow->registerImGuiCallback([=]() {
    static int spp = 1;
    if (ImGui::SliderInt("spp", &spp, 1, 64)) {
      ospSet1i(renderer, "spp", spp);
      ospCommit(renderer);
    }

    static int tessellationLevel = 5;
    if (ImGui::SliderInt("tessellation level", &tessellationLevel, 1, 10)) {
      ospSet1f(subdivisionGeometry.geometry, "level", tessellationLevel);
      ospCommit(subdivisionGeometry.geometry);
      ospCommit(world);

      GLFWOSPRayWindow::getActiveWindow()->clearFrameBuffer();
    }

    static float vertexCreaseWeight = 2.f;
    if (ImGui::SliderFloat(
            "vertex crease weights", &vertexCreaseWeight, 0.f, 5.f)) {
      // vertex crease indices already set on cube geometry

      // vertex crease weights; use a constant weight for each vertex
      std::vector<float> vertexCreaseWeights(8);
      std::fill(vertexCreaseWeights.begin(),
                vertexCreaseWeights.end(),
                vertexCreaseWeight);

      OSPData vertexCreaseWeightsData = ospNewData(
          vertexCreaseWeights.size(), OSP_FLOAT, vertexCreaseWeights.data());

      ospSetData(subdivisionGeometry.geometry,
                 "vertexCrease.weight",
                 vertexCreaseWeightsData);
      ospRelease(vertexCreaseWeightsData);

      ospCommit(subdivisionGeometry.geometry);
      ospCommit(world);

      GLFWOSPRayWindow::getActiveWindow()->clearFrameBuffer();
    }

    static float edgeCreaseWeight = 2.f;
    if (ImGui::SliderFloat(
            "edge crease weights", &edgeCreaseWeight, 0.f, 5.f)) {
      // edge crease indices already set on cube geometry

      // edge crease weights; use a constant weight for each edge
      std::vector<float> edgeCreaseWeights(12);
      std::fill(
          edgeCreaseWeights.begin(), edgeCreaseWeights.end(), edgeCreaseWeight);

      OSPData edgeCreaseWeightsData = ospNewData(
          edgeCreaseWeights.size(), OSP_FLOAT, edgeCreaseWeights.data());

      ospSetData(subdivisionGeometry.geometry,
                 "edgeCrease.weight",
                 edgeCreaseWeightsData);
      ospRelease(edgeCreaseWeightsData);

      ospCommit(subdivisionGeometry.geometry);
      ospCommit(world);

      GLFWOSPRayWindow::getActiveWindow()->clearFrameBuffer();
    }
  });

  // start the GLFW main loop, which will continuously render
  glfwOSPRayWindow->mainLoop();

  // cleanup remaining objects
  ospRelease(world);
  ospRelease(renderer);

  // cleanly shut OSPRay down
  ospShutdown();

  return 0;
}
<commit_msg>spp control not needed in this tutorial,<commit_after>// ======================================================================== //
// Copyright 2018-2019 Intel Corporation                                    //
//                                                                          //
// 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 <iterator>
#include <memory>
#include <random>
#include "GLFWOSPRayWindow.h"

#include "ospray_testing.h"

#include <imgui.h>

using namespace ospcommon;

OSPGeometry createGroundPlaneGeometry()
{
  OSPGeometry planeGeometry = ospNewGeometry("quads");

  struct Vertex
  {
    vec3f position;
    vec3f normal;
    vec4f color;
  };

  struct QuadIndex
  {
    int x;
    int y;
    int z;
    int w;
  };

  std::vector<Vertex> vertices;
  std::vector<QuadIndex> quadIndices;

  // ground plane
  int startingIndex = vertices.size();

  // extent of plane in the (x, z) directions
  const float planeExtent = 1.5f;

  const vec3f up   = vec3f{0.f, 1.f, 0.f};
  const vec4f gray = vec4f{0.9f, 0.9f, 0.9f, 0.75f};

  vertices.push_back(Vertex{vec3f{-planeExtent, -1.f, -planeExtent}, up, gray});
  vertices.push_back(Vertex{vec3f{planeExtent, -1.f, -planeExtent}, up, gray});
  vertices.push_back(Vertex{vec3f{planeExtent, -1.f, planeExtent}, up, gray});
  vertices.push_back(Vertex{vec3f{-planeExtent, -1.f, planeExtent}, up, gray});

  quadIndices.push_back(QuadIndex{
      startingIndex, startingIndex + 1, startingIndex + 2, startingIndex + 3});

  // stripes on ground plane
  const float stripeWidth  = 0.025f;
  const float paddedExtent = planeExtent + stripeWidth;
  const size_t numStripes  = 10;

  const vec4f stripeColor = vec4f{1.0f, 0.1f, 0.1f, 1.f};

  for (size_t i = 0; i < numStripes; i++) {
    // the center coordinate of the stripe, either in the x or z direction
    const float coord =
        -planeExtent + float(i) / float(numStripes - 1) * 2.f * planeExtent;

    // offset the stripes by an epsilon above the ground plane
    const float yLevel = -1.f + 1e-3f;

    // x-direction stripes
    startingIndex = vertices.size();

    vertices.push_back(Vertex{
        vec3f{-paddedExtent, yLevel, coord - stripeWidth}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{paddedExtent, yLevel, coord - stripeWidth}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{paddedExtent, yLevel, coord + stripeWidth}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{-paddedExtent, yLevel, coord + stripeWidth}, up, stripeColor});

    quadIndices.push_back(QuadIndex{startingIndex,
                                    startingIndex + 1,
                                    startingIndex + 2,
                                    startingIndex + 3});

    // z-direction stripes
    startingIndex = vertices.size();

    vertices.push_back(Vertex{
        vec3f{coord - stripeWidth, yLevel, -paddedExtent}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{coord + stripeWidth, yLevel, -paddedExtent}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{coord + stripeWidth, yLevel, paddedExtent}, up, stripeColor});
    vertices.push_back(Vertex{
        vec3f{coord - stripeWidth, yLevel, paddedExtent}, up, stripeColor});

    quadIndices.push_back(QuadIndex{startingIndex,
                                    startingIndex + 1,
                                    startingIndex + 2,
                                    startingIndex + 3});
  }

  // create OSPRay data objects
  std::vector<vec3f> positionVector;
  std::vector<vec3f> normalVector;
  std::vector<vec4f> colorVector;

  std::transform(vertices.begin(),
                 vertices.end(),
                 std::back_inserter(positionVector),
                 [](Vertex const &v) { return v.position; });
  std::transform(vertices.begin(),
                 vertices.end(),
                 std::back_inserter(normalVector),
                 [](Vertex const &v) { return v.normal; });
  std::transform(vertices.begin(),
                 vertices.end(),
                 std::back_inserter(colorVector),
                 [](Vertex const &v) { return v.color; });

  OSPData positionData =
      ospNewData(vertices.size(), OSP_FLOAT3, positionVector.data());
  OSPData normalData =
      ospNewData(vertices.size(), OSP_FLOAT3, normalVector.data());
  OSPData colorData =
      ospNewData(vertices.size(), OSP_FLOAT4, colorVector.data());
  OSPData indexData =
      ospNewData(quadIndices.size(), OSP_INT4, quadIndices.data());

  // set vertex / index data on the geometry
  ospSetData(planeGeometry, "vertex", positionData);
  ospSetData(planeGeometry, "vertex.normal", normalData);
  ospSetData(planeGeometry, "vertex.color", colorData);
  ospSetData(planeGeometry, "index", indexData);

  // create and assign a material to the geometry
  OSPMaterial material = ospNewMaterial2("scivis", "OBJMaterial");
  ospCommit(material);

  ospSetMaterial(planeGeometry, material);

  // finally, commit the geometry
  ospCommit(planeGeometry);

  // release handles we no longer need
  ospRelease(positionData);
  ospRelease(normalData);
  ospRelease(colorData);
  ospRelease(indexData);
  ospRelease(material);

  return planeGeometry;
}

int main(int argc, const char **argv)
{
  // initialize OSPRay; OSPRay parses (and removes) its commandline parameters,
  // e.g. "--osp:debug"
  OSPError initError = ospInit(&argc, argv);

  if (initError != OSP_NO_ERROR)
    return initError;

  // set an error callback to catch any OSPRay errors and exit the application
  ospDeviceSetErrorFunc(
      ospGetCurrentDevice(), [](OSPError error, const char *errorDetails) {
        std::cerr << "OSPRay error: " << errorDetails << std::endl;
        exit(error);
      });

  // create the "world" model which will contain all of our geometries
  OSPModel world = ospNewModel();

  // add in subdivision geometry
  OSPTestingGeometry subdivisionGeometry =
      ospTestingNewGeometry("subdivision_cube", "scivis");
  ospAddGeometry(world, subdivisionGeometry.geometry);
  ospRelease(subdivisionGeometry.geometry);

  // add in a ground plane geometry
  ospAddGeometry(world, createGroundPlaneGeometry());

  // commit the world model
  ospCommit(world);

  // create OSPRay renderer
  OSPRenderer renderer = ospNewRenderer("scivis");

  OSPData lightsData = ospTestingNewLights("ambient_and_directional");
  ospSetData(renderer, "lights", lightsData);
  ospRelease(lightsData);

  ospCommit(renderer);

  // create a GLFW OSPRay window: this object will create and manage the OSPRay
  // frame buffer and camera directly
  auto glfwOSPRayWindow =
      std::unique_ptr<GLFWOSPRayWindow>(new GLFWOSPRayWindow(
          vec2i{1024, 768}, box3f(vec3f(-1.f), vec3f(1.f)), world, renderer));

  glfwOSPRayWindow->registerImGuiCallback([=]() {
    static int tessellationLevel = 5;
    if (ImGui::SliderInt("tessellation level", &tessellationLevel, 1, 10)) {
      ospSet1f(subdivisionGeometry.geometry, "level", tessellationLevel);
      ospCommit(subdivisionGeometry.geometry);
      ospCommit(world);

      GLFWOSPRayWindow::getActiveWindow()->clearFrameBuffer();
    }

    static float vertexCreaseWeight = 2.f;
    if (ImGui::SliderFloat(
            "vertex crease weights", &vertexCreaseWeight, 0.f, 5.f)) {
      // vertex crease indices already set on cube geometry

      // vertex crease weights; use a constant weight for each vertex
      std::vector<float> vertexCreaseWeights(8);
      std::fill(vertexCreaseWeights.begin(),
                vertexCreaseWeights.end(),
                vertexCreaseWeight);

      OSPData vertexCreaseWeightsData = ospNewData(
          vertexCreaseWeights.size(), OSP_FLOAT, vertexCreaseWeights.data());

      ospSetData(subdivisionGeometry.geometry,
                 "vertexCrease.weight",
                 vertexCreaseWeightsData);
      ospRelease(vertexCreaseWeightsData);

      ospCommit(subdivisionGeometry.geometry);
      ospCommit(world);

      GLFWOSPRayWindow::getActiveWindow()->clearFrameBuffer();
    }

    static float edgeCreaseWeight = 2.f;
    if (ImGui::SliderFloat(
            "edge crease weights", &edgeCreaseWeight, 0.f, 5.f)) {
      // edge crease indices already set on cube geometry

      // edge crease weights; use a constant weight for each edge
      std::vector<float> edgeCreaseWeights(12);
      std::fill(
          edgeCreaseWeights.begin(), edgeCreaseWeights.end(), edgeCreaseWeight);

      OSPData edgeCreaseWeightsData = ospNewData(
          edgeCreaseWeights.size(), OSP_FLOAT, edgeCreaseWeights.data());

      ospSetData(subdivisionGeometry.geometry,
                 "edgeCrease.weight",
                 edgeCreaseWeightsData);
      ospRelease(edgeCreaseWeightsData);

      ospCommit(subdivisionGeometry.geometry);
      ospCommit(world);

      GLFWOSPRayWindow::getActiveWindow()->clearFrameBuffer();
    }
  });

  // start the GLFW main loop, which will continuously render
  glfwOSPRayWindow->mainLoop();

  // cleanup remaining objects
  ospRelease(world);
  ospRelease(renderer);

  // cleanly shut OSPRay down
  ospShutdown();

  return 0;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <limits>
#include <queue>

#include "imgui/imgui.h"
#include "imgui_impl_sdl_gl3.hpp"
#include "glm/glm.hpp"
#include "GL/gl3w.h"
#include "SDL2/SDL.h"
#include "stb/stb_image_write.h"
#include "tinyobjloader/tiny_obj_loader.h"
#include "elektra/filesystem.hpp"
#include "remotery/remotery.h"

#include "utils.hpp"
#include "image.hpp"
#include "embree.hpp"
#include "mesh.hpp"
#include "occlusion.hpp"
#include "rasterizer.hpp"
#include "postprocess.hpp"

#include "embree2/rtcore.h"
#pragma warning (push, 0)
#include "embree2/rtcore_ray.h"
#pragma warning (pop)
#include <xmmintrin.h>
#include <pmmintrin.h>

static constexpr uint32_t APP_WIDTH = 1280;
static constexpr uint32_t APP_HEIGHT = 720;
static constexpr char* APP_NAME = "Occlusion and Translucency Baker";
static constexpr uint32_t MAP_SIZE = 1024;
static constexpr char* RESOURCE_PATH = "test/test.obj";

#ifdef _DEBUG
static void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, void* userParam);
#endif

SDL_Window* window;

// TODO(Corralx): Rewrite the imgui <-> SDL bridge
// TODO(Corralx): Set up some way to disable/enable remotery when needed
// TODO(Corralx): Investigate an ImGui file dialog
int main(int, char*[])
{
	if (SDL_Init(SDL_INIT_VIDEO) != 0)
	{
		std::cout << "SDL initialization error: " << SDL_GetError() << std::endl;
		return 1;
	}

	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

#ifdef _DEBUG
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG |
						SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
#else
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
#endif

	SDL_DisplayMode current;
	SDL_GetCurrentDisplayMode(0, &current);
	window = SDL_CreateWindow(APP_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
							  APP_WIDTH, APP_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
	SDL_GLContext glcontext = SDL_GL_CreateContext(window);

	gl3wInit();

#ifdef _DEBUG
	glEnable(GL_DEBUG_OUTPUT);
	glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
	glDebugMessageCallback((GLDEBUGPROC)gl_debug_callback, nullptr);
#endif

	ImGui_ImplSdlGL3_Init(window);

	glViewport(0, 0, APP_WIDTH, APP_HEIGHT);
	glClearColor(1.f, .0f, .0f, 1.f);
	SDL_GL_SetSwapInterval(0);

	rmtSettings* settings = rmt_Settings();
	settings->input_handler_context = nullptr;
	settings->input_handler = [](const char* text, void*)
	{
		std::cout << "Remotery: " << text << std::endl;
	};

	Remotery* rmt;
	rmt_CreateGlobalInstance(&rmt);
	rmt_BindOpenGL();

	std::cout << "Loading meshes..." << std::endl;
	auto shapes = load_meshes(RESOURCE_PATH);
	if (shapes.empty())
	{
		std::cerr << "Error loading meshes!" << std::endl;
		return 1;
	}

	std::cout << "Shapes found: " << shapes.size() << std::endl;

	for (uint32_t i = 0; i < shapes.size(); ++i)
	{
		size_t num_vertices = shapes[i].vertices().size();
		size_t num_normals = shapes[i].normals().size();
		size_t num_tex_coords = shapes[i].texture_coords().size();
		size_t num_tris = shapes[i].faces().size();

		std::cout << std::endl << "Shape " << i << std::endl;
		std::cout << "Vertices: " << num_vertices << std::endl;
		std::cout << "Normals: " << num_normals << std::endl;
		std::cout << "Texture coords: " << num_tex_coords << std::endl;
		std::cout << "Triangles: " << num_tris << std::endl;
	}
	std::cout << std::endl;

	std::cout << "Initializing Embree..." << std::endl;
	embree::context context;
	for (const mesh_t& m : shapes)
		context.add_mesh(m);
	if (!context.commit())
	{
		std::cerr << "Error initializing Embree!" << std::endl;
		return 1;
	}

	const uint32_t mesh_index = 0;

	std::cout << "Rasterizing UVs..." << std::endl;
	image<image_format::U32> indices_map(MAP_SIZE, MAP_SIZE);
	indices_map.initialize(255);
	rasterize_triangle_hardware(shapes[mesh_index], indices_map).get();

	std::cout << "Calculating occlusion map..." << std::endl;
	image<image_format::F32> occlusion_map(MAP_SIZE, MAP_SIZE);
	occlusion_map.initialize(0);

	occlusion_params params{};
	params.min_distance = .0001f;
	params.max_distance = 15.f;
	params.smooth_normal_interpolation = true;
	params.linear_attenuation = 1.f;
	params.quadratic_attenuation = 1.f;
	params.tile_width = 64;
	params.tile_height = 64;
	params.quality = 8;
	params.worker_num = std::min((uint8_t)8, (uint8_t)std::thread::hardware_concurrency());

	generate_occlusion_map(context, shapes[mesh_index], params, indices_map, occlusion_map).get();

	std::cout << "Postprocessing occlusion map..." << std::endl;
	gaussian_blur(occlusion_map, 3, 3, 1.f).get();
	invert(occlusion_map).get();

	std::cout << "Saving to disk..." << std::endl;
	write_image("maps/occlusion_map.hdr", occlusion_map);

	bool should_run = true;
	while (should_run)
	{
		rmt_BeginCPUSample(RenderLoop);

		rmt_BeginCPUSample(EventManagement);
		SDL_Event event;
		while (SDL_PollEvent(&event))
		{
			ImGui_ImplSdlGL3_ProcessEvent(&event);
			switch (event.type)
			{
				case SDL_QUIT:
					should_run = false;
					break;

				case SDL_KEYDOWN:
					if (event.key.keysym.sym == SDLK_ESCAPE)
						should_run = false;
					break;
			}
		}
		rmt_EndCPUSample();

		// TODO: Update

		rmt_BeginCPUSample(BufferClear);
		glClear(GL_COLOR_BUFFER_BIT);
		rmt_EndCPUSample();

		// TODO: Render

		rmt_BeginCPUSample(ImGuiRender);
		ImGui_ImplSdlGL3_NewFrame();
		{
			static float f = 0.0f;
			ImGui::Text("Hello, world!");
			ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
			ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
		}
		ImGui::Render();
		rmt_EndCPUSample();

		SDL_GL_SwapWindow(window);
		rmt_EndCPUSample();
	}

	rmt_UnbindOpenGL();
	rmt_DestroyGlobalInstance(rmt);

	ImGui_ImplSdlGL3_Shutdown();
	SDL_GL_DeleteContext(glcontext);
	SDL_DestroyWindow(window);
	SDL_Quit();

	return 0;
}

#ifdef _DEBUG
void gl_debug_callback(GLenum, GLenum type, GLuint id, GLenum severity, GLsizei, const GLchar* message, void*)
{
	using namespace std;

	cout << "---------------------opengl-callback-start------------" << endl;
	cout << "message: " << message << endl;
	cout << "type: ";
	switch (type)
	{
		case GL_DEBUG_TYPE_ERROR:
			cout << "ERROR";
			break;
		case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
			cout << "DEPRECATED_BEHAVIOR";
			break;
		case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
			cout << "UNDEFINED_BEHAVIOR";
			break;
		case GL_DEBUG_TYPE_PORTABILITY:
			cout << "PORTABILITY";
			break;
		case GL_DEBUG_TYPE_PERFORMANCE:
			cout << "PERFORMANCE";
			break;
		case GL_DEBUG_TYPE_OTHER:
			cout << "OTHER";
			break;
	}
	cout << endl;

	cout << "id: " << id << endl;
	cout << "severity: ";
	switch (severity)
	{
		case GL_DEBUG_SEVERITY_LOW:
			cout << "LOW";
			break;
		case GL_DEBUG_SEVERITY_MEDIUM:
			cout << "MEDIUM";
			break;
		case GL_DEBUG_SEVERITY_HIGH:
			cout << "HIGH";
			break;
	}
	cout << endl;
	cout << "---------------------opengl-callback-end--------------" << endl;
}
#endif
<commit_msg>Removed unused header inclusion<commit_after>#include <iostream>

#include "imgui/imgui.h"
#include "imgui_impl_sdl_gl3.hpp"
#include "GL/gl3w.h"
#include "SDL2/SDL.h"
#include "remotery/remotery.h"
#include "elektra/filesystem.hpp"

#include "utils.hpp"
#include "image.hpp"
#include "embree.hpp"
#include "mesh.hpp"
#include "occlusion.hpp"
#include "rasterizer.hpp"
#include "postprocess.hpp"

static constexpr uint32_t APP_WIDTH = 1280;
static constexpr uint32_t APP_HEIGHT = 720;
static constexpr char* APP_NAME = "Occlusion and Translucency Baker";
static constexpr uint32_t MAP_SIZE = 1024;
static constexpr char* RESOURCE_PATH = "test/test.obj";

#ifdef _DEBUG
static void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, void* userParam);
#endif

SDL_Window* window;

// TODO(Corralx): Rewrite the imgui <-> SDL bridge
// TODO(Corralx): Set up some way to disable/enable remotery when needed
// TODO(Corralx): Investigate an ImGui file dialog
int main(int, char*[])
{
	if (SDL_Init(SDL_INIT_VIDEO) != 0)
	{
		std::cout << "SDL initialization error: " << SDL_GetError() << std::endl;
		return 1;
	}

	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

#ifdef _DEBUG
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG |
						SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
#else
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
#endif

	SDL_DisplayMode current;
	SDL_GetCurrentDisplayMode(0, &current);
	window = SDL_CreateWindow(APP_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
							  APP_WIDTH, APP_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
	SDL_GLContext glcontext = SDL_GL_CreateContext(window);

	gl3wInit();

#ifdef _DEBUG
	glEnable(GL_DEBUG_OUTPUT);
	glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
	glDebugMessageCallback((GLDEBUGPROC)gl_debug_callback, nullptr);
#endif

	ImGui_ImplSdlGL3_Init(window);

	glViewport(0, 0, APP_WIDTH, APP_HEIGHT);
	glClearColor(1.f, .0f, .0f, 1.f);
	SDL_GL_SetSwapInterval(0);

	rmtSettings* settings = rmt_Settings();
	settings->input_handler_context = nullptr;
	settings->input_handler = [](const char* text, void*)
	{
		std::cout << "Remotery: " << text << std::endl;
	};

	Remotery* rmt;
	rmt_CreateGlobalInstance(&rmt);
	rmt_BindOpenGL();

	std::cout << "Loading meshes..." << std::endl;
	auto shapes = load_meshes(RESOURCE_PATH);
	if (shapes.empty())
	{
		std::cerr << "Error loading meshes!" << std::endl;
		return 1;
	}

	std::cout << "Shapes found: " << shapes.size() << std::endl;

	for (uint32_t i = 0; i < shapes.size(); ++i)
	{
		size_t num_vertices = shapes[i].vertices().size();
		size_t num_normals = shapes[i].normals().size();
		size_t num_tex_coords = shapes[i].texture_coords().size();
		size_t num_tris = shapes[i].faces().size();

		std::cout << std::endl << "Shape " << i << std::endl;
		std::cout << "Vertices: " << num_vertices << std::endl;
		std::cout << "Normals: " << num_normals << std::endl;
		std::cout << "Texture coords: " << num_tex_coords << std::endl;
		std::cout << "Triangles: " << num_tris << std::endl;
	}
	std::cout << std::endl;

	std::cout << "Initializing Embree..." << std::endl;
	embree::context context;
	for (const mesh_t& m : shapes)
		context.add_mesh(m);
	if (!context.commit())
	{
		std::cerr << "Error initializing Embree!" << std::endl;
		return 1;
	}

	const uint32_t mesh_index = 0;

	std::cout << "Rasterizing UVs..." << std::endl;
	image<image_format::U32> indices_map(MAP_SIZE, MAP_SIZE);
	indices_map.initialize(255);
	rasterize_triangle_hardware(shapes[mesh_index], indices_map).get();

	std::cout << "Calculating occlusion map..." << std::endl;
	image<image_format::F32> occlusion_map(MAP_SIZE, MAP_SIZE);
	occlusion_map.initialize(0);

	occlusion_params params{};
	params.min_distance = .0001f;
	params.max_distance = 15.f;
	params.smooth_normal_interpolation = true;
	params.linear_attenuation = 1.f;
	params.quadratic_attenuation = 1.f;
	params.tile_width = 64;
	params.tile_height = 64;
	params.quality = 8;
	params.worker_num = std::min((uint8_t)8, (uint8_t)std::thread::hardware_concurrency());

	generate_occlusion_map(context, shapes[mesh_index], params, indices_map, occlusion_map).get();

	std::cout << "Postprocessing occlusion map..." << std::endl;
	gaussian_blur(occlusion_map, 3, 3, 1.f).get();
	invert(occlusion_map).get();

	std::cout << "Saving to disk..." << std::endl;
	write_image("maps/occlusion_map.hdr", occlusion_map);

	bool should_run = true;
	while (should_run)
	{
		rmt_BeginCPUSample(RenderLoop);

		rmt_BeginCPUSample(EventManagement);
		SDL_Event event;
		while (SDL_PollEvent(&event))
		{
			ImGui_ImplSdlGL3_ProcessEvent(&event);
			switch (event.type)
			{
				case SDL_QUIT:
					should_run = false;
					break;

				case SDL_KEYDOWN:
					if (event.key.keysym.sym == SDLK_ESCAPE)
						should_run = false;
					break;
			}
		}
		rmt_EndCPUSample();

		// TODO: Update

		rmt_BeginCPUSample(BufferClear);
		glClear(GL_COLOR_BUFFER_BIT);
		rmt_EndCPUSample();

		// TODO: Render

		rmt_BeginCPUSample(ImGuiRender);
		ImGui_ImplSdlGL3_NewFrame();
		{
			static float f = 0.0f;
			ImGui::Text("Hello, world!");
			ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
			ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
		}
		ImGui::Render();
		rmt_EndCPUSample();

		SDL_GL_SwapWindow(window);
		rmt_EndCPUSample();
	}

	rmt_UnbindOpenGL();
	rmt_DestroyGlobalInstance(rmt);

	ImGui_ImplSdlGL3_Shutdown();
	SDL_GL_DeleteContext(glcontext);
	SDL_DestroyWindow(window);
	SDL_Quit();

	return 0;
}

#ifdef _DEBUG
void gl_debug_callback(GLenum, GLenum type, GLuint id, GLenum severity, GLsizei, const GLchar* message, void*)
{
	using namespace std;

	cout << "---------------------opengl-callback-start------------" << endl;
	cout << "message: " << message << endl;
	cout << "type: ";
	switch (type)
	{
		case GL_DEBUG_TYPE_ERROR:
			cout << "ERROR";
			break;
		case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
			cout << "DEPRECATED_BEHAVIOR";
			break;
		case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
			cout << "UNDEFINED_BEHAVIOR";
			break;
		case GL_DEBUG_TYPE_PORTABILITY:
			cout << "PORTABILITY";
			break;
		case GL_DEBUG_TYPE_PERFORMANCE:
			cout << "PERFORMANCE";
			break;
		case GL_DEBUG_TYPE_OTHER:
			cout << "OTHER";
			break;
	}
	cout << endl;

	cout << "id: " << id << endl;
	cout << "severity: ";
	switch (severity)
	{
		case GL_DEBUG_SEVERITY_LOW:
			cout << "LOW";
			break;
		case GL_DEBUG_SEVERITY_MEDIUM:
			cout << "MEDIUM";
			break;
		case GL_DEBUG_SEVERITY_HIGH:
			cout << "HIGH";
			break;
	}
	cout << endl;
	cout << "---------------------opengl-callback-end--------------" << endl;
}
#endif
<|endoftext|>


<commit_before>// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/ui/views/global_menu_bar_x11.h"

#include <X11/Xlib.h>

// This conflicts with mate::Converter,
#undef True
#undef False
// and V8,
#undef None
// and url_request_status.h,
#undef Status

#include <dlfcn.h>
#include <glib-object.h>

#include "atom/browser/native_window_views.h"
#include "atom/browser/ui/atom_menu_model.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"

// libdbusmenu-glib types
typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_func)();
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_with_id_func)(int id);

typedef int (*dbusmenu_menuitem_get_id_func)(DbusmenuMenuitem* item);
typedef GList* (*dbusmenu_menuitem_get_children_func)(DbusmenuMenuitem* item);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_child_append_func)(
    DbusmenuMenuitem* parent,
    DbusmenuMenuitem* child);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_func)(
    DbusmenuMenuitem* item,
    const char* property,
    const char* value);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_variant_func)(
    DbusmenuMenuitem* item,
    const char* property,
    GVariant* value);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_bool_func)(
    DbusmenuMenuitem* item,
    const char* property,
    bool value);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_int_func)(
    DbusmenuMenuitem* item,
    const char* property,
    int value);

typedef struct _DbusmenuServer      DbusmenuServer;
typedef DbusmenuServer* (*dbusmenu_server_new_func)(const char* object);
typedef void (*dbusmenu_server_set_root_func)(DbusmenuServer* self,
                                              DbusmenuMenuitem* root);

namespace atom {

namespace {

// Retrieved functions from libdbusmenu-glib.

// DbusmenuMenuItem methods:
dbusmenu_menuitem_new_func menuitem_new = NULL;
dbusmenu_menuitem_new_with_id_func menuitem_new_with_id = NULL;
dbusmenu_menuitem_get_id_func menuitem_get_id = NULL;
dbusmenu_menuitem_get_children_func menuitem_get_children = NULL;
dbusmenu_menuitem_get_children_func menuitem_take_children = NULL;
dbusmenu_menuitem_child_append_func menuitem_child_append = NULL;
dbusmenu_menuitem_property_set_func menuitem_property_set = NULL;
dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant =
    NULL;
dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = NULL;
dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = NULL;

// DbusmenuServer methods:
dbusmenu_server_new_func server_new = NULL;
dbusmenu_server_set_root_func server_set_root = NULL;

// Properties that we set on menu items:
const char kPropertyEnabled[] = "enabled";
const char kPropertyLabel[] = "label";
const char kPropertyShortcut[] = "shortcut";
const char kPropertyType[] = "type";
const char kPropertyToggleType[] = "toggle-type";
const char kPropertyToggleState[] = "toggle-state";
const char kPropertyVisible[] = "visible";
const char kPropertyChildrenDisplay[] = "children-display";

const char kToggleCheck[] = "checkmark";
const char kToggleRadio[] = "radio";
const char kTypeSeparator[] = "separator";
const char kDisplaySubmenu[] = "submenu";

void EnsureMethodsLoaded() {
  static bool attempted_load = false;
  if (attempted_load)
    return;
  attempted_load = true;

  void* dbusmenu_lib = dlopen("libdbusmenu-glib.so", RTLD_LAZY);
  if (!dbusmenu_lib)
    dbusmenu_lib = dlopen("libdbusmenu-glib.so.4", RTLD_LAZY);
  if (!dbusmenu_lib)
    return;

  // DbusmenuMenuItem methods.
  menuitem_new = reinterpret_cast<dbusmenu_menuitem_new_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_new"));
  menuitem_new_with_id = reinterpret_cast<dbusmenu_menuitem_new_with_id_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_new_with_id"));
  menuitem_get_id = reinterpret_cast<dbusmenu_menuitem_get_id_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_id"));
  menuitem_get_children = reinterpret_cast<dbusmenu_menuitem_get_children_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_children"));
  menuitem_take_children =
      reinterpret_cast<dbusmenu_menuitem_get_children_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_take_children"));
  menuitem_child_append = reinterpret_cast<dbusmenu_menuitem_child_append_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_append"));
  menuitem_property_set = reinterpret_cast<dbusmenu_menuitem_property_set_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set"));
  menuitem_property_set_variant =
      reinterpret_cast<dbusmenu_menuitem_property_set_variant_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_variant"));
  menuitem_property_set_bool =
      reinterpret_cast<dbusmenu_menuitem_property_set_bool_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_bool"));
  menuitem_property_set_int =
      reinterpret_cast<dbusmenu_menuitem_property_set_int_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_int"));

  // DbusmenuServer methods.
  server_new = reinterpret_cast<dbusmenu_server_new_func>(
      dlsym(dbusmenu_lib, "dbusmenu_server_new"));
  server_set_root = reinterpret_cast<dbusmenu_server_set_root_func>(
      dlsym(dbusmenu_lib, "dbusmenu_server_set_root"));
}

AtomMenuModel* ModelForMenuItem(DbusmenuMenuitem* item) {
  return reinterpret_cast<AtomMenuModel*>(
      g_object_get_data(G_OBJECT(item), "model"));
}

bool GetMenuItemID(DbusmenuMenuitem* item, int *id) {
  gpointer id_ptr = g_object_get_data(G_OBJECT(item), "menu-id");
  if (id_ptr != NULL) {
    *id = GPOINTER_TO_INT(id_ptr) - 1;
    return true;
  }

  return false;
}

void SetMenuItemID(DbusmenuMenuitem* item, int id) {
  DCHECK_GE(id, 0);

  // Add 1 to the menu_id to avoid setting zero (null) to "menu-id".
  g_object_set_data(G_OBJECT(item), "menu-id", GINT_TO_POINTER(id + 1));
}

}  // namespace

GlobalMenuBarX11::GlobalMenuBarX11(NativeWindowViews* window)
    : window_(window),
      xid_(window_->GetNativeWindow()->GetHost()->GetAcceleratedWidget()),
      server_(NULL) {
  EnsureMethodsLoaded();
  if (server_new)
    InitServer(xid_);

  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid_);
}

GlobalMenuBarX11::~GlobalMenuBarX11() {
  if (IsServerStarted())
    g_object_unref(server_);

  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid_);
}

// static
std::string GlobalMenuBarX11::GetPathForWindow(gfx::AcceleratedWidget xid) {
  return base::StringPrintf("/com/canonical/menu/%lX", xid);
}

void GlobalMenuBarX11::SetMenu(AtomMenuModel* menu_model) {
  if (!IsServerStarted())
    return;

  DbusmenuMenuitem* root_item = menuitem_new();
  menuitem_property_set(root_item, kPropertyLabel, "Root");
  menuitem_property_set_bool(root_item, kPropertyVisible, true);
  BuildMenuFromModel(menu_model, root_item);

  server_set_root(server_, root_item);
  g_object_unref(root_item);
}

bool GlobalMenuBarX11::IsServerStarted() const {
  return server_;
}

void GlobalMenuBarX11::InitServer(gfx::AcceleratedWidget xid) {
  std::string path = GetPathForWindow(xid);
  server_ = server_new(path.c_str());
}

void GlobalMenuBarX11::OnWindowMapped() {
  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid_);
}

void GlobalMenuBarX11::OnWindowUnmapped() {
  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid_);
}

void GlobalMenuBarX11::BuildMenuFromModel(AtomMenuModel* model,
                                          DbusmenuMenuitem* parent) {
  for (int i = 0; i < model->GetItemCount(); ++i) {
    DbusmenuMenuitem* item = menuitem_new();
    menuitem_property_set_bool(item, kPropertyVisible, model->IsVisibleAt(i));

    AtomMenuModel::ItemType type = model->GetTypeAt(i);
    if (type == AtomMenuModel::TYPE_SEPARATOR) {
      menuitem_property_set(item, kPropertyType, kTypeSeparator);
    } else {
      std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
          base::UTF16ToUTF8(model->GetLabelAt(i)));
      menuitem_property_set(item, kPropertyLabel, label.c_str());
      menuitem_property_set_bool(item, kPropertyEnabled, model->IsEnabledAt(i));

      g_object_set_data(G_OBJECT(item), "model", model);
      SetMenuItemID(item, i);

      if (type == AtomMenuModel::TYPE_SUBMENU) {
        menuitem_property_set(item, kPropertyChildrenDisplay, kDisplaySubmenu);
        g_signal_connect(item, "about-to-show",
                         G_CALLBACK(OnSubMenuShowThunk), this);
      } else {
        ui::Accelerator accelerator;
        if (model->GetAcceleratorAtWithParams(i, true, &accelerator))
          RegisterAccelerator(item, accelerator);

        g_signal_connect(item, "item-activated",
                         G_CALLBACK(OnItemActivatedThunk), this);

        if (type == AtomMenuModel::TYPE_CHECK ||
            type == AtomMenuModel::TYPE_RADIO) {
          menuitem_property_set(item, kPropertyToggleType,
              type == AtomMenuModel::TYPE_CHECK ? kToggleCheck : kToggleRadio);
          menuitem_property_set_int(item, kPropertyToggleState,
              model->IsItemCheckedAt(i));
        }
      }
    }

    menuitem_child_append(parent, item);
    g_object_unref(item);
  }
}

void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item,
                                           const ui::Accelerator& accelerator) {
  // A translation of libdbusmenu-gtk's menuitem_property_set_shortcut()
  // translated from GDK types to ui::Accelerator types.
  GVariantBuilder builder;
  g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);

  if (accelerator.IsCtrlDown())
    g_variant_builder_add(&builder, "s", "Control");
  if (accelerator.IsAltDown())
    g_variant_builder_add(&builder, "s", "Alt");
  if (accelerator.IsShiftDown())
    g_variant_builder_add(&builder, "s", "Shift");

  char* name = XKeysymToString(XKeysymForWindowsKeyCode(
      accelerator.key_code(), false));
  if (!name) {
    NOTIMPLEMENTED();
    return;
  }
  g_variant_builder_add(&builder, "s", name);

  GVariant* inside_array = g_variant_builder_end(&builder);
  g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
  g_variant_builder_add_value(&builder, inside_array);
  GVariant* outside_array = g_variant_builder_end(&builder);

  menuitem_property_set_variant(item, kPropertyShortcut, outside_array);
}

void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item,
                                       unsigned int timestamp) {
  int id;
  AtomMenuModel* model = ModelForMenuItem(item);
  if (model && GetMenuItemID(item, &id))
    model->ActivatedAt(id, 0);
}

void GlobalMenuBarX11::OnSubMenuShow(DbusmenuMenuitem* item) {
  int id;
  AtomMenuModel* model = ModelForMenuItem(item);
  if (!model || !GetMenuItemID(item, &id))
    return;

  // Clear children.
  GList *children = menuitem_take_children(item);
  g_list_foreach(children, reinterpret_cast<GFunc>(g_object_unref), NULL);
  g_list_free(children);

  // Build children.
  BuildMenuFromModel(model->GetSubmenuModelAt(id), item);
}

}  // namespace atom
<commit_msg>Only update dbus menu when it has been changed<commit_after>// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/ui/views/global_menu_bar_x11.h"

#include <X11/Xlib.h>

// This conflicts with mate::Converter,
#undef True
#undef False
// and V8,
#undef None
// and url_request_status.h,
#undef Status

#include <dlfcn.h>
#include <glib-object.h>

#include "atom/browser/native_window_views.h"
#include "atom/browser/ui/atom_menu_model.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"

// libdbusmenu-glib types
typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_func)();
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_with_id_func)(int id);

typedef int (*dbusmenu_menuitem_get_id_func)(DbusmenuMenuitem* item);
typedef GList* (*dbusmenu_menuitem_get_children_func)(DbusmenuMenuitem* item);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_child_append_func)(
    DbusmenuMenuitem* parent,
    DbusmenuMenuitem* child);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_func)(
    DbusmenuMenuitem* item,
    const char* property,
    const char* value);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_variant_func)(
    DbusmenuMenuitem* item,
    const char* property,
    GVariant* value);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_bool_func)(
    DbusmenuMenuitem* item,
    const char* property,
    bool value);
typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_int_func)(
    DbusmenuMenuitem* item,
    const char* property,
    int value);

typedef struct _DbusmenuServer      DbusmenuServer;
typedef DbusmenuServer* (*dbusmenu_server_new_func)(const char* object);
typedef void (*dbusmenu_server_set_root_func)(DbusmenuServer* self,
                                              DbusmenuMenuitem* root);

namespace atom {

namespace {

// Retrieved functions from libdbusmenu-glib.

// DbusmenuMenuItem methods:
dbusmenu_menuitem_new_func menuitem_new = NULL;
dbusmenu_menuitem_new_with_id_func menuitem_new_with_id = NULL;
dbusmenu_menuitem_get_id_func menuitem_get_id = NULL;
dbusmenu_menuitem_get_children_func menuitem_get_children = NULL;
dbusmenu_menuitem_get_children_func menuitem_take_children = NULL;
dbusmenu_menuitem_child_append_func menuitem_child_append = NULL;
dbusmenu_menuitem_property_set_func menuitem_property_set = NULL;
dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant =
    NULL;
dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = NULL;
dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = NULL;

// DbusmenuServer methods:
dbusmenu_server_new_func server_new = NULL;
dbusmenu_server_set_root_func server_set_root = NULL;

// Properties that we set on menu items:
const char kPropertyEnabled[] = "enabled";
const char kPropertyLabel[] = "label";
const char kPropertyShortcut[] = "shortcut";
const char kPropertyType[] = "type";
const char kPropertyToggleType[] = "toggle-type";
const char kPropertyToggleState[] = "toggle-state";
const char kPropertyVisible[] = "visible";
const char kPropertyChildrenDisplay[] = "children-display";

const char kToggleCheck[] = "checkmark";
const char kToggleRadio[] = "radio";
const char kTypeSeparator[] = "separator";
const char kDisplaySubmenu[] = "submenu";

void EnsureMethodsLoaded() {
  static bool attempted_load = false;
  if (attempted_load)
    return;
  attempted_load = true;

  void* dbusmenu_lib = dlopen("libdbusmenu-glib.so", RTLD_LAZY);
  if (!dbusmenu_lib)
    dbusmenu_lib = dlopen("libdbusmenu-glib.so.4", RTLD_LAZY);
  if (!dbusmenu_lib)
    return;

  // DbusmenuMenuItem methods.
  menuitem_new = reinterpret_cast<dbusmenu_menuitem_new_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_new"));
  menuitem_new_with_id = reinterpret_cast<dbusmenu_menuitem_new_with_id_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_new_with_id"));
  menuitem_get_id = reinterpret_cast<dbusmenu_menuitem_get_id_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_id"));
  menuitem_get_children = reinterpret_cast<dbusmenu_menuitem_get_children_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_children"));
  menuitem_take_children =
      reinterpret_cast<dbusmenu_menuitem_get_children_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_take_children"));
  menuitem_child_append = reinterpret_cast<dbusmenu_menuitem_child_append_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_append"));
  menuitem_property_set = reinterpret_cast<dbusmenu_menuitem_property_set_func>(
      dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set"));
  menuitem_property_set_variant =
      reinterpret_cast<dbusmenu_menuitem_property_set_variant_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_variant"));
  menuitem_property_set_bool =
      reinterpret_cast<dbusmenu_menuitem_property_set_bool_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_bool"));
  menuitem_property_set_int =
      reinterpret_cast<dbusmenu_menuitem_property_set_int_func>(
          dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_int"));

  // DbusmenuServer methods.
  server_new = reinterpret_cast<dbusmenu_server_new_func>(
      dlsym(dbusmenu_lib, "dbusmenu_server_new"));
  server_set_root = reinterpret_cast<dbusmenu_server_set_root_func>(
      dlsym(dbusmenu_lib, "dbusmenu_server_set_root"));
}

AtomMenuModel* ModelForMenuItem(DbusmenuMenuitem* item) {
  return reinterpret_cast<AtomMenuModel*>(
      g_object_get_data(G_OBJECT(item), "model"));
}

bool GetMenuItemID(DbusmenuMenuitem* item, int *id) {
  gpointer id_ptr = g_object_get_data(G_OBJECT(item), "menu-id");
  if (id_ptr != NULL) {
    *id = GPOINTER_TO_INT(id_ptr) - 1;
    return true;
  }

  return false;
}

void SetMenuItemID(DbusmenuMenuitem* item, int id) {
  DCHECK_GE(id, 0);

  // Add 1 to the menu_id to avoid setting zero (null) to "menu-id".
  g_object_set_data(G_OBJECT(item), "menu-id", GINT_TO_POINTER(id + 1));
}

std::string GetMenuModelStatus(AtomMenuModel* model) {
  std::string ret;
  for (int i = 0; i < model->GetItemCount(); ++i) {
    int status = model->GetTypeAt(i) | (model->IsVisibleAt(i) << 30)
                                     | (model->IsEnabledAt(i) << 29)
                                     | (model->IsItemCheckedAt(i) << 28);
    ret += base::StringPrintf(
        "%s-%d\n", base::UTF16ToUTF8(model->GetLabelAt(i)).c_str(), status);
  }
  return ret;
}

}  // namespace

GlobalMenuBarX11::GlobalMenuBarX11(NativeWindowViews* window)
    : window_(window),
      xid_(window_->GetNativeWindow()->GetHost()->GetAcceleratedWidget()),
      server_(NULL) {
  EnsureMethodsLoaded();
  if (server_new)
    InitServer(xid_);

  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid_);
}

GlobalMenuBarX11::~GlobalMenuBarX11() {
  if (IsServerStarted())
    g_object_unref(server_);

  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid_);
}

// static
std::string GlobalMenuBarX11::GetPathForWindow(gfx::AcceleratedWidget xid) {
  return base::StringPrintf("/com/canonical/menu/%lX", xid);
}

void GlobalMenuBarX11::SetMenu(AtomMenuModel* menu_model) {
  if (!IsServerStarted())
    return;

  DbusmenuMenuitem* root_item = menuitem_new();
  menuitem_property_set(root_item, kPropertyLabel, "Root");
  menuitem_property_set_bool(root_item, kPropertyVisible, true);
  BuildMenuFromModel(menu_model, root_item);

  server_set_root(server_, root_item);
  g_object_unref(root_item);
}

bool GlobalMenuBarX11::IsServerStarted() const {
  return server_;
}

void GlobalMenuBarX11::InitServer(gfx::AcceleratedWidget xid) {
  std::string path = GetPathForWindow(xid);
  server_ = server_new(path.c_str());
}

void GlobalMenuBarX11::OnWindowMapped() {
  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid_);
}

void GlobalMenuBarX11::OnWindowUnmapped() {
  GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid_);
}

void GlobalMenuBarX11::BuildMenuFromModel(AtomMenuModel* model,
                                          DbusmenuMenuitem* parent) {
  for (int i = 0; i < model->GetItemCount(); ++i) {
    DbusmenuMenuitem* item = menuitem_new();
    menuitem_property_set_bool(item, kPropertyVisible, model->IsVisibleAt(i));

    AtomMenuModel::ItemType type = model->GetTypeAt(i);
    if (type == AtomMenuModel::TYPE_SEPARATOR) {
      menuitem_property_set(item, kPropertyType, kTypeSeparator);
    } else {
      std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
          base::UTF16ToUTF8(model->GetLabelAt(i)));
      menuitem_property_set(item, kPropertyLabel, label.c_str());
      menuitem_property_set_bool(item, kPropertyEnabled, model->IsEnabledAt(i));

      g_object_set_data(G_OBJECT(item), "model", model);
      SetMenuItemID(item, i);

      if (type == AtomMenuModel::TYPE_SUBMENU) {
        menuitem_property_set(item, kPropertyChildrenDisplay, kDisplaySubmenu);
        g_signal_connect(item, "about-to-show",
                         G_CALLBACK(OnSubMenuShowThunk), this);
      } else {
        ui::Accelerator accelerator;
        if (model->GetAcceleratorAtWithParams(i, true, &accelerator))
          RegisterAccelerator(item, accelerator);

        g_signal_connect(item, "item-activated",
                         G_CALLBACK(OnItemActivatedThunk), this);

        if (type == AtomMenuModel::TYPE_CHECK ||
            type == AtomMenuModel::TYPE_RADIO) {
          menuitem_property_set(item, kPropertyToggleType,
              type == AtomMenuModel::TYPE_CHECK ? kToggleCheck : kToggleRadio);
          menuitem_property_set_int(item, kPropertyToggleState,
              model->IsItemCheckedAt(i));
        }
      }
    }

    menuitem_child_append(parent, item);
    g_object_unref(item);
  }
}

void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item,
                                           const ui::Accelerator& accelerator) {
  // A translation of libdbusmenu-gtk's menuitem_property_set_shortcut()
  // translated from GDK types to ui::Accelerator types.
  GVariantBuilder builder;
  g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);

  if (accelerator.IsCtrlDown())
    g_variant_builder_add(&builder, "s", "Control");
  if (accelerator.IsAltDown())
    g_variant_builder_add(&builder, "s", "Alt");
  if (accelerator.IsShiftDown())
    g_variant_builder_add(&builder, "s", "Shift");

  char* name = XKeysymToString(XKeysymForWindowsKeyCode(
      accelerator.key_code(), false));
  if (!name) {
    NOTIMPLEMENTED();
    return;
  }
  g_variant_builder_add(&builder, "s", name);

  GVariant* inside_array = g_variant_builder_end(&builder);
  g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
  g_variant_builder_add_value(&builder, inside_array);
  GVariant* outside_array = g_variant_builder_end(&builder);

  menuitem_property_set_variant(item, kPropertyShortcut, outside_array);
}

void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item,
                                       unsigned int timestamp) {
  int id;
  AtomMenuModel* model = ModelForMenuItem(item);
  if (model && GetMenuItemID(item, &id))
    model->ActivatedAt(id, 0);
}

void GlobalMenuBarX11::OnSubMenuShow(DbusmenuMenuitem* item) {
  int id;
  AtomMenuModel* model = ModelForMenuItem(item);
  if (!model || !GetMenuItemID(item, &id))
    return;

  // Do not update menu if the submenu has not been changed.
  std::string status = GetMenuModelStatus(model);
  char* old = static_cast<char*>(g_object_get_data(G_OBJECT(item), "status"));
  if (old && status == old)
    return;

  // Save the new status.
  g_object_set_data_full(G_OBJECT(item), "status", g_strdup(status.c_str()),
                         g_free);

  // Clear children.
  GList *children = menuitem_take_children(item);
  g_list_foreach(children, reinterpret_cast<GFunc>(g_object_unref), NULL);
  g_list_free(children);

  // Build children.
  BuildMenuFromModel(model->GetSubmenuModelAt(id), item);
}

}  // namespace atom
<|endoftext|>