<commit_before>/* This file is part of Mapnik (c++ mapping toolkit)
 * Copyright (C) 2005 Artem Pavlenko
 *
 * Mapnik is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//$Id$

#ifndef VALUE_HPP
#define VALUE_HPP

#include <string>
#include <sstream>
#include <boost/variant.hpp>

using namespace boost;
namespace mapnik {

	typedef variant<std::string,int,double> value_base;

    namespace impl {
	struct equals
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator() (const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator() (T lhs, T rhs) const
	    {
		return lhs == rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs == rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs == rhs;
	    }

	    bool operator() (std::string const& lhs, std::string const& rhs) const
	    {
		return  lhs == rhs;
	    }
	};

	struct greater_than
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator()( T lhs, T rhs ) const
	    {
		return lhs > rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs > rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs > rhs;
	    }

	    bool operator() (std::string const& lhs, std::string const& rhs) const
	    {
		return  lhs > rhs;
	    }
	};

	struct greater_or_equal
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator() (T lhs, T rhs) const
	    {
		return lhs >= rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs >= rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs >= rhs;
	    }

	    bool operator() (std::string const& lhs, std::string const& rhs ) const
	    {
		return lhs >= rhs;
	    }
	};

	struct less_than
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator()( T  lhs,T  rhs) const
	    {
		return lhs < rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs < rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs < rhs;
	    }

	    bool operator()( std::string const& lhs, std::string const& rhs ) const
	    {
		return lhs < rhs;
	    }
	};

	struct less_or_equal
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator()(T lhs, T rhs ) const
	    {
		return lhs <= rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs <= rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs <= rhs;
	    }

	    template <typename T>
	    bool operator()( std::string const& lhs, std::string const& rhs ) const
	    {
		return lhs <= rhs;
	    }
	};

	template <typename V>
	struct add : public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs, T2 const&) const
	    {
		return lhs;
	    }
	    template <typename T>
	    value_type operator() (T lhs, T rhs) const
	    {
		return lhs + rhs ;
	    }

	    value_type operator() (std::string const& lhs,std::string const& rhs ) const
	    {
		return lhs + rhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs + rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs + rhs;
	    }
	};
	template <typename V>
	struct sub : public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs, T2 const&) const
	    {
		return lhs;
	    }

	    template <typename T>
	    value_type operator() (T  lhs, T rhs) const
	    {
		return lhs - rhs ;
	    }

	    value_type operator() (std::string const& lhs,std::string const& ) const
	    {
		return lhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs - rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs - rhs;
	    }
	};

	template <typename V>
	struct mult : public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs , T2 const& ) const
	    {
		return lhs;
	    }
	    template <typename T>
	    value_type operator() (T lhs, T rhs) const
	    {
		return lhs * rhs;
	    }

	    value_type operator() (std::string const& lhs,std::string const& ) const
	    {
		return lhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs * rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs * rhs;
	    }
	};

	template <typename V>
	struct div: public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs, T2 const&) const
	    {
		return lhs;
	    }

	    template <typename T>
	    value_type operator() (T lhs, T rhs) const
	    {
		return lhs / rhs;
	    }

	    value_type operator() (std::string const& lhs,std::string const&) const
	    {
		return lhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs / rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs / rhs;
	    }
	};

	struct to_string : public boost::static_visitor<std::string>
	{
	    template <typename T>
	    std::string operator() (T val) const
	    {
		std::stringstream ss;
		ss << val;
		return ss.str();
	    }
	    std::string const& operator() (std::string const& val) const
	    {
		return val;
	    }
	};

	struct to_expression_string : public boost::static_visitor<std::string>
	{
	    template <typename T>
	    std::string operator() (T val) const
	    {
		std::stringstream ss;
		ss << val;
		return ss.str();
	    }
	    std::string operator() (std::string const& val) const
	    {
		return "'" + val + "'";
	    }
	};
    }

    class value : public value_base
    {
    public:
	value ()
	    : value_base(0) {}

	template <typename T> value(T _val_)
	    : value_base(_val_) {}

	value (const value& rhs)
	{
        //todo!!!!!!!!!
	}
	value& operator=(value const& rhs)
	{
		if (this == &rhs)
			return  *this;
		//TODO!!!!!
		return *this;
	}
	bool operator==(value const& other) const
	{
	    return boost::apply_visitor(impl::equals(),*this,other);
	}

	bool operator!=(value const& other) const
	{
	    return !(boost::apply_visitor(impl::equals(),*this,other));
	}

	bool operator>(value const& other) const
	{
	    return boost::apply_visitor(impl::greater_than(),*this,other);
	}

	bool operator>=(value const& other) const
	{
	    return boost::apply_visitor(impl::greater_or_equal(),*this,other);
	}

	bool operator<(value const& other) const
	{
	    return boost::apply_visitor(impl::less_than(),*this,other);
	}

	bool operator<=(value const& other) const
	{
	    return boost::apply_visitor(impl::less_or_equal(),*this,other);
	}

	value& operator+=(value const& other)
	{
	    *this = boost::apply_visitor(impl::add<value>(),*this,other);
	    return *this;
	}

	value& operator-=(value const& other)
	{
	    *this = boost::apply_visitor(impl::sub<value>(),*this,other);
	    return *this;
	}

	value& operator*=(value const& other)
	{
	    *this = boost::apply_visitor(impl::mult<value>(),*this,other);
	    return *this;
	}

	value& operator/=(value const& other)
	{
	    *this = boost::apply_visitor(impl::div<value>(),*this,other);
	    return *this;
	}

	std::string to_expression_string() const
	{
	    return boost::apply_visitor(impl::to_expression_string(),*this);
	}

	std::string to_string() const
	{
	    return boost::apply_visitor(impl::to_string(),*this);
	}
    };

    inline const value operator+(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp+=p2;
	//return tmp;
		return boost::apply_visitor(impl::add<value>(),p1, p2);
    }

    inline const value operator-(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp-=p2;
	//return tmp;
		return boost::apply_visitor(impl::sub<value>(),p1, p2);
    }

    inline const value operator*(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp*=p2;
	//return tmp;
		return boost::apply_visitor(impl::mult<value>(),p1, p2);
    }

    inline const value operator/(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp/=p2;
	//return tmp;
	    return boost::apply_visitor(impl::div<value>(),p1, p2);
	}

    //template <typename charT, typename traits>
    //inline std::basic_ostream<charT,traits>&
    //operator << (std::basic_ostream<charT,traits>& out,/
    //		 value const& v)
    // {
    //	out << v.get();
    //	return out;
    //}
}

#endif //VALUE_HPP
<commit_msg>re-implemented value class to compile on win32<commit_after>/* This file is part of Mapnik (c++ mapping toolkit)
 * Copyright (C) 2005 Artem Pavlenko
 *
 * Mapnik is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

//$Id$

#ifndef VALUE_HPP
#define VALUE_HPP

#include <string>
#include <sstream>
#include <boost/variant.hpp>

using namespace boost;
namespace mapnik {

    typedef variant<int,double,std::string> value_base;

    namespace impl {
	struct equals
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator() (const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator() (T lhs, T rhs) const
	    {
		return lhs == rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs == rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs == rhs;
	    }

	    bool operator() (std::string const& lhs, std::string const& rhs) const
	    {
		return  lhs == rhs;
	    }
	};

	struct greater_than
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator()( T lhs, T rhs ) const
	    {
		return lhs > rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs > rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs > rhs;
	    }

	    bool operator() (std::string const& lhs, std::string const& rhs) const
	    {
		return  lhs > rhs;
	    }
	};

	struct greater_or_equal
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator() (T lhs, T rhs) const
	    {
		return lhs >= rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs >= rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs >= rhs;
	    }

	    bool operator() (std::string const& lhs, std::string const& rhs ) const
	    {
		return lhs >= rhs;
	    }
	};

	struct less_than
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator()( T  lhs,T  rhs) const
	    {
		return lhs < rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs < rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs < rhs;
	    }

	    bool operator()( std::string const& lhs, std::string const& rhs ) const
	    {
		return lhs < rhs;
	    }
	};

	struct less_or_equal
	    : public boost::static_visitor<bool>
	{
	    template <typename T, typename U>
	    bool operator()( const T &, const U & ) const
	    {
		return false;
	    }

	    template <typename T>
	    bool operator()(T lhs, T rhs ) const
	    {
		return lhs <= rhs;
	    }

	    bool operator() (int lhs, double rhs) const
	    {
		return  lhs <= rhs;
	    }

	    bool operator() (double lhs, int rhs) const
	    {
		return  lhs <= rhs;
	    }

	    template <typename T>
	    bool operator()( std::string const& lhs, std::string const& rhs ) const
	    {
		return lhs <= rhs;
	    }
	};

	template <typename V>
	struct add : public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs, T2 const&) const
	    {
		return lhs;
	    }
	    template <typename T>
	    value_type operator() (T lhs, T rhs) const
	    {
		return lhs + rhs ;
	    }

	    value_type operator() (std::string const& lhs,std::string const& rhs ) const
	    {
		return lhs + rhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs + rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs + rhs;
	    }
	};
	template <typename V>
	struct sub : public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs, T2 const&) const
	    {
		return lhs;
	    }

	    template <typename T>
	    value_type operator() (T  lhs, T rhs) const
	    {
		return lhs - rhs ;
	    }

	    value_type operator() (std::string const& lhs,std::string const& ) const
	    {
		return lhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs - rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs - rhs;
	    }
	};

	template <typename V>
	struct mult : public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs , T2 const& ) const
	    {
		return lhs;
	    }
	    template <typename T>
	    value_type operator() (T lhs, T rhs) const
	    {
		return lhs * rhs;
	    }

	    value_type operator() (std::string const& lhs,std::string const& ) const
	    {
		return lhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs * rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs * rhs;
	    }
	};

	template <typename V>
	struct div: public boost::static_visitor<V>
	{
	    typedef V value_type;
	    template <typename T1, typename T2>
	    value_type operator() (T1 const& lhs, T2 const&) const
	    {
		return lhs;
	    }

	    template <typename T>
	    value_type operator() (T lhs, T rhs) const
	    {
		return lhs / rhs;
	    }

	    value_type operator() (std::string const& lhs,std::string const&) const
	    {
		return lhs;
	    }

	    value_type operator() (double lhs, int rhs) const
	    {
		return lhs / rhs;
	    }

	    value_type operator() (int lhs, double rhs) const
	    {
		return lhs / rhs;
	    }
	};

	struct to_string : public boost::static_visitor<std::string>
	{
	    template <typename T>
	    std::string operator() (T val) const
	    {
		std::stringstream ss;
		ss << val;
		return ss.str();
	    }
	    std::string const& operator() (std::string const& val) const
	    {
		return val;
	    }
	};

    struct to_expression_string : public boost::static_visitor<std::string>
    {
        template <typename T>
        std::string operator() (T val) const
        {
            std::stringstream ss;
            ss << val;
            return ss.str();
        }
        std::string operator() (std::string const& val) const
        {
            return "'" + val + "'";
        }
    };
    }

    class value
    {
        value_base base_;
        friend const value operator+(value const&,value const&);
        friend const value operator-(value const&,value const&);
        friend const value operator*(value const&,value const&);
        friend const value operator/(value const&,value const&);
        //friend template <typename charT, typename traits>
          //      std::basic_ostream<charT,traits>&  operator << (std::basic_ostream<charT,traits>&,
    		//                                                       value const& );
    public:
	value ()
	    : base_(0) {}

	template <typename T> value(T _val_)
	    : base_(_val_) {}

    //value (std::string const& str)
     //   : base(str) {}

	//value& operator=(value const& rhs)
	//{
	//	if (this == &rhs)
	//		return  *this;
//		//TODO!!!!!
//		return *this;
	//}
	bool operator==(value const& other) const
	{
	    return boost::apply_visitor(impl::equals(),base_,other.base_);
	}

	bool operator!=(value const& other) const
	{
	    return !(boost::apply_visitor(impl::equals(),base_,other.base_));
	}

	bool operator>(value const& other) const
	{
	    return boost::apply_visitor(impl::greater_than(),base_,other.base_);
	}

	bool operator>=(value const& other) const
	{
	    return boost::apply_visitor(impl::greater_or_equal(),base_,other.base_);
	}

	bool operator<(value const& other) const
	{
	    return boost::apply_visitor(impl::less_than(),base_,other.base_);
	}

	bool operator<=(value const& other) const
	{
	    return boost::apply_visitor(impl::less_or_equal(),base_,other.base_);
	}
    value_base const& base() const
    {
        return base_;
    }
/*
	value& operator+=(value const& other)
	{
	    *this = boost::apply_visitor(impl::add<value>(),*this,other);
	    return *this;
	}

	value& operator-=(value const& other)
	{
	    *this = boost::apply_visitor(impl::sub<value>(),*this,other);
	    return *this;
	}

	value& operator*=(value const& other)
	{
	    *this = boost::apply_visitor(impl::mult<value>(),*this,other);
	    return *this;
	}

	value& operator/=(value const& other)
	{
	    *this = boost::apply_visitor(impl::div<value>(),*this,other);
	    return *this;
	}
*/
	std::string to_expression_string() const
	{
	    return boost::apply_visitor(impl::to_expression_string(),base_);
	}

	std::string to_string() const
	{
	    return boost::apply_visitor(impl::to_string(),base_);
	}
    };

    inline const value operator+(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp+=p2;
	//return tmp;
		return value(boost::apply_visitor(impl::add<value>(),p1.base_, p2.base_));
    }

    inline const value operator-(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp-=p2;
	//return tmp;
		return value(boost::apply_visitor(impl::sub<value>(),p1.base_, p2.base_));
    }

    inline const value operator*(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp*=p2;
	//return tmp;
		return value(boost::apply_visitor(impl::mult<value>(),p1.base_, p2.base_));
    }

    inline const value operator/(value const& p1,value const& p2)
    {
	//value tmp(p1);
	//tmp/=p2;
	//return tmp;
	    return value(boost::apply_visitor(impl::div<value>(),p1.base_, p2.base_));
	}

    template <typename charT, typename traits>
    inline std::basic_ostream<charT,traits>&
    operator << (std::basic_ostream<charT,traits>& out,
    		 value const& v)
    {
    	out << v.base();
    	return out;
    }
}

#endif //VALUE_HPP
<|endoftext|>


<commit_before>//===- MCExpr.cpp - Assembly Level Expression Implementation --------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

void MCExpr::print(raw_ostream &OS, const MCAsmInfo *MAI) const {
  switch (getKind()) {
  case MCExpr::Constant:
    OS << cast<MCConstantExpr>(*this).getValue();
    return;

  case MCExpr::SymbolRef:
    cast<MCSymbolRefExpr>(*this).getSymbol().print(OS, MAI);
    return;

  case MCExpr::Unary: {
    const MCUnaryExpr &UE = cast<MCUnaryExpr>(*this);
    switch (UE.getOpcode()) {
    default: assert(0 && "Invalid opcode!");
    case MCUnaryExpr::LNot:  OS << '!'; break;
    case MCUnaryExpr::Minus: OS << '-'; break;
    case MCUnaryExpr::Not:   OS << '~'; break;
    case MCUnaryExpr::Plus:  OS << '+'; break;
    }
    UE.getSubExpr()->print(OS, MAI);
    return;
  }

  case MCExpr::Binary: {
    const MCBinaryExpr &BE = cast<MCBinaryExpr>(*this);

    // Only print parens around the LHS if it is non-trivial.
    if (isa<MCConstantExpr>(BE.getLHS()) || isa<MCSymbolRefExpr>(BE.getLHS())) {
      BE.getLHS()->print(OS, MAI);
    } else {
      OS << '(';
      BE.getLHS()->print(OS, MAI);
      OS << ')';
    }

    switch (BE.getOpcode()) {
    default: assert(0 && "Invalid opcode!");
    case MCBinaryExpr::Add:
      // Print "X-42" instead of "X+-42".
      if (const MCConstantExpr *RHSC = dyn_cast<MCConstantExpr>(BE.getRHS())) {
        if (RHSC->getValue() < 0) {
          OS << RHSC->getValue();
          return;
        }
      }

      OS <<  '+';
      break;
    case MCBinaryExpr::And:  OS <<  '&'; break;
    case MCBinaryExpr::Div:  OS <<  '/'; break;
    case MCBinaryExpr::EQ:   OS << "=="; break;
    case MCBinaryExpr::GT:   OS <<  '>'; break;
    case MCBinaryExpr::GTE:  OS << ">="; break;
    case MCBinaryExpr::LAnd: OS << "&&"; break;
    case MCBinaryExpr::LOr:  OS << "||"; break;
    case MCBinaryExpr::LT:   OS <<  '<'; break;
    case MCBinaryExpr::LTE:  OS << "<="; break;
    case MCBinaryExpr::Mod:  OS <<  '%'; break;
    case MCBinaryExpr::Mul:  OS <<  '*'; break;
    case MCBinaryExpr::NE:   OS << "!="; break;
    case MCBinaryExpr::Or:   OS <<  '|'; break;
    case MCBinaryExpr::Shl:  OS << "<<"; break;
    case MCBinaryExpr::Shr:  OS << ">>"; break;
    case MCBinaryExpr::Sub:  OS <<  '-'; break;
    case MCBinaryExpr::Xor:  OS <<  '^'; break;
    }

    // Only print parens around the LHS if it is non-trivial.
    if (isa<MCConstantExpr>(BE.getRHS()) || isa<MCSymbolRefExpr>(BE.getRHS())) {
      BE.getRHS()->print(OS, MAI);
    } else {
      OS << '(';
      BE.getRHS()->print(OS, MAI);
      OS << ')';
    }
    return;
  }
  }

  assert(0 && "Invalid expression kind!");
}

void MCExpr::dump() const {
  print(errs(), 0);
  errs() << '\n';
}

/* *** */

const MCBinaryExpr *MCBinaryExpr::Create(Opcode Opc, const MCExpr *LHS,
                                         const MCExpr *RHS, MCContext &Ctx) {
  return new (Ctx) MCBinaryExpr(Opc, LHS, RHS);
}

const MCUnaryExpr *MCUnaryExpr::Create(Opcode Opc, const MCExpr *Expr,
                                       MCContext &Ctx) {
  return new (Ctx) MCUnaryExpr(Opc, Expr);
}

const MCConstantExpr *MCConstantExpr::Create(int64_t Value, MCContext &Ctx) {
  return new (Ctx) MCConstantExpr(Value);
}

const MCSymbolRefExpr *MCSymbolRefExpr::Create(const MCSymbol *Sym,
                                               MCContext &Ctx) {
  return new (Ctx) MCSymbolRefExpr(Sym);
}

/* *** */

bool MCExpr::EvaluateAsAbsolute(MCContext &Ctx, int64_t &Res) const {
  MCValue Value;

  if (!EvaluateAsRelocatable(Ctx, Value) || !Value.isAbsolute())
    return false;

  Res = Value.getConstant();
  return true;
}

static bool EvaluateSymbolicAdd(const MCValue &LHS, const MCSymbol *RHS_A,
                                const MCSymbol *RHS_B, int64_t RHS_Cst,
                                MCValue &Res) {
  // We can't add or subtract two symbols.
  if ((LHS.getSymA() && RHS_A) ||
      (LHS.getSymB() && RHS_B))
    return false;

  const MCSymbol *A = LHS.getSymA() ? LHS.getSymA() : RHS_A;
  const MCSymbol *B = LHS.getSymB() ? LHS.getSymB() : RHS_B;
  if (B) {
    // If we have a negated symbol, then we must have also have a non-negated
    // symbol in order to encode the expression. We can do this check later to
    // permit expressions which eventually fold to a representable form -- such
    // as (a + (0 - b)) -- if necessary.
    if (!A)
      return false;
  }
  Res = MCValue::get(A, B, LHS.getConstant() + RHS_Cst);
  return true;
}

bool MCExpr::EvaluateAsRelocatable(MCContext &Ctx, MCValue &Res) const {
  switch (getKind()) {
  case Constant:
    Res = MCValue::get(cast<MCConstantExpr>(this)->getValue());
    return true;

  case SymbolRef: {
    const MCSymbol &Sym = cast<MCSymbolRefExpr>(this)->getSymbol();
    if (const MCValue *Value = Ctx.GetSymbolValue(&Sym))
      Res = *Value;
    else
      Res = MCValue::get(&Sym, 0, 0);
    return true;
  }

  case Unary: {
    const MCUnaryExpr *AUE = cast<MCUnaryExpr>(this);
    MCValue Value;

    if (!AUE->getSubExpr()->EvaluateAsRelocatable(Ctx, Value))
      return false;

    switch (AUE->getOpcode()) {
    case MCUnaryExpr::LNot:
      if (!Value.isAbsolute())
        return false;
      Res = MCValue::get(!Value.getConstant());
      break;
    case MCUnaryExpr::Minus:
      /// -(a - b + const) ==> (b - a - const)
      if (Value.getSymA() && !Value.getSymB())
        return false;
      Res = MCValue::get(Value.getSymB(), Value.getSymA(),
                         -Value.getConstant());
      break;
    case MCUnaryExpr::Not:
      if (!Value.isAbsolute())
        return false;
      Res = MCValue::get(~Value.getConstant());
      break;
    case MCUnaryExpr::Plus:
      Res = Value;
      break;
    }

    return true;
  }

  case Binary: {
    const MCBinaryExpr *ABE = cast<MCBinaryExpr>(this);
    MCValue LHSValue, RHSValue;

    if (!ABE->getLHS()->EvaluateAsRelocatable(Ctx, LHSValue) ||
        !ABE->getRHS()->EvaluateAsRelocatable(Ctx, RHSValue))
      return false;

    // We only support a few operations on non-constant expressions, handle
    // those first.
    if (!LHSValue.isAbsolute() || !RHSValue.isAbsolute()) {
      switch (ABE->getOpcode()) {
      default:
        return false;
      case MCBinaryExpr::Sub:
        // Negate RHS and add.
        return EvaluateSymbolicAdd(LHSValue,
                                   RHSValue.getSymB(), RHSValue.getSymA(),
                                   -RHSValue.getConstant(),
                                   Res);

      case MCBinaryExpr::Add:
        return EvaluateSymbolicAdd(LHSValue,
                                   RHSValue.getSymA(), RHSValue.getSymB(),
                                   RHSValue.getConstant(),
                                   Res);
      }
    }

    // FIXME: We need target hooks for the evaluation. It may be limited in
    // width, and gas defines the result of comparisons differently from Apple
    // as (the result is sign extended).
    int64_t LHS = LHSValue.getConstant(), RHS = RHSValue.getConstant();
    int64_t Result = 0;
    switch (ABE->getOpcode()) {
    case MCBinaryExpr::Add:  Result = LHS + RHS; break;
    case MCBinaryExpr::And:  Result = LHS & RHS; break;
    case MCBinaryExpr::Div:  Result = LHS / RHS; break;
    case MCBinaryExpr::EQ:   Result = LHS == RHS; break;
    case MCBinaryExpr::GT:   Result = LHS > RHS; break;
    case MCBinaryExpr::GTE:  Result = LHS >= RHS; break;
    case MCBinaryExpr::LAnd: Result = LHS && RHS; break;
    case MCBinaryExpr::LOr:  Result = LHS || RHS; break;
    case MCBinaryExpr::LT:   Result = LHS < RHS; break;
    case MCBinaryExpr::LTE:  Result = LHS <= RHS; break;
    case MCBinaryExpr::Mod:  Result = LHS % RHS; break;
    case MCBinaryExpr::Mul:  Result = LHS * RHS; break;
    case MCBinaryExpr::NE:   Result = LHS != RHS; break;
    case MCBinaryExpr::Or:   Result = LHS | RHS; break;
    case MCBinaryExpr::Shl:  Result = LHS << RHS; break;
    case MCBinaryExpr::Shr:  Result = LHS >> RHS; break;
    case MCBinaryExpr::Sub:  Result = LHS - RHS; break;
    case MCBinaryExpr::Xor:  Result = LHS ^ RHS; break;
    }

    Res = MCValue::get(Result);
    return true;
  }
  }

  assert(0 && "Invalid assembly expression kind!");
  return false;
}
<commit_msg>parenthesize symbol names that start with $, fixing X86/dollar-name.ll with the new asmprinter.<commit_after>//===- MCExpr.cpp - Assembly Level Expression Implementation --------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

void MCExpr::print(raw_ostream &OS, const MCAsmInfo *MAI) const {
  switch (getKind()) {
  case MCExpr::Constant:
    OS << cast<MCConstantExpr>(*this).getValue();
    return;

  case MCExpr::SymbolRef: {
    const MCSymbol &Sym = cast<MCSymbolRefExpr>(*this).getSymbol();

    // Parenthesize names that start with $ so that they don't look like
    // absolute names.
    if (Sym.getName()[0] == '$') {
      OS << '(';
      Sym.print(OS, MAI);
      OS << ')';
    } else {
      Sym.print(OS, MAI);
    }
    return;
  }

  case MCExpr::Unary: {
    const MCUnaryExpr &UE = cast<MCUnaryExpr>(*this);
    switch (UE.getOpcode()) {
    default: assert(0 && "Invalid opcode!");
    case MCUnaryExpr::LNot:  OS << '!'; break;
    case MCUnaryExpr::Minus: OS << '-'; break;
    case MCUnaryExpr::Not:   OS << '~'; break;
    case MCUnaryExpr::Plus:  OS << '+'; break;
    }
    UE.getSubExpr()->print(OS, MAI);
    return;
  }

  case MCExpr::Binary: {
    const MCBinaryExpr &BE = cast<MCBinaryExpr>(*this);

    // Only print parens around the LHS if it is non-trivial.
    if (isa<MCConstantExpr>(BE.getLHS()) || isa<MCSymbolRefExpr>(BE.getLHS())) {
      BE.getLHS()->print(OS, MAI);
    } else {
      OS << '(';
      BE.getLHS()->print(OS, MAI);
      OS << ')';
    }

    switch (BE.getOpcode()) {
    default: assert(0 && "Invalid opcode!");
    case MCBinaryExpr::Add:
      // Print "X-42" instead of "X+-42".
      if (const MCConstantExpr *RHSC = dyn_cast<MCConstantExpr>(BE.getRHS())) {
        if (RHSC->getValue() < 0) {
          OS << RHSC->getValue();
          return;
        }
      }

      OS <<  '+';
      break;
    case MCBinaryExpr::And:  OS <<  '&'; break;
    case MCBinaryExpr::Div:  OS <<  '/'; break;
    case MCBinaryExpr::EQ:   OS << "=="; break;
    case MCBinaryExpr::GT:   OS <<  '>'; break;
    case MCBinaryExpr::GTE:  OS << ">="; break;
    case MCBinaryExpr::LAnd: OS << "&&"; break;
    case MCBinaryExpr::LOr:  OS << "||"; break;
    case MCBinaryExpr::LT:   OS <<  '<'; break;
    case MCBinaryExpr::LTE:  OS << "<="; break;
    case MCBinaryExpr::Mod:  OS <<  '%'; break;
    case MCBinaryExpr::Mul:  OS <<  '*'; break;
    case MCBinaryExpr::NE:   OS << "!="; break;
    case MCBinaryExpr::Or:   OS <<  '|'; break;
    case MCBinaryExpr::Shl:  OS << "<<"; break;
    case MCBinaryExpr::Shr:  OS << ">>"; break;
    case MCBinaryExpr::Sub:  OS <<  '-'; break;
    case MCBinaryExpr::Xor:  OS <<  '^'; break;
    }

    // Only print parens around the LHS if it is non-trivial.
    if (isa<MCConstantExpr>(BE.getRHS()) || isa<MCSymbolRefExpr>(BE.getRHS())) {
      BE.getRHS()->print(OS, MAI);
    } else {
      OS << '(';
      BE.getRHS()->print(OS, MAI);
      OS << ')';
    }
    return;
  }
  }

  assert(0 && "Invalid expression kind!");
}

void MCExpr::dump() const {
  print(errs(), 0);
  errs() << '\n';
}

/* *** */

const MCBinaryExpr *MCBinaryExpr::Create(Opcode Opc, const MCExpr *LHS,
                                         const MCExpr *RHS, MCContext &Ctx) {
  return new (Ctx) MCBinaryExpr(Opc, LHS, RHS);
}

const MCUnaryExpr *MCUnaryExpr::Create(Opcode Opc, const MCExpr *Expr,
                                       MCContext &Ctx) {
  return new (Ctx) MCUnaryExpr(Opc, Expr);
}

const MCConstantExpr *MCConstantExpr::Create(int64_t Value, MCContext &Ctx) {
  return new (Ctx) MCConstantExpr(Value);
}

const MCSymbolRefExpr *MCSymbolRefExpr::Create(const MCSymbol *Sym,
                                               MCContext &Ctx) {
  return new (Ctx) MCSymbolRefExpr(Sym);
}

/* *** */

bool MCExpr::EvaluateAsAbsolute(MCContext &Ctx, int64_t &Res) const {
  MCValue Value;

  if (!EvaluateAsRelocatable(Ctx, Value) || !Value.isAbsolute())
    return false;

  Res = Value.getConstant();
  return true;
}

static bool EvaluateSymbolicAdd(const MCValue &LHS, const MCSymbol *RHS_A,
                                const MCSymbol *RHS_B, int64_t RHS_Cst,
                                MCValue &Res) {
  // We can't add or subtract two symbols.
  if ((LHS.getSymA() && RHS_A) ||
      (LHS.getSymB() && RHS_B))
    return false;

  const MCSymbol *A = LHS.getSymA() ? LHS.getSymA() : RHS_A;
  const MCSymbol *B = LHS.getSymB() ? LHS.getSymB() : RHS_B;
  if (B) {
    // If we have a negated symbol, then we must have also have a non-negated
    // symbol in order to encode the expression. We can do this check later to
    // permit expressions which eventually fold to a representable form -- such
    // as (a + (0 - b)) -- if necessary.
    if (!A)
      return false;
  }
  Res = MCValue::get(A, B, LHS.getConstant() + RHS_Cst);
  return true;
}

bool MCExpr::EvaluateAsRelocatable(MCContext &Ctx, MCValue &Res) const {
  switch (getKind()) {
  case Constant:
    Res = MCValue::get(cast<MCConstantExpr>(this)->getValue());
    return true;

  case SymbolRef: {
    const MCSymbol &Sym = cast<MCSymbolRefExpr>(this)->getSymbol();
    if (const MCValue *Value = Ctx.GetSymbolValue(&Sym))
      Res = *Value;
    else
      Res = MCValue::get(&Sym, 0, 0);
    return true;
  }

  case Unary: {
    const MCUnaryExpr *AUE = cast<MCUnaryExpr>(this);
    MCValue Value;

    if (!AUE->getSubExpr()->EvaluateAsRelocatable(Ctx, Value))
      return false;

    switch (AUE->getOpcode()) {
    case MCUnaryExpr::LNot:
      if (!Value.isAbsolute())
        return false;
      Res = MCValue::get(!Value.getConstant());
      break;
    case MCUnaryExpr::Minus:
      /// -(a - b + const) ==> (b - a - const)
      if (Value.getSymA() && !Value.getSymB())
        return false;
      Res = MCValue::get(Value.getSymB(), Value.getSymA(),
                         -Value.getConstant());
      break;
    case MCUnaryExpr::Not:
      if (!Value.isAbsolute())
        return false;
      Res = MCValue::get(~Value.getConstant());
      break;
    case MCUnaryExpr::Plus:
      Res = Value;
      break;
    }

    return true;
  }

  case Binary: {
    const MCBinaryExpr *ABE = cast<MCBinaryExpr>(this);
    MCValue LHSValue, RHSValue;

    if (!ABE->getLHS()->EvaluateAsRelocatable(Ctx, LHSValue) ||
        !ABE->getRHS()->EvaluateAsRelocatable(Ctx, RHSValue))
      return false;

    // We only support a few operations on non-constant expressions, handle
    // those first.
    if (!LHSValue.isAbsolute() || !RHSValue.isAbsolute()) {
      switch (ABE->getOpcode()) {
      default:
        return false;
      case MCBinaryExpr::Sub:
        // Negate RHS and add.
        return EvaluateSymbolicAdd(LHSValue,
                                   RHSValue.getSymB(), RHSValue.getSymA(),
                                   -RHSValue.getConstant(),
                                   Res);

      case MCBinaryExpr::Add:
        return EvaluateSymbolicAdd(LHSValue,
                                   RHSValue.getSymA(), RHSValue.getSymB(),
                                   RHSValue.getConstant(),
                                   Res);
      }
    }

    // FIXME: We need target hooks for the evaluation. It may be limited in
    // width, and gas defines the result of comparisons differently from Apple
    // as (the result is sign extended).
    int64_t LHS = LHSValue.getConstant(), RHS = RHSValue.getConstant();
    int64_t Result = 0;
    switch (ABE->getOpcode()) {
    case MCBinaryExpr::Add:  Result = LHS + RHS; break;
    case MCBinaryExpr::And:  Result = LHS & RHS; break;
    case MCBinaryExpr::Div:  Result = LHS / RHS; break;
    case MCBinaryExpr::EQ:   Result = LHS == RHS; break;
    case MCBinaryExpr::GT:   Result = LHS > RHS; break;
    case MCBinaryExpr::GTE:  Result = LHS >= RHS; break;
    case MCBinaryExpr::LAnd: Result = LHS && RHS; break;
    case MCBinaryExpr::LOr:  Result = LHS || RHS; break;
    case MCBinaryExpr::LT:   Result = LHS < RHS; break;
    case MCBinaryExpr::LTE:  Result = LHS <= RHS; break;
    case MCBinaryExpr::Mod:  Result = LHS % RHS; break;
    case MCBinaryExpr::Mul:  Result = LHS * RHS; break;
    case MCBinaryExpr::NE:   Result = LHS != RHS; break;
    case MCBinaryExpr::Or:   Result = LHS | RHS; break;
    case MCBinaryExpr::Shl:  Result = LHS << RHS; break;
    case MCBinaryExpr::Shr:  Result = LHS >> RHS; break;
    case MCBinaryExpr::Sub:  Result = LHS - RHS; break;
    case MCBinaryExpr::Xor:  Result = LHS ^ RHS; break;
    }

    Res = MCValue::get(Result);
    return true;
  }
  }

  assert(0 && "Invalid assembly expression kind!");
  return false;
}
<|endoftext|>


<commit_before>#include "globals.hh"
#include "shared.hh"
#include "store-api.hh"
#include "util.hh"
#include "loggers.hh"

#include <algorithm>
#include <cctype>
#include <exception>
#include <iostream>
#include <mutex>

#include <cstdlib>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#ifdef __linux__
#include <features.h>
#endif
#ifdef __GLIBC__
#include <gnu/lib-names.h>
#include <nss.h>
#include <dlfcn.h>
#endif

#include <openssl/crypto.h>

#include <sodium.h>

namespace nix {

static bool gcWarning = true;

void printGCWarning()
{
    if (!gcWarning) return;
    static bool haveWarned = false;
    warnOnce(haveWarned,
        "you did not specify '--add-root'; "
        "the result might be removed by the garbage collector");
}

void printMissing(ref<Store> store, const std::vector<DerivedPath> & paths, Verbosity lvl)
{
    uint64_t downloadSize, narSize;
    StorePathSet willBuild, willSubstitute, unknown;
    store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
    printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize, lvl);
}

void printMissing(ref<Store> store, const StorePathSet & willBuild,
    const StorePathSet & willSubstitute, const StorePathSet & unknown,
    uint64_t downloadSize, uint64_t narSize, Verbosity lvl)
{
    if (!willBuild.empty()) {
        if (willBuild.size() == 1)
            printMsg(lvl, fmt("this derivation will be built:"));
        else
            printMsg(lvl, fmt("these %d derivations will be built:", willBuild.size()));
        auto sorted = store->topoSortPaths(willBuild);
        reverse(sorted.begin(), sorted.end());
        for (auto & i : sorted)
            printMsg(lvl, fmt("  %s", store->printStorePath(i)));
    }

    if (!willSubstitute.empty()) {
        const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
        const float narSizeMiB = narSize / (1024.f * 1024.f);
        if (willSubstitute.size() == 1) {
            printMsg(lvl, fmt("this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
                downloadSizeMiB,
                narSizeMiB));
        } else {
            printMsg(lvl, fmt("these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
                willSubstitute.size(),
                downloadSizeMiB,
                narSizeMiB));
        }
        for (auto & i : willSubstitute)
            printMsg(lvl, fmt("  %s", store->printStorePath(i)));
    }

    if (!unknown.empty()) {
        printMsg(lvl, fmt("don't know how to build these paths%s:",
                (settings.readOnlyMode ? " (may be caused by read-only store access)" : "")));
        for (auto & i : unknown)
            printMsg(lvl, fmt("  %s", store->printStorePath(i)));
    }
}

std::string getArg(const std::string & opt,
    Strings::iterator & i, const Strings::iterator & end)
{
    ++i;
    if (i == end) throw UsageError("'%1%' requires an argument", opt);
    return *i;
}

#if OPENSSL_VERSION_NUMBER < 0x10101000L
/* OpenSSL is not thread-safe by default - it will randomly crash
   unless the user supplies a mutex locking function. So let's do
   that. */
static std::vector<std::mutex> opensslLocks;

static void opensslLockCallback(int mode, int type, const char * file, int line)
{
    if (mode & CRYPTO_LOCK)
        opensslLocks[type].lock();
    else
        opensslLocks[type].unlock();
}
#endif

static std::once_flag dns_resolve_flag;

static void preloadNSS() {
    /* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of
       one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already
       been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
       load its lookup libraries in the parent before any child gets a chance to. */
    std::call_once(dns_resolve_flag, []() {
#ifdef __GLIBC__
        /* On linux, glibc will run every lookup through the nss layer.
         * That means every lookup goes, by default, through nscd, which acts as a local
         * cache.
         * Because we run builds in a sandbox, we also remove access to nscd otherwise
         * lookups would leak into the sandbox.
         *
         * But now we have a new problem, we need to make sure the nss_dns backend that
         * does the dns lookups when nscd is not available is loaded or available.
         *
         * We can't make it available without leaking nix's environment, so instead we'll
         * load the backend, and configure nss so it does not try to run dns lookups
         * through nscd.
         *
         * This is technically only used for builtins:fetch* functions so we only care
         * about dns.
         *
         * All other platforms are unaffected.
         */
        if (!dlopen(LIBNSS_DNS_SO, RTLD_NOW))
            warn("unable to load nss_dns backend");
        // FIXME: get hosts entry from nsswitch.conf.
        __nss_configure_lookup("hosts", "files dns");
#endif
    });
}

static void sigHandler(int signo) { }

void initNix()
{
    /* Turn on buffering for cerr. */
#if HAVE_PUBSETBUF
    static char buf[1024];
    std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
#endif

#if OPENSSL_VERSION_NUMBER < 0x10101000L
    /* Initialise OpenSSL locking. */
    opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks());
    CRYPTO_set_locking_callback(opensslLockCallback);
#endif

    if (sodium_init() == -1)
        throw Error("could not initialise libsodium");

    loadConfFile();

    startSignalHandlerThread();

    /* Reset SIGCHLD to its default. */
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_handler = SIG_DFL;
    act.sa_flags = 0;
    if (sigaction(SIGCHLD, &act, 0))
        throw SysError("resetting SIGCHLD");

    /* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
    act.sa_handler = sigHandler;
    if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1");

#if __APPLE__
    /* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH.
     * Instead, add a dummy sigaction handler, and signalHandlerThread
     * can handle the rest. */
    struct sigaction sa;
    sa.sa_handler = sigHandler;
    if (sigaction(SIGWINCH, &sa, 0)) throw SysError("handling SIGWINCH");
#endif

    /* Register a SIGSEGV handler to detect stack overflows. */
    detectStackOverflow();

    /* There is no privacy in the Nix system ;-)  At least not for
       now.  In particular, store objects should be readable by
       everybody. */
    umask(0022);

    /* Initialise the PRNG. */
    struct timeval tv;
    gettimeofday(&tv, 0);
    srandom(tv.tv_usec);

    /* On macOS, don't use the per-session TMPDIR (as set e.g. by
       sshd). This breaks build users because they don't have access
       to the TMPDIR, in particular in ‘nix-store --serve’. */
#if __APPLE__
    if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/"))
        unsetenv("TMPDIR");
#endif

    preloadNSS();
}

LegacyArgs::LegacyArgs(const std::string & programName,
    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
    : MixCommonArgs(programName), parseArg(parseArg)
{
    printError("FOO %s", programName);

    addFlag({
        .longName = "no-build-output",
        .shortName = 'Q',
        .description = "Do not show build output.",
        .handler = {[&]() {setLogFormat(LogFormat::raw); }},
    });

    addFlag({
        .longName = "keep-failed",
        .shortName ='K',
        .description = "Keep temporary directories of failed builds.",
        .handler = {&(bool&) settings.keepFailed, true},
    });

    addFlag({
        .longName = "keep-going",
        .shortName ='k',
        .description = "Keep going after a build fails.",
        .handler = {&(bool&) settings.keepGoing, true},
    });

    addFlag({
        .longName = "fallback",
        .description = "Build from source if substitution fails.",
        .handler = {&(bool&) settings.tryFallback, true},
    });

    auto intSettingAlias = [&](char shortName, const std::string & longName,
        const std::string & description, const std::string & dest)
    {
        addFlag({
            .longName = longName,
            .shortName = shortName,
            .description = description,
            .labels = {"n"},
            .handler = {[=](std::string s) {
                auto n = string2IntWithUnitPrefix<uint64_t>(s);
                settings.set(dest, std::to_string(n));
            }}
        });
    };

    intSettingAlias(0, "cores", "Maximum number of CPU cores to use inside a build.", "cores");
    intSettingAlias(0, "max-silent-time", "Number of seconds of silence before a build is killed.", "max-silent-time");
    intSettingAlias(0, "timeout", "Number of seconds before a build is killed.", "timeout");

    addFlag({
        .longName = "readonly-mode",
        .description = "Do not write to the Nix store.",
        .handler = {&settings.readOnlyMode, true},
    });

    addFlag({
        .longName = "no-gc-warning",
        .description = "Disable warnings about not using `--add-root`.",
        .handler = {&gcWarning, false},
    });

    addFlag({
        .longName = "store",
        .description = "The URL of the Nix store to use.",
        .labels = {"store-uri"},
        .handler = {&(std::string&) settings.storeUri},
    });
}

bool LegacyArgs::processFlag(Strings::iterator & pos, Strings::iterator end)
{
    if (MixCommonArgs::processFlag(pos, end)) return true;
    bool res = parseArg(pos, end);
    if (res) ++pos;
    return res;
}

bool LegacyArgs::processArgs(const Strings & args, bool finish)
{
    if (args.empty()) return true;
    assert(args.size() == 1);
    Strings ss(args);
    auto pos = ss.begin();
    if (!parseArg(pos, ss.end()))
        throw UsageError("unexpected argument '%1%'", args.front());
    return true;
}

void parseCmdLine(int argc, char * * argv,
    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
{
    parseCmdLine(std::string(baseNameOf(argv[0])), argvToStrings(argc, argv), parseArg);
}

void parseCmdLine(const std::string & programName, const Strings & args,
    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
{
    LegacyArgs(programName, parseArg).parseCmdline(args);
}

void printVersion(const std::string & programName)
{
    std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
    if (verbosity > lvlInfo) {
        Strings cfg;
#if HAVE_BOEHMGC
        cfg.push_back("gc");
#endif
        cfg.push_back("signed-caches");
        std::cout << "System type: " << settings.thisSystem << "\n";
        std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n";
        std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
        std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n";
        std::cout << "User configuration files: " <<
            concatStringsSep(":", settings.nixUserConfFiles)
            << "\n";
        std::cout << "Store directory: " << settings.nixStore << "\n";
        std::cout << "State directory: " << settings.nixStateDir << "\n";
    }
    throw Exit();
}

void showManPage(const std::string & name)
{
    restoreProcessContext();
    setenv("MANPATH", settings.nixManDir.c_str(), 1);
    execlp("man", "man", name.c_str(), nullptr);
    throw SysError("command 'man %1%' failed", name.c_str());
}

int handleExceptions(const std::string & programName, std::function<void()> fun)
{
    ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this

    ErrorInfo::programName = baseNameOf(programName);

    std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
    try {
        try {
            fun();
        } catch (...) {
            /* Subtle: we have to make sure that any `interrupted'
               condition is discharged before we reach printMsg()
               below, since otherwise it will throw an (uncaught)
               exception. */
            setInterruptThrown();
            throw;
        }
    } catch (Exit & e) {
        return e.status;
    } catch (UsageError & e) {
        logError(e.info());
        printError("Try '%1% --help' for more information.", programName);
        return 1;
    } catch (BaseError & e) {
        logError(e.info());
        if (e.hasTrace() && !loggerSettings.showTrace.get())
            printError("(use '--show-trace' to show detailed location information)");
        return e.status;
    } catch (std::bad_alloc & e) {
        printError(error + "out of memory");
        return 1;
    } catch (std::exception & e) {
        printError(error + e.what());
        return 1;
    }

    return 0;
}

RunPager::RunPager()
{
    if (!isatty(STDOUT_FILENO)) return;
    char * pager = getenv("NIX_PAGER");
    if (!pager) pager = getenv("PAGER");
    if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;

    Pipe toPager;
    toPager.create();

    pid = startProcess([&]() {
        if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
            throw SysError("dupping stdin");
        if (!getenv("LESS"))
            setenv("LESS", "FRSXMK", 1);
        restoreProcessContext();
        if (pager)
            execl("/bin/sh", "sh", "-c", pager, nullptr);
        execlp("pager", "pager", nullptr);
        execlp("less", "less", nullptr);
        execlp("more", "more", nullptr);
        throw SysError("executing '%1%'", pager);
    });

    pid.setKillSignal(SIGINT);
    stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
    if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
        throw SysError("dupping stdout");
}

RunPager::~RunPager()
{
    try {
        if (pid != -1) {
            std::cout.flush();
            dup2(stdout, STDOUT_FILENO);
            pid.wait();
        }
    } catch (...) {
        ignoreException();
    }
}

PrintFreed::~PrintFreed()
{
    if (show)
        std::cout << fmt("%d store paths deleted, %s freed\n",
            results.paths.size(),
            showBytes(results.bytesFreed));
}

Exit::~Exit() { }

}
<commit_msg>Remove stray debug line<commit_after>#include "globals.hh"
#include "shared.hh"
#include "store-api.hh"
#include "util.hh"
#include "loggers.hh"

#include <algorithm>
#include <cctype>
#include <exception>
#include <iostream>
#include <mutex>

#include <cstdlib>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#ifdef __linux__
#include <features.h>
#endif
#ifdef __GLIBC__
#include <gnu/lib-names.h>
#include <nss.h>
#include <dlfcn.h>
#endif

#include <openssl/crypto.h>

#include <sodium.h>

namespace nix {

static bool gcWarning = true;

void printGCWarning()
{
    if (!gcWarning) return;
    static bool haveWarned = false;
    warnOnce(haveWarned,
        "you did not specify '--add-root'; "
        "the result might be removed by the garbage collector");
}

void printMissing(ref<Store> store, const std::vector<DerivedPath> & paths, Verbosity lvl)
{
    uint64_t downloadSize, narSize;
    StorePathSet willBuild, willSubstitute, unknown;
    store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
    printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize, lvl);
}

void printMissing(ref<Store> store, const StorePathSet & willBuild,
    const StorePathSet & willSubstitute, const StorePathSet & unknown,
    uint64_t downloadSize, uint64_t narSize, Verbosity lvl)
{
    if (!willBuild.empty()) {
        if (willBuild.size() == 1)
            printMsg(lvl, fmt("this derivation will be built:"));
        else
            printMsg(lvl, fmt("these %d derivations will be built:", willBuild.size()));
        auto sorted = store->topoSortPaths(willBuild);
        reverse(sorted.begin(), sorted.end());
        for (auto & i : sorted)
            printMsg(lvl, fmt("  %s", store->printStorePath(i)));
    }

    if (!willSubstitute.empty()) {
        const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
        const float narSizeMiB = narSize / (1024.f * 1024.f);
        if (willSubstitute.size() == 1) {
            printMsg(lvl, fmt("this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
                downloadSizeMiB,
                narSizeMiB));
        } else {
            printMsg(lvl, fmt("these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
                willSubstitute.size(),
                downloadSizeMiB,
                narSizeMiB));
        }
        for (auto & i : willSubstitute)
            printMsg(lvl, fmt("  %s", store->printStorePath(i)));
    }

    if (!unknown.empty()) {
        printMsg(lvl, fmt("don't know how to build these paths%s:",
                (settings.readOnlyMode ? " (may be caused by read-only store access)" : "")));
        for (auto & i : unknown)
            printMsg(lvl, fmt("  %s", store->printStorePath(i)));
    }
}

std::string getArg(const std::string & opt,
    Strings::iterator & i, const Strings::iterator & end)
{
    ++i;
    if (i == end) throw UsageError("'%1%' requires an argument", opt);
    return *i;
}

#if OPENSSL_VERSION_NUMBER < 0x10101000L
/* OpenSSL is not thread-safe by default - it will randomly crash
   unless the user supplies a mutex locking function. So let's do
   that. */
static std::vector<std::mutex> opensslLocks;

static void opensslLockCallback(int mode, int type, const char * file, int line)
{
    if (mode & CRYPTO_LOCK)
        opensslLocks[type].lock();
    else
        opensslLocks[type].unlock();
}
#endif

static std::once_flag dns_resolve_flag;

static void preloadNSS() {
    /* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of
       one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already
       been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
       load its lookup libraries in the parent before any child gets a chance to. */
    std::call_once(dns_resolve_flag, []() {
#ifdef __GLIBC__
        /* On linux, glibc will run every lookup through the nss layer.
         * That means every lookup goes, by default, through nscd, which acts as a local
         * cache.
         * Because we run builds in a sandbox, we also remove access to nscd otherwise
         * lookups would leak into the sandbox.
         *
         * But now we have a new problem, we need to make sure the nss_dns backend that
         * does the dns lookups when nscd is not available is loaded or available.
         *
         * We can't make it available without leaking nix's environment, so instead we'll
         * load the backend, and configure nss so it does not try to run dns lookups
         * through nscd.
         *
         * This is technically only used for builtins:fetch* functions so we only care
         * about dns.
         *
         * All other platforms are unaffected.
         */
        if (!dlopen(LIBNSS_DNS_SO, RTLD_NOW))
            warn("unable to load nss_dns backend");
        // FIXME: get hosts entry from nsswitch.conf.
        __nss_configure_lookup("hosts", "files dns");
#endif
    });
}

static void sigHandler(int signo) { }

void initNix()
{
    /* Turn on buffering for cerr. */
#if HAVE_PUBSETBUF
    static char buf[1024];
    std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
#endif

#if OPENSSL_VERSION_NUMBER < 0x10101000L
    /* Initialise OpenSSL locking. */
    opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks());
    CRYPTO_set_locking_callback(opensslLockCallback);
#endif

    if (sodium_init() == -1)
        throw Error("could not initialise libsodium");

    loadConfFile();

    startSignalHandlerThread();

    /* Reset SIGCHLD to its default. */
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_handler = SIG_DFL;
    act.sa_flags = 0;
    if (sigaction(SIGCHLD, &act, 0))
        throw SysError("resetting SIGCHLD");

    /* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
    act.sa_handler = sigHandler;
    if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1");

#if __APPLE__
    /* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH.
     * Instead, add a dummy sigaction handler, and signalHandlerThread
     * can handle the rest. */
    struct sigaction sa;
    sa.sa_handler = sigHandler;
    if (sigaction(SIGWINCH, &sa, 0)) throw SysError("handling SIGWINCH");
#endif

    /* Register a SIGSEGV handler to detect stack overflows. */
    detectStackOverflow();

    /* There is no privacy in the Nix system ;-)  At least not for
       now.  In particular, store objects should be readable by
       everybody. */
    umask(0022);

    /* Initialise the PRNG. */
    struct timeval tv;
    gettimeofday(&tv, 0);
    srandom(tv.tv_usec);

    /* On macOS, don't use the per-session TMPDIR (as set e.g. by
       sshd). This breaks build users because they don't have access
       to the TMPDIR, in particular in ‘nix-store --serve’. */
#if __APPLE__
    if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/"))
        unsetenv("TMPDIR");
#endif

    preloadNSS();
}

LegacyArgs::LegacyArgs(const std::string & programName,
    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
    : MixCommonArgs(programName), parseArg(parseArg)
{
    addFlag({
        .longName = "no-build-output",
        .shortName = 'Q',
        .description = "Do not show build output.",
        .handler = {[&]() {setLogFormat(LogFormat::raw); }},
    });

    addFlag({
        .longName = "keep-failed",
        .shortName ='K',
        .description = "Keep temporary directories of failed builds.",
        .handler = {&(bool&) settings.keepFailed, true},
    });

    addFlag({
        .longName = "keep-going",
        .shortName ='k',
        .description = "Keep going after a build fails.",
        .handler = {&(bool&) settings.keepGoing, true},
    });

    addFlag({
        .longName = "fallback",
        .description = "Build from source if substitution fails.",
        .handler = {&(bool&) settings.tryFallback, true},
    });

    auto intSettingAlias = [&](char shortName, const std::string & longName,
        const std::string & description, const std::string & dest)
    {
        addFlag({
            .longName = longName,
            .shortName = shortName,
            .description = description,
            .labels = {"n"},
            .handler = {[=](std::string s) {
                auto n = string2IntWithUnitPrefix<uint64_t>(s);
                settings.set(dest, std::to_string(n));
            }}
        });
    };

    intSettingAlias(0, "cores", "Maximum number of CPU cores to use inside a build.", "cores");
    intSettingAlias(0, "max-silent-time", "Number of seconds of silence before a build is killed.", "max-silent-time");
    intSettingAlias(0, "timeout", "Number of seconds before a build is killed.", "timeout");

    addFlag({
        .longName = "readonly-mode",
        .description = "Do not write to the Nix store.",
        .handler = {&settings.readOnlyMode, true},
    });

    addFlag({
        .longName = "no-gc-warning",
        .description = "Disable warnings about not using `--add-root`.",
        .handler = {&gcWarning, false},
    });

    addFlag({
        .longName = "store",
        .description = "The URL of the Nix store to use.",
        .labels = {"store-uri"},
        .handler = {&(std::string&) settings.storeUri},
    });
}

bool LegacyArgs::processFlag(Strings::iterator & pos, Strings::iterator end)
{
    if (MixCommonArgs::processFlag(pos, end)) return true;
    bool res = parseArg(pos, end);
    if (res) ++pos;
    return res;
}

bool LegacyArgs::processArgs(const Strings & args, bool finish)
{
    if (args.empty()) return true;
    assert(args.size() == 1);
    Strings ss(args);
    auto pos = ss.begin();
    if (!parseArg(pos, ss.end()))
        throw UsageError("unexpected argument '%1%'", args.front());
    return true;
}

void parseCmdLine(int argc, char * * argv,
    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
{
    parseCmdLine(std::string(baseNameOf(argv[0])), argvToStrings(argc, argv), parseArg);
}

void parseCmdLine(const std::string & programName, const Strings & args,
    std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
{
    LegacyArgs(programName, parseArg).parseCmdline(args);
}

void printVersion(const std::string & programName)
{
    std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
    if (verbosity > lvlInfo) {
        Strings cfg;
#if HAVE_BOEHMGC
        cfg.push_back("gc");
#endif
        cfg.push_back("signed-caches");
        std::cout << "System type: " << settings.thisSystem << "\n";
        std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n";
        std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
        std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n";
        std::cout << "User configuration files: " <<
            concatStringsSep(":", settings.nixUserConfFiles)
            << "\n";
        std::cout << "Store directory: " << settings.nixStore << "\n";
        std::cout << "State directory: " << settings.nixStateDir << "\n";
    }
    throw Exit();
}

void showManPage(const std::string & name)
{
    restoreProcessContext();
    setenv("MANPATH", settings.nixManDir.c_str(), 1);
    execlp("man", "man", name.c_str(), nullptr);
    throw SysError("command 'man %1%' failed", name.c_str());
}

int handleExceptions(const std::string & programName, std::function<void()> fun)
{
    ReceiveInterrupts receiveInterrupts; // FIXME: need better place for this

    ErrorInfo::programName = baseNameOf(programName);

    std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
    try {
        try {
            fun();
        } catch (...) {
            /* Subtle: we have to make sure that any `interrupted'
               condition is discharged before we reach printMsg()
               below, since otherwise it will throw an (uncaught)
               exception. */
            setInterruptThrown();
            throw;
        }
    } catch (Exit & e) {
        return e.status;
    } catch (UsageError & e) {
        logError(e.info());
        printError("Try '%1% --help' for more information.", programName);
        return 1;
    } catch (BaseError & e) {
        logError(e.info());
        if (e.hasTrace() && !loggerSettings.showTrace.get())
            printError("(use '--show-trace' to show detailed location information)");
        return e.status;
    } catch (std::bad_alloc & e) {
        printError(error + "out of memory");
        return 1;
    } catch (std::exception & e) {
        printError(error + e.what());
        return 1;
    }

    return 0;
}

RunPager::RunPager()
{
    if (!isatty(STDOUT_FILENO)) return;
    char * pager = getenv("NIX_PAGER");
    if (!pager) pager = getenv("PAGER");
    if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;

    Pipe toPager;
    toPager.create();

    pid = startProcess([&]() {
        if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
            throw SysError("dupping stdin");
        if (!getenv("LESS"))
            setenv("LESS", "FRSXMK", 1);
        restoreProcessContext();
        if (pager)
            execl("/bin/sh", "sh", "-c", pager, nullptr);
        execlp("pager", "pager", nullptr);
        execlp("less", "less", nullptr);
        execlp("more", "more", nullptr);
        throw SysError("executing '%1%'", pager);
    });

    pid.setKillSignal(SIGINT);
    stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
    if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
        throw SysError("dupping stdout");
}

RunPager::~RunPager()
{
    try {
        if (pid != -1) {
            std::cout.flush();
            dup2(stdout, STDOUT_FILENO);
            pid.wait();
        }
    } catch (...) {
        ignoreException();
    }
}

PrintFreed::~PrintFreed()
{
    if (show)
        std::cout << fmt("%d store paths deleted, %s freed\n",
            results.paths.size(),
            showBytes(results.bytesFreed));
}

Exit::~Exit() { }

}
<|endoftext|>


<commit_before>#include <wayfire/plugin.hpp>
#include <wayfire/view.hpp>
#include <wayfire/matcher.hpp>
#include <wayfire/output.hpp>
#include <wayfire/view-transform.hpp>
#include <wayfire/workspace-stream.hpp>
#include <wayfire/workspace-manager.hpp>
#include <wayfire/signal-definitions.hpp>

#include "blur.hpp"

using blur_algorithm_provider = std::function<nonstd::observer_ptr<wf_blur_base>()>;
class wf_blur_transformer : public wf::view_transformer_t
{
    blur_algorithm_provider provider;
    wf::output_t *output;
    wayfire_view view;

  public:
    wf_blur_transformer(blur_algorithm_provider blur_algorithm_provider,
        wf::output_t *output, wayfire_view view)
    {
        provider     = blur_algorithm_provider;
        this->output = output;
        this->view   = view;
    }

    wf::pointf_t transform_point(wf::geometry_t view,
        wf::pointf_t point) override
    {
        return point;
    }

    wf::pointf_t untransform_point(wf::geometry_t view,
        wf::pointf_t point) override
    {
        return point;
    }

    wlr_box get_bounding_box(wf::geometry_t view, wlr_box region) override
    {
        return region;
    }

    wf::region_t transform_opaque_region(
        wf::geometry_t bbox, wf::region_t region) override
    {
        return region;
    }

    uint32_t get_z_order() override
    {
        return wf::TRANSFORMER_BLUR;
    }

    /* Render without blending */
    void direct_render(wf::texture_t src_tex, wlr_box src_box,
        const wf::region_t& damage, const wf::framebuffer_t& target_fb)
    {
        OpenGL::render_begin(target_fb);
        for (auto& rect : damage)
        {
            target_fb.logic_scissor(wlr_box_from_pixman_box(rect));
            OpenGL::render_texture(src_tex, target_fb, src_box);
        }

        OpenGL::render_end();
    }

    void render_with_damage(wf::texture_t src_tex, wlr_box src_box,
        const wf::region_t& damage, const wf::framebuffer_t& target_fb) override
    {
        wf::region_t clip_damage = damage & src_box;

        /* We want to check if the opaque region completely occludes
         * the bounding box. If this is the case, we can skip blurring
         * altogether and just render the surface. First we disable
         * shrinking and get the opaque region without padding */
        wf::surface_interface_t::set_opaque_shrink_constraint("blur", 0);
        wf::region_t full_opaque = view->get_transformed_opaque_region();

        /* Shrink the opaque region by the padding amount since the render
         * chain expects this, as we have applied padding to damage in
         * frame_pre_paint for this frame already */
        int padding = std::ceil(provider()->calculate_blur_radius() /
            output->render->get_target_framebuffer().scale);
        wf::surface_interface_t::set_opaque_shrink_constraint("blur", padding);

        wf::region_t bbox_region{src_box};
        if ((bbox_region ^ full_opaque).empty())
        {
            /* In case the whole surface is opaque, we can simply skip blurring */
            direct_render(src_tex, src_box, damage, target_fb);

            return;
        }

        wf::region_t opaque_region  = view->get_transformed_opaque_region();
        wf::region_t blurred_region = clip_damage ^ opaque_region;

        provider()->pre_render(src_tex, src_box, blurred_region, target_fb);
        wf::view_transformer_t::render_with_damage(src_tex, src_box, blurred_region,
            target_fb);

        /* Opaque non-blurred regions can be rendered directly without blending */
        direct_render(src_tex, src_box, opaque_region & clip_damage, target_fb);
    }

    void render_box(wf::texture_t src_tex, wlr_box src_box, wlr_box scissor_box,
        const wf::framebuffer_t& target_fb) override
    {
        provider()->render(src_tex, src_box, scissor_box, target_fb);
    }
};

class wayfire_blur : public wf::plugin_interface_t
{
    wf::button_callback button_toggle;

    wf::effect_hook_t frame_pre_paint;
    wf::signal_callback_t workspace_stream_pre, workspace_stream_post,
        view_attached, view_detached;

    wf::view_matcher_t blur_by_default{"blur/blur_by_default"};
    wf::option_wrapper_t<std::string> method_opt{"blur/method"};
    wf::option_wrapper_t<wf::buttonbinding_t> toggle_button{"blur/toggle"};
    wf::config::option_base_t::updated_callback_t blur_method_changed;
    std::unique_ptr<wf_blur_base> blur_algorithm;

    const std::string transformer_name = "blur";

    /* the pixels from padded_region */
    wf::framebuffer_base_t saved_pixels;
    wf::region_t padded_region;

    void add_transformer(wayfire_view view)
    {
        if (view->get_transformer(transformer_name))
        {
            return;
        }

        view->add_transformer(std::make_unique<wf_blur_transformer>(
            [=] () {return nonstd::make_observer(blur_algorithm.get()); },
            output, view),
            transformer_name);
    }

    void pop_transformer(wayfire_view view)
    {
        if (view->get_transformer(transformer_name))
        {
            view->pop_transformer(transformer_name);
        }
    }

    void remove_transformers()
    {
        for (auto& view : output->workspace->get_views_in_layer(wf::ALL_LAYERS))
        {
            pop_transformer(view);
        }
    }

    /** Transform region into framebuffer coordinates */
    wf::region_t get_fb_region(const wf::region_t& region,
        const wf::framebuffer_t& fb) const
    {
        wf::region_t result;
        for (const auto& rect : region)
        {
            result |= fb.framebuffer_box_from_geometry_box(
                wlr_box_from_pixman_box(rect));
        }

        return result;
    }

  public:
    void init() override
    {
        grab_interface->name = "blur";
        grab_interface->capabilities = 0;

        blur_method_changed = [=] ()
        {
            blur_algorithm = create_blur_from_name(output, method_opt);
            output->render->damage_whole();
        };
        /* Create initial blur algorithm */
        blur_method_changed();
        method_opt.set_callback(blur_method_changed);

        /* Toggles the blur state of the view the user clicked on */
        button_toggle = [=] (uint32_t, int, int)
        {
            if (!output->can_activate_plugin(grab_interface))
            {
                return false;
            }

            auto view = wf::get_core().get_cursor_focus_view();
            if (!view)
            {
                return false;
            }

            if (view->get_transformer(transformer_name))
            {
                view->pop_transformer(transformer_name);
            } else
            {
                add_transformer(view);
            }

            return true;
        };
        output->add_button(toggle_button, &button_toggle);

        // Add blur transformers to views which have blur enabled
        view_attached = [=] (wf::signal_data_t *data)
        {
            auto view = get_signaled_view(data);
            /* View was just created -> we don't know its layer yet */
            if (!view->is_mapped())
            {
                return;
            }

            if (blur_by_default.matches(view))
            {
                add_transformer(view);
            }
        };

        /* If a view is detached, we remove its blur transformer.
         * If it is just moved to another output, the blur plugin
         * on the other output will add its own transformer there */
        view_detached = [=] (wf::signal_data_t *data)
        {
            auto view = get_signaled_view(data);
            pop_transformer(view);
        };
        output->connect_signal("view-attached", &view_attached);
        output->connect_signal("view-mapped", &view_attached);
        output->connect_signal("view-detached", &view_detached);

        /* frame_pre_paint is called before each frame has started.
         * It expands the damage by the blur radius.
         * This is needed, because when blurring, the pixels that changed
         * affect a larger area than the really damaged region, e.g the region
         * that comes from client damage */
        frame_pre_paint = [=] ()
        {
            auto damage    = output->render->get_scheduled_damage();
            const auto& fb = output->render->get_target_framebuffer();

            int padding = std::ceil(
                blur_algorithm->calculate_blur_radius() / fb.scale);
            wf::surface_interface_t::set_opaque_shrink_constraint("blur",
                padding);

            wf::region_t padded;
            for (const auto& rect : damage)
            {
                padded |= wlr_box{
                    (rect.x1 - padding),
                    (rect.y1 - padding),
                    (rect.x2 - rect.x1) + 2 * padding,
                    (rect.y2 - rect.y1) + 2 * padding
                };
            }

            output->render->damage(padded);
        };
        output->render->add_effect(&frame_pre_paint, wf::OUTPUT_EFFECT_DAMAGE);

        /* workspace_stream_pre is called before rendering each frame
         * when rendering a workspace. It gives us a chance to pad
         * damage and take a snapshot of the padded area. The padded
         * damage will be used to render the scene as normal. Then
         * workspace_stream_post is called so we can copy the padded
         * pixels back. */
        workspace_stream_pre = [=] (wf::signal_data_t *data)
        {
            auto& damage   = static_cast<wf::stream_signal_t*>(data)->raw_damage;
            const auto& ws = static_cast<wf::stream_signal_t*>(data)->ws;
            const auto& target_fb = static_cast<wf::stream_signal_t*>(data)->fb;

            /* As long as the padding is big enough to cover the
             * furthest sampled pixel by the shader, there should
             * be no visual artifacts. */
            int padding = std::ceil(
                blur_algorithm->calculate_blur_radius() / target_fb.scale);

            wf::region_t expanded_damage;
            for (const auto& rect : damage)
            {
                expanded_damage |= {
                    rect.x1 - padding,
                    rect.y1 - padding,
                    (rect.x2 - rect.x1) + 2 * padding,
                    (rect.y2 - rect.y1) + 2 * padding
                };
            }

            /* Keep rects on screen */
            expanded_damage &= output->render->get_ws_box(ws);

            /* Compute padded region and store result in padded_region.
             * We need to be careful, because core needs to scale the damage
             * back and forth for wlroots. */
            padded_region = get_fb_region(expanded_damage, target_fb) ^
                get_fb_region(damage, target_fb);

            OpenGL::render_begin(target_fb);
            /* Initialize a place to store padded region pixels. */
            saved_pixels.allocate(target_fb.viewport_width,
                target_fb.viewport_height);

            /* Setup framebuffer I/O. target_fb contains the pixels
             * from last frame at this point. We are writing them
             * to saved_pixels, bound as GL_DRAW_FRAMEBUFFER */
            saved_pixels.bind();
            GL_CALL(glBindFramebuffer(GL_READ_FRAMEBUFFER, target_fb.fb));

            /* Copy pixels in padded_region from target_fb to saved_pixels. */
            for (const auto& box : padded_region)
            {
                GL_CALL(glBlitFramebuffer(
                    box.x1, target_fb.viewport_height - box.y2,
                    box.x2, target_fb.viewport_height - box.y1,
                    box.x1, box.y1, box.x2, box.y2,
                    GL_COLOR_BUFFER_BIT, GL_LINEAR));
            }

            /* This effectively makes damage the same as expanded_damage. */
            damage |= expanded_damage;
            GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
            OpenGL::render_end();
        };

        output->render->connect_signal("workspace-stream-pre",
            &workspace_stream_pre);

        /* workspace_stream_post is called after rendering each frame
         * when rendering a workspace. It gives us a chance to copy
         * the pixels back to the framebuffer that we saved in
         * workspace_stream_pre. */
        workspace_stream_post = [=] (wf::signal_data_t *data)
        {
            const auto& target_fb = static_cast<wf::stream_signal_t*>(data)->fb;
            OpenGL::render_begin(target_fb);
            /* Setup framebuffer I/O. target_fb contains the frame
             * rendered with expanded damage and artifacts on the edges.
             * saved_pixels has the the padded region of pixels to overwrite the
             * artifacts that blurring has left behind. */
            GL_CALL(glBindFramebuffer(GL_READ_FRAMEBUFFER, saved_pixels.fb));

            /* Copy pixels back from saved_pixels to target_fb. */
            for (const auto& box : padded_region)
            {
                GL_CALL(glBlitFramebuffer(box.x1, box.y1, box.x2, box.y2,
                    box.x1, target_fb.viewport_height - box.y2,
                    box.x2, target_fb.viewport_height - box.y1,
                    GL_COLOR_BUFFER_BIT, GL_LINEAR));
            }

            /* Reset stuff */
            padded_region.clear();
            GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
            OpenGL::render_end();
        };

        output->render->connect_signal("workspace-stream-post",
            &workspace_stream_post);
    }

    void fini() override
    {
        remove_transformers();

        output->rem_binding(&button_toggle);
        output->disconnect_signal("view-attached", &view_attached);
        output->disconnect_signal("view-mapped", &view_attached);
        output->disconnect_signal("view-detached", &view_detached);
        output->render->rem_effect(&frame_pre_paint);
        output->render->disconnect_signal("workspace-stream-pre",
            &workspace_stream_pre);
        output->render->disconnect_signal("workspace-stream-post",
            &workspace_stream_post);

        /* Call blur algorithm destructor */
        blur_algorithm = nullptr;

        OpenGL::render_begin();
        saved_pixels.release();
        OpenGL::render_end();
    }
};

DECLARE_WAYFIRE_PLUGIN(wayfire_blur);
<commit_msg>blur: calculate blur region<commit_after>#include <wayfire/plugin.hpp>
#include <wayfire/view.hpp>
#include <wayfire/matcher.hpp>
#include <wayfire/output.hpp>
#include <wayfire/view-transform.hpp>
#include <wayfire/workspace-stream.hpp>
#include <wayfire/workspace-manager.hpp>
#include <wayfire/signal-definitions.hpp>

#include "blur.hpp"

using blur_algorithm_provider = std::function<nonstd::observer_ptr<wf_blur_base>()>;
class wf_blur_transformer : public wf::view_transformer_t
{
    blur_algorithm_provider provider;
    wf::output_t *output;
    wayfire_view view;

  public:
    wf_blur_transformer(blur_algorithm_provider blur_algorithm_provider,
        wf::output_t *output, wayfire_view view)
    {
        provider     = blur_algorithm_provider;
        this->output = output;
        this->view   = view;
    }

    wf::pointf_t transform_point(wf::geometry_t view,
        wf::pointf_t point) override
    {
        return point;
    }

    wf::pointf_t untransform_point(wf::geometry_t view,
        wf::pointf_t point) override
    {
        return point;
    }

    wlr_box get_bounding_box(wf::geometry_t view, wlr_box region) override
    {
        return region;
    }

    wf::region_t transform_opaque_region(
        wf::geometry_t bbox, wf::region_t region) override
    {
        return region;
    }

    uint32_t get_z_order() override
    {
        return wf::TRANSFORMER_BLUR;
    }

    /* Render without blending */
    void direct_render(wf::texture_t src_tex, wlr_box src_box,
        const wf::region_t& damage, const wf::framebuffer_t& target_fb)
    {
        OpenGL::render_begin(target_fb);
        for (auto& rect : damage)
        {
            target_fb.logic_scissor(wlr_box_from_pixman_box(rect));
            OpenGL::render_texture(src_tex, target_fb, src_box);
        }

        OpenGL::render_end();
    }

    void render_with_damage(wf::texture_t src_tex, wlr_box src_box,
        const wf::region_t& damage, const wf::framebuffer_t& target_fb) override
    {
        wf::region_t clip_damage = damage & src_box;

        /* We want to check if the opaque region completely occludes
         * the bounding box. If this is the case, we can skip blurring
         * altogether and just render the surface. First we disable
         * shrinking and get the opaque region without padding */
        wf::surface_interface_t::set_opaque_shrink_constraint("blur", 0);
        wf::region_t full_opaque = view->get_transformed_opaque_region();

        /* Shrink the opaque region by the padding amount since the render
         * chain expects this, as we have applied padding to damage in
         * frame_pre_paint for this frame already */
        int padding = std::ceil(provider()->calculate_blur_radius() /
            output->render->get_target_framebuffer().scale);
        wf::surface_interface_t::set_opaque_shrink_constraint("blur", padding);

        wf::region_t bbox_region{src_box};
        if ((bbox_region ^ full_opaque).empty())
        {
            /* In case the whole surface is opaque, we can simply skip blurring */
            direct_render(src_tex, src_box, damage, target_fb);

            return;
        }

        wf::region_t opaque_region  = view->get_transformed_opaque_region();
        wf::region_t blurred_region = clip_damage ^ opaque_region;

        provider()->pre_render(src_tex, src_box, blurred_region, target_fb);
        wf::view_transformer_t::render_with_damage(src_tex, src_box, blurred_region,
            target_fb);

        /* Opaque non-blurred regions can be rendered directly without blending */
        direct_render(src_tex, src_box, opaque_region & clip_damage, target_fb);
    }

    void render_box(wf::texture_t src_tex, wlr_box src_box, wlr_box scissor_box,
        const wf::framebuffer_t& target_fb) override
    {
        provider()->render(src_tex, src_box, scissor_box, target_fb);
    }
};

class wayfire_blur : public wf::plugin_interface_t
{
    wf::button_callback button_toggle;

    wf::effect_hook_t frame_pre_paint;
    wf::signal_callback_t workspace_stream_pre, workspace_stream_post,
        view_attached, view_detached;

    wf::view_matcher_t blur_by_default{"blur/blur_by_default"};
    wf::option_wrapper_t<std::string> method_opt{"blur/method"};
    wf::option_wrapper_t<wf::buttonbinding_t> toggle_button{"blur/toggle"};
    wf::config::option_base_t::updated_callback_t blur_method_changed;
    std::unique_ptr<wf_blur_base> blur_algorithm;

    const std::string transformer_name = "blur";

    /* the pixels from padded_region */
    wf::framebuffer_base_t saved_pixels;
    wf::region_t padded_region;

    void add_transformer(wayfire_view view)
    {
        if (view->get_transformer(transformer_name))
        {
            return;
        }

        view->add_transformer(std::make_unique<wf_blur_transformer>(
            [=] () {return nonstd::make_observer(blur_algorithm.get()); },
            output, view),
            transformer_name);
    }

    void pop_transformer(wayfire_view view)
    {
        if (view->get_transformer(transformer_name))
        {
            view->pop_transformer(transformer_name);
        }
    }

    void remove_transformers()
    {
        for (auto& view : output->workspace->get_views_in_layer(wf::ALL_LAYERS))
        {
            pop_transformer(view);
        }
    }

    /** Transform region into framebuffer coordinates */
    wf::region_t get_fb_region(const wf::region_t& region,
        const wf::framebuffer_t& fb) const
    {
        wf::region_t result;
        for (const auto& rect : region)
        {
            result |= fb.framebuffer_box_from_geometry_box(
                wlr_box_from_pixman_box(rect));
        }

        return result;
    }

    wf::region_t expand_region(const wf::region_t& region, double scale) const
    {
        // As long as the padding is big enough to cover the
        // furthest sampled pixel by the shader, there should
        // be no visual artifacts.
        int padding = std::ceil(
            blur_algorithm->calculate_blur_radius() / scale);

        wf::region_t padded;
        for (const auto& rect : region)
        {
            padded |= wlr_box{
                (rect.x1 - padding),
                (rect.y1 - padding),
                (rect.x2 - rect.x1) + 2 * padding,
                (rect.y2 - rect.y1) + 2 * padding
            };
        }

        return padded;
    }

    // Blur region for current frame
    wf::region_t blur_region;

    void update_blur_region()
    {
        blur_region.clear();
        auto views = output->workspace->get_views_in_layer(wf::ALL_LAYERS);

        for (auto& view : views)
        {
            if (!view->get_transformer("blur"))
            {
                continue;
            }

            auto bbox = view->get_bounding_box();
            if (!view->sticky)
            {
                blur_region |= bbox;
            } else
            {
                auto wsize = output->workspace->get_workspace_grid_size();
                for (int i = 0; i < wsize.width; i++)
                {
                    for (int j = 0; j < wsize.height; j++)
                    {
                        blur_region |=
                            bbox + wf::origin(output->render->get_ws_box({i, j}));
                    }
                }
            }
        }
    }

    /** Find the region of blurred views on the given workspace */
    wf::region_t get_blur_region(wf::point_t ws) const
    {
        return blur_region & output->render->get_ws_box(ws);
    }

  public:
    void init() override
    {
        grab_interface->name = "blur";
        grab_interface->capabilities = 0;

        blur_method_changed = [=] ()
        {
            blur_algorithm = create_blur_from_name(output, method_opt);
            output->render->damage_whole();
        };
        /* Create initial blur algorithm */
        blur_method_changed();
        method_opt.set_callback(blur_method_changed);

        /* Toggles the blur state of the view the user clicked on */
        button_toggle = [=] (uint32_t, int, int)
        {
            if (!output->can_activate_plugin(grab_interface))
            {
                return false;
            }

            auto view = wf::get_core().get_cursor_focus_view();
            if (!view)
            {
                return false;
            }

            if (view->get_transformer(transformer_name))
            {
                view->pop_transformer(transformer_name);
            } else
            {
                add_transformer(view);
            }

            return true;
        };
        output->add_button(toggle_button, &button_toggle);

        // Add blur transformers to views which have blur enabled
        view_attached = [=] (wf::signal_data_t *data)
        {
            auto view = get_signaled_view(data);
            /* View was just created -> we don't know its layer yet */
            if (!view->is_mapped())
            {
                return;
            }

            if (blur_by_default.matches(view))
            {
                add_transformer(view);
            }
        };

        /* If a view is detached, we remove its blur transformer.
         * If it is just moved to another output, the blur plugin
         * on the other output will add its own transformer there */
        view_detached = [=] (wf::signal_data_t *data)
        {
            auto view = get_signaled_view(data);
            pop_transformer(view);
        };
        output->connect_signal("view-attached", &view_attached);
        output->connect_signal("view-mapped", &view_attached);
        output->connect_signal("view-detached", &view_detached);

        /* frame_pre_paint is called before each frame has started.
         * It expands the damage by the blur radius.
         * This is needed, because when blurring, the pixels that changed
         * affect a larger area than the really damaged region, e.g the region
         * that comes from client damage */
        frame_pre_paint = [=] ()
        {
            update_blur_region();
            auto damage    = output->render->get_scheduled_damage();
            const auto& fb = output->render->get_target_framebuffer();

            int padding = std::ceil(
                blur_algorithm->calculate_blur_radius() / fb.scale);
            wf::surface_interface_t::set_opaque_shrink_constraint("blur",
                padding);

            output->render->damage(expand_region(
                damage & this->blur_region, fb.scale));
            return;
        };
        output->render->add_effect(&frame_pre_paint, wf::OUTPUT_EFFECT_DAMAGE);

        /* workspace_stream_pre is called before rendering each frame
         * when rendering a workspace. It gives us a chance to pad
         * damage and take a snapshot of the padded area. The padded
         * damage will be used to render the scene as normal. Then
         * workspace_stream_post is called so we can copy the padded
         * pixels back. */
        workspace_stream_pre = [=] (wf::signal_data_t *data)
        {
            auto& damage   = static_cast<wf::stream_signal_t*>(data)->raw_damage;
            const auto& ws = static_cast<wf::stream_signal_t*>(data)->ws;
            const auto& target_fb = static_cast<wf::stream_signal_t*>(data)->fb;

            wf::region_t expanded_damage = expand_region(damage, target_fb.scale);

            /* Keep rects on screen */
            expanded_damage &= expand_region(get_blur_region(ws), target_fb.scale);
            expanded_damage &= output->render->get_ws_box(ws);

            /* Compute padded region and store result in padded_region.
             * We need to be careful, because core needs to scale the damage
             * back and forth for wlroots. */
            padded_region = get_fb_region(expanded_damage, target_fb) ^
                get_fb_region(damage, target_fb);

            OpenGL::render_begin(target_fb);
            /* Initialize a place to store padded region pixels. */
            saved_pixels.allocate(target_fb.viewport_width,
                target_fb.viewport_height);

            /* Setup framebuffer I/O. target_fb contains the pixels
             * from last frame at this point. We are writing them
             * to saved_pixels, bound as GL_DRAW_FRAMEBUFFER */
            saved_pixels.bind();
            GL_CALL(glBindFramebuffer(GL_READ_FRAMEBUFFER, target_fb.fb));

            /* Copy pixels in padded_region from target_fb to saved_pixels. */
            for (const auto& box : padded_region)
            {
                GL_CALL(glBlitFramebuffer(
                    box.x1, target_fb.viewport_height - box.y2,
                    box.x2, target_fb.viewport_height - box.y1,
                    box.x1, box.y1, box.x2, box.y2,
                    GL_COLOR_BUFFER_BIT, GL_LINEAR));
            }

            /* This effectively makes damage the same as expanded_damage. */
            damage |= expanded_damage;
            GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
            OpenGL::render_end();
        };

        output->render->connect_signal("workspace-stream-pre",
            &workspace_stream_pre);

        /* workspace_stream_post is called after rendering each frame
         * when rendering a workspace. It gives us a chance to copy
         * the pixels back to the framebuffer that we saved in
         * workspace_stream_pre. */
        workspace_stream_post = [=] (wf::signal_data_t *data)
        {
            const auto& target_fb = static_cast<wf::stream_signal_t*>(data)->fb;
            OpenGL::render_begin(target_fb);
            /* Setup framebuffer I/O. target_fb contains the frame
             * rendered with expanded damage and artifacts on the edges.
             * saved_pixels has the the padded region of pixels to overwrite the
             * artifacts that blurring has left behind. */
            GL_CALL(glBindFramebuffer(GL_READ_FRAMEBUFFER, saved_pixels.fb));

            /* Copy pixels back from saved_pixels to target_fb. */
            for (const auto& box : padded_region)
            {
                GL_CALL(glBlitFramebuffer(box.x1, box.y1, box.x2, box.y2,
                    box.x1, target_fb.viewport_height - box.y2,
                    box.x2, target_fb.viewport_height - box.y1,
                    GL_COLOR_BUFFER_BIT, GL_LINEAR));
            }

            /* Reset stuff */
            padded_region.clear();
            GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
            OpenGL::render_end();
        };

        output->render->connect_signal("workspace-stream-post",
            &workspace_stream_post);
    }

    void fini() override
    {
        remove_transformers();

        output->rem_binding(&button_toggle);
        output->disconnect_signal("view-attached", &view_attached);
        output->disconnect_signal("view-mapped", &view_attached);
        output->disconnect_signal("view-detached", &view_detached);
        output->render->rem_effect(&frame_pre_paint);
        output->render->disconnect_signal("workspace-stream-pre",
            &workspace_stream_pre);
        output->render->disconnect_signal("workspace-stream-post",
            &workspace_stream_post);

        /* Call blur algorithm destructor */
        blur_algorithm = nullptr;

        OpenGL::render_begin();
        saved_pixels.release();
        OpenGL::render_end();
    }
};

DECLARE_WAYFIRE_PLUGIN(wayfire_blur);
<|endoftext|>