<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP
#define MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP

#include <mapnik/renderer_common.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/geometry_type.hpp>

namespace mapnik {

template <typename VD, typename RD, typename RendererType, typename ContextType>
struct render_marker_symbolizer_visitor
{
    using vector_dispatch_type = VD;
    using raster_dispatch_type = RD;
    using buffer_type = typename std::tuple_element<0,ContextType>::type;

    render_marker_symbolizer_visitor(std::string const& filename,
                                     markers_symbolizer const& sym,
                                     mapnik::feature_impl & feature,
                                     proj_transform const& prj_trans,
                                     RendererType const& common,
                                     box2d<double> const& clip_box,
                                     ContextType const& renderer_context)
        : filename_(filename),
          sym_(sym),
          feature_(feature),
          prj_trans_(prj_trans),
          common_(common),
          clip_box_(clip_box),
          renderer_context_(renderer_context) {}

    void operator() (marker_null const&) {}

    void operator() (marker_svg const& mark)
    {
        using namespace mapnik::svg;
        bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
        double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
        double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
        double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);

        // https://github.com/mapnik/mapnik/issues/1316
        bool snap_to_pixels = !mapnik::marker_cache::instance().is_uri(filename_);

        agg::trans_affine geom_tr;
        auto transform = get_optional<transform_type>(sym_, keys::geometry_transform);
        if (transform) evaluate_transform(geom_tr, feature_, common_.vars_, *transform, common_.scale_factor_);
        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        boost::optional<svg_path_ptr> const& stock_vector_marker = mark.get_data();

        // special case for simple ellipse markers
        // to allow for full control over rx/ry dimensions
        if (filename_ == "shape://ellipse"
           && (has_key(sym_,keys::width) || has_key(sym_,keys::height)))
        {
            svg_path_ptr marker_ellipse = std::make_shared<svg_storage_type>();
            vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse->source());
            svg_path_adapter svg_path(stl_storage);
            build_ellipse(sym_, feature_, common_.vars_, *marker_ellipse, svg_path);
            svg_attribute_type attributes;
            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym_, feature_, common_.vars_);
            auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
            if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
            vector_dispatch_type rasterizer_dispatch(marker_ellipse,
                                                     svg_path,
                                                     result ? attributes : (*stock_vector_marker)->attributes(),
                                                     image_tr,
                                                     sym_,
                                                     *common_.detector_,
                                                     common_.scale_factor_,
                                                     feature_,
                                                     common_.vars_,
                                                     snap_to_pixels,
                                                     renderer_context_);

            using vertex_converter_type = vertex_converter<vector_dispatch_type,clip_line_tag,
                                          clip_poly_tag,
                                          transform_tag,
                                          affine_transform_tag,
                                          simplify_tag, smooth_tag,
                                          offset_transform_tag>;
            vertex_converter_type converter(clip_box_,
                                            rasterizer_dispatch,
                                            sym_,
                                            common_.t_,
                                            prj_trans_,
                                            geom_tr,
                                            feature_,
                                            common_.vars_,
                                            common_.scale_factor_);
            if (clip) // optional clip (default: true)
            {
                geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
                if (type == geometry::geometry_types::Polygon)
                    converter.template set<clip_poly_tag>();
                else if (type == geometry::geometry_types::LineString)
                    converter.template set<clip_line_tag>();
            }

            converter.template set<transform_tag>(); //always transform
            if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
            converter.template set<affine_transform_tag>(); // optional affine transform
            if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
            if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
            apply_markers_multi(feature_, common_.vars_, converter, sym_);
        }
        else
        {
            box2d<double> const& bbox = mark.bounding_box();
            setup_transform_scaling(image_tr, bbox.width(), bbox.height(), feature_, common_.vars_, sym_);
            auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
            if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
            vertex_stl_adapter<svg_path_storage> stl_storage((*stock_vector_marker)->source());
            svg_path_adapter svg_path(stl_storage);
            svg_attribute_type attributes;
            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym_, feature_, common_.vars_);
            vector_dispatch_type rasterizer_dispatch(*stock_vector_marker,
                                                     svg_path,
                                                     result ? attributes : (*stock_vector_marker)->attributes(),
                                                     image_tr,
                                                     sym_,
                                                     *common_.detector_,
                                                     common_.scale_factor_,
                                                     feature_,
                                                     common_.vars_,
                                                     snap_to_pixels,
                                                     renderer_context_);

            using vertex_converter_type = vertex_converter<vector_dispatch_type,clip_line_tag,
                                          clip_poly_tag,
                                          transform_tag,
                                          affine_transform_tag,
                                          simplify_tag, smooth_tag,
                                          offset_transform_tag>;
            vertex_converter_type converter(clip_box_,
                                            rasterizer_dispatch,
                                            sym_,
                                            common_.t_,
                                            prj_trans_,
                                            geom_tr,
                                            feature_,
                                            common_.vars_,
                                            common_.scale_factor_);
            if (clip) // optional clip (default: true)
            {
                geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
                if (type == geometry::geometry_types::Polygon)
                    converter.template set<clip_poly_tag>();
                else if (type == geometry::geometry_types::LineString)
                    converter.template set<clip_line_tag>();
            }

            converter.template set<transform_tag>(); //always transform
            if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
            converter.template set<affine_transform_tag>(); // optional affine transform
            if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
            if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
            apply_markers_multi(feature_, common_.vars_, converter, sym_);
        }
    }

    void operator() (marker_rgba8 const& mark)
    {
        using namespace mapnik::svg;
        bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
        double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
        double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
        double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);

        agg::trans_affine geom_tr;
        auto transform = get_optional<transform_type>(sym_, keys::geometry_transform);
        if (transform) evaluate_transform(geom_tr, feature_, common_.vars_, *transform, common_.scale_factor_);
        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
        auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
        box2d<double> const& bbox = mark.bounding_box();
        mapnik::image_rgba8 const& marker = mark.get_data();
        // - clamp sizes to > 4 pixels of interactivity
        coord2d center = bbox.center();
        agg::trans_affine_translation recenter(-center.x, -center.y);
        agg::trans_affine marker_trans = recenter * image_tr;
        raster_dispatch_type rasterizer_dispatch(marker,
                                                 marker_trans,
                                                 sym_,
                                                 *common_.detector_,
                                                 common_.scale_factor_,
                                                 feature_,
                                                 common_.vars_,
                                                 renderer_context_);

        using vertex_converter_type = vertex_converter<raster_dispatch_type,clip_line_tag,
                                      clip_poly_tag,
                                      transform_tag,
                                      affine_transform_tag,
                                      simplify_tag, smooth_tag,
                                      offset_transform_tag>;
        vertex_converter_type converter(clip_box_,
                                        rasterizer_dispatch,
                                        sym_,
                                        common_.t_,
                                        prj_trans_,
                                        geom_tr,
                                        feature_,
                                        common_.vars_,
                                        common_.scale_factor_);

        if (clip) // optional clip (default: true)
        {
            geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
            if (type == geometry::geometry_types::Polygon)
                converter.template set<clip_poly_tag>();
            else if (type == geometry::geometry_types::LineString)
                converter.template set<clip_line_tag>();
        }
        converter.template set<transform_tag>(); //always transform
        if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
        converter.template set<affine_transform_tag>(); // optional affine transform
        if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
        if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
        apply_markers_multi(feature_, common_.vars_, converter, sym_);
    }

  private:
    std::string const& filename_;
    markers_symbolizer const& sym_;
    mapnik::feature_impl & feature_;
    proj_transform const& prj_trans_;
    RendererType const& common_;
    box2d<double> const& clip_box_;
    ContextType const& renderer_context_;
};

template <typename VD, typename RD, typename RendererType, typename ContextType>
void render_markers_symbolizer(markers_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans,
                               RendererType const& common,
                               box2d<double> const& clip_box,
                               ContextType const& renderer_context)
{
    using namespace mapnik::svg;
    std::string filename = get<std::string>(sym, keys::file, feature, common.vars_, "shape://ellipse");
    if (!filename.empty())
    {
        mapnik::marker const& mark = mapnik::marker_cache::instance().find(filename, true);
        render_marker_symbolizer_visitor<VD,RD,RendererType,ContextType> visitor(filename,
                                         sym,
                                         feature,
                                         prj_trans,
                                         common,
                                         clip_box,
                                         renderer_context);
        util::apply_visitor(visitor, mark);
    }
}

} // namespace mapnik

#endif // MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP
<commit_msg>visual tests : fix marker-on-hex-grid<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP
#define MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP

#include <mapnik/renderer_common.hpp>
#include <mapnik/svg/svg_storage.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/marker_helpers.hpp>
#include <mapnik/geometry_type.hpp>

namespace mapnik {

template <typename VD, typename RD, typename RendererType, typename ContextType>
struct render_marker_symbolizer_visitor
{
    using vector_dispatch_type = VD;
    using raster_dispatch_type = RD;
    using buffer_type = typename std::tuple_element<0,ContextType>::type;

    render_marker_symbolizer_visitor(std::string const& filename,
                                     markers_symbolizer const& sym,
                                     mapnik::feature_impl & feature,
                                     proj_transform const& prj_trans,
                                     RendererType const& common,
                                     box2d<double> const& clip_box,
                                     ContextType const& renderer_context)
        : filename_(filename),
          sym_(sym),
          feature_(feature),
          prj_trans_(prj_trans),
          common_(common),
          clip_box_(clip_box),
          renderer_context_(renderer_context) {}

    void operator() (marker_null const&) {}

    void operator() (marker_svg const& mark)
    {
        using namespace mapnik::svg;
        bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
        double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
        double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
        double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);

        // https://github.com/mapnik/mapnik/issues/1316
        bool snap_to_pixels = !mapnik::marker_cache::instance().is_uri(filename_);

        agg::trans_affine geom_tr;
        auto transform = get_optional<transform_type>(sym_, keys::geometry_transform);
        if (transform) evaluate_transform(geom_tr, feature_, common_.vars_, *transform, common_.scale_factor_);
        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        boost::optional<svg_path_ptr> const& stock_vector_marker = mark.get_data();

        // special case for simple ellipse markers
        // to allow for full control over rx/ry dimensions
        if (filename_ == "shape://ellipse"
           && (has_key(sym_,keys::width) || has_key(sym_,keys::height)))
        {
            svg_path_ptr marker_ellipse = std::make_shared<svg_storage_type>();
            vertex_stl_adapter<svg_path_storage> stl_storage(marker_ellipse->source());
            svg_path_adapter svg_path(stl_storage);
            build_ellipse(sym_, feature_, common_.vars_, *marker_ellipse, svg_path);
            svg_attribute_type attributes;
            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym_, feature_, common_.vars_);
            auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
            if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
            vector_dispatch_type rasterizer_dispatch(marker_ellipse,
                                                     svg_path,
                                                     result ? attributes : (*stock_vector_marker)->attributes(),
                                                     image_tr,
                                                     sym_,
                                                     *common_.detector_,
                                                     common_.scale_factor_,
                                                     feature_,
                                                     common_.vars_,
                                                     snap_to_pixels,
                                                     renderer_context_);

            using vertex_converter_type = vertex_converter<vector_dispatch_type,clip_line_tag,
                                          clip_poly_tag,
                                          transform_tag,
                                          affine_transform_tag,
                                          simplify_tag, smooth_tag,
                                          offset_transform_tag>;
            vertex_converter_type converter(clip_box_,
                                            rasterizer_dispatch,
                                            sym_,
                                            common_.t_,
                                            prj_trans_,
                                            geom_tr,
                                            feature_,
                                            common_.vars_,
                                            common_.scale_factor_);
            if (clip) // optional clip (default: true)
            {
                geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
                if (type == geometry::geometry_types::Polygon)
                    converter.template set<clip_poly_tag>();
                else if (type == geometry::geometry_types::LineString)
                    converter.template set<clip_line_tag>();
            }

            converter.template set<transform_tag>(); //always transform
            if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
            converter.template set<affine_transform_tag>(); // optional affine transform
            if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
            if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
            apply_markers_multi(feature_, common_.vars_, converter, sym_);
        }
        else
        {
            box2d<double> const& bbox = mark.bounding_box();
            setup_transform_scaling(image_tr, bbox.width(), bbox.height(), feature_, common_.vars_, sym_);
            auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
            if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
            vertex_stl_adapter<svg_path_storage> stl_storage((*stock_vector_marker)->source());
            svg_path_adapter svg_path(stl_storage);
            svg_attribute_type attributes;
            bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym_, feature_, common_.vars_);
            vector_dispatch_type rasterizer_dispatch(*stock_vector_marker,
                                                     svg_path,
                                                     result ? attributes : (*stock_vector_marker)->attributes(),
                                                     image_tr,
                                                     sym_,
                                                     *common_.detector_,
                                                     common_.scale_factor_,
                                                     feature_,
                                                     common_.vars_,
                                                     snap_to_pixels,
                                                     renderer_context_);

            using vertex_converter_type = vertex_converter<vector_dispatch_type,clip_line_tag,
                                          clip_poly_tag,
                                          transform_tag,
                                          affine_transform_tag,
                                          simplify_tag, smooth_tag,
                                          offset_transform_tag>;
            vertex_converter_type converter(clip_box_,
                                            rasterizer_dispatch,
                                            sym_,
                                            common_.t_,
                                            prj_trans_,
                                            geom_tr,
                                            feature_,
                                            common_.vars_,
                                            common_.scale_factor_);
            if (clip) // optional clip (default: true)
            {
                geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
                if (type == geometry::geometry_types::Polygon || type == geometry::geometry_types::MultiPolygon)
                    converter.template set<clip_poly_tag>();
                else if (type == geometry::geometry_types::LineString || type == geometry::geometry_types::MultiLineString)
                    converter.template set<clip_line_tag>();
            }

            converter.template set<transform_tag>(); //always transform
            if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
            converter.template set<affine_transform_tag>(); // optional affine transform
            if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
            if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
            apply_markers_multi(feature_, common_.vars_, converter, sym_);
        }
    }

    void operator() (marker_rgba8 const& mark)
    {
        using namespace mapnik::svg;
        bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
        double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
        double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
        double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);

        agg::trans_affine geom_tr;
        auto transform = get_optional<transform_type>(sym_, keys::geometry_transform);
        if (transform) evaluate_transform(geom_tr, feature_, common_.vars_, *transform, common_.scale_factor_);
        agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);

        setup_transform_scaling(image_tr, mark.width(), mark.height(), feature_, common_.vars_, sym_);
        auto image_transform = get_optional<transform_type>(sym_, keys::image_transform);
        if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform);
        box2d<double> const& bbox = mark.bounding_box();
        mapnik::image_rgba8 const& marker = mark.get_data();
        // - clamp sizes to > 4 pixels of interactivity
        coord2d center = bbox.center();
        agg::trans_affine_translation recenter(-center.x, -center.y);
        agg::trans_affine marker_trans = recenter * image_tr;
        raster_dispatch_type rasterizer_dispatch(marker,
                                                 marker_trans,
                                                 sym_,
                                                 *common_.detector_,
                                                 common_.scale_factor_,
                                                 feature_,
                                                 common_.vars_,
                                                 renderer_context_);

        using vertex_converter_type = vertex_converter<raster_dispatch_type,clip_line_tag,
                                      clip_poly_tag,
                                      transform_tag,
                                      affine_transform_tag,
                                      simplify_tag, smooth_tag,
                                      offset_transform_tag>;
        vertex_converter_type converter(clip_box_,
                                        rasterizer_dispatch,
                                        sym_,
                                        common_.t_,
                                        prj_trans_,
                                        geom_tr,
                                        feature_,
                                        common_.vars_,
                                        common_.scale_factor_);

        if (clip) // optional clip (default: true)
        {
            geometry::geometry_types type = geometry::geometry_type(feature_.get_geometry());
            if (type == geometry::geometry_types::Polygon)
                converter.template set<clip_poly_tag>();
            else if (type == geometry::geometry_types::LineString)
                converter.template set<clip_line_tag>();
        }
        converter.template set<transform_tag>(); //always transform
        if (std::fabs(offset) > 0.0) converter.template set<offset_transform_tag>(); // parallel offset
        converter.template set<affine_transform_tag>(); // optional affine transform
        if (simplify_tolerance > 0.0) converter.template set<simplify_tag>(); // optional simplify converter
        if (smooth > 0.0) converter.template set<smooth_tag>(); // optional smooth converter
        apply_markers_multi(feature_, common_.vars_, converter, sym_);
    }

  private:
    std::string const& filename_;
    markers_symbolizer const& sym_;
    mapnik::feature_impl & feature_;
    proj_transform const& prj_trans_;
    RendererType const& common_;
    box2d<double> const& clip_box_;
    ContextType const& renderer_context_;
};

template <typename VD, typename RD, typename RendererType, typename ContextType>
void render_markers_symbolizer(markers_symbolizer const& sym,
                               mapnik::feature_impl & feature,
                               proj_transform const& prj_trans,
                               RendererType const& common,
                               box2d<double> const& clip_box,
                               ContextType const& renderer_context)
{
    using namespace mapnik::svg;
    std::string filename = get<std::string>(sym, keys::file, feature, common.vars_, "shape://ellipse");
    if (!filename.empty())
    {
        mapnik::marker const& mark = mapnik::marker_cache::instance().find(filename, true);
        render_marker_symbolizer_visitor<VD,RD,RendererType,ContextType> visitor(filename,
                                         sym,
                                         feature,
                                         prj_trans,
                                         common,
                                         clip_box,
                                         renderer_context);
        util::apply_visitor(visitor, mark);
    }
}

} // namespace mapnik

#endif // MAPNIK_RENDERER_COMMON_PROCESS_MARKERS_SYMBOLIZER_HPP
<|endoftext|>


<commit_before>/*
 * Copyright 2008 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "condor_common.h"

#include "ClassAdReevaluator.h"

#include "condor_classad.h"
#include "string_list.h"
#include "MyString.h"
#include "condor_debug.h"

bool
classad_reevaluate(ClassAd *ad, const ClassAd *context)
{
	StringList *reevaluate_attrs;
	MyString stmp;
	char *ptmp, *atmp, *ntmp = NULL;
	ExprTree *etmp;
	int itmp;
	float ftmp;

	if (!ad->LookupString("REEVALUATE_ATTRIBUTES", &ptmp)) {
		dprintf(D_FULLDEBUG,
				"classad_reevaluate: REEVALUATE_ATTRIBUTES not defined, skipping\n");

		return true;
	}

	reevaluate_attrs = new StringList(ptmp);
	if (!reevaluate_attrs) {
		dprintf(D_ALWAYS,
				"classad_reevaluate: Failed to parse REEVALUATE_ATTRS: %s\n",
				ptmp);

		goto FAIL;
	}

	free(ptmp);
	ptmp = NULL;

	reevaluate_attrs->rewind();
	while (NULL != (atmp = reevaluate_attrs->next())) {
		stmp.sprintf("REEVALUATE_%s_EXPR", atmp);

		dprintf(D_FULLDEBUG,
				"classad_reevaluate: Attempting reevaluate %s with %s\n",
				atmp, stmp.Value());

		etmp = ad->LookupExpr(atmp);
		if (!etmp) {
			dprintf(D_ALWAYS,
					"classad_reevaluate: %s does not exist in ad, returning\n",
					atmp);

			goto FAIL;
		}

		switch (etmp->MyType()) {
		case LX_STRING:
			if (!ad->EvalString(stmp.Value(), context, &ntmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as a String\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, ntmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %s to %s\n",
						ntmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %s\n",
					atmp, ntmp);

			free(ntmp);
			ntmp = NULL;

			break;
		case LX_INTEGER:
			if (!ad->EvalInteger(stmp.Value(), context, itmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as an Integer\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, itmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %d to %s\n",
						itmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %d\n",
					atmp, itmp);

			break;
		case LX_FLOAT:
			if (!ad->EvalFloat(stmp.Value(), context, ftmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as a Float\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, ftmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %f to %s\n",
						ftmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %f\n",
					atmp, ftmp);

			break;
		case LX_BOOL:
			if (!ad->EvalBool(stmp.Value(), context, itmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as a Bool\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, (itmp ? true : false))) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %d to %s\n",
						itmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %d\n",
					atmp, itmp);

			break;
		default:
			dprintf(D_ALWAYS,
					"classad_reevaluate: %s has unsupported type %d\n, cannot reevaluate\n",
					atmp, etmp->MyType());
		}
	}

	delete reevaluate_attrs;

	return true;

 FAIL:

	if (reevaluate_attrs) delete reevaluate_attrs;
	if (ntmp) free(ntmp);

	return false;
}
<commit_msg>Remove usage of ExprTree::MyType() in classad_reevaluate(). #619<commit_after>/*
 * Copyright 2008 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "condor_common.h"

#include "ClassAdReevaluator.h"

#include "condor_classad.h"
#include "string_list.h"
#include "MyString.h"
#include "condor_debug.h"

bool
classad_reevaluate(ClassAd *ad, const ClassAd *context)
{
	StringList *reevaluate_attrs;
	MyString stmp;
	char *ptmp, *atmp, *ntmp = NULL;
	ExprTree *etmp;
	int itmp;
	float ftmp;

	if (!ad->LookupString("REEVALUATE_ATTRIBUTES", &ptmp)) {
		dprintf(D_FULLDEBUG,
				"classad_reevaluate: REEVALUATE_ATTRIBUTES not defined, skipping\n");

		return true;
	}

	reevaluate_attrs = new StringList(ptmp);
	if (!reevaluate_attrs) {
		dprintf(D_ALWAYS,
				"classad_reevaluate: Failed to parse REEVALUATE_ATTRS: %s\n",
				ptmp);

		goto FAIL;
	}

	free(ptmp);
	ptmp = NULL;

	reevaluate_attrs->rewind();
	while (NULL != (atmp = reevaluate_attrs->next())) {
		stmp.sprintf("REEVALUATE_%s_EXPR", atmp);

		dprintf(D_FULLDEBUG,
				"classad_reevaluate: Attempting reevaluate %s with %s\n",
				atmp, stmp.Value());

		etmp = ad->LookupExpr(atmp);
		if (!etmp) {
			dprintf(D_ALWAYS,
					"classad_reevaluate: %s does not exist in ad, returning\n",
					atmp);

			goto FAIL;
		}

		if ( ad->LookupString(atmp, &ntmp) ) {
			free(ntmp);
			ntmp = NULL;

			if (!ad->EvalString(stmp.Value(), context, &ntmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as a String\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, ntmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %s to %s\n",
						ntmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %s\n",
					atmp, ntmp);

			free(ntmp);
			ntmp = NULL;

		} else if ( ad->LookupInteger(atmp, itmp) ) {

			if (!ad->EvalInteger(stmp.Value(), context, itmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as an Integer\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, itmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %d to %s\n",
						itmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %d\n",
					atmp, itmp);

		} else if ( ad->LookupFloat(atmp, ftmp) ) {

			if (!ad->EvalFloat(stmp.Value(), context, ftmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as a Float\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, ftmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %f to %s\n",
						ftmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %f\n",
					atmp, ftmp);

		} else if ( ad->LookupBool(atmp, itmp) ) {

			if (!ad->EvalBool(stmp.Value(), context, itmp)) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to evaluate %s as a Bool\n",
						stmp.Value());

				goto FAIL;
			}

			if (!ad->Assign(atmp, (itmp ? true : false))) {
				dprintf(D_ALWAYS,
						"classad_reevaluate: Failed to assign new value %d to %s\n",
						itmp, atmp);

				goto FAIL;
			}

			dprintf(D_FULLDEBUG,
					"classad_reevaluate: Updated %s to %d\n",
					atmp, itmp);

		} else {
			dprintf(D_ALWAYS,
					"classad_reevaluate: %s has an unsupported type\n, cannot reevaluate\n",
					atmp);
		}
	}

	delete reevaluate_attrs;

	return true;

 FAIL:

	if (reevaluate_attrs) delete reevaluate_attrs;
	if (ntmp) free(ntmp);

	return false;
}
<|endoftext|>


<commit_before>#include "Config.hpp"
#include "EventOutputQueue.hpp"
#include "ListHookedConsumer.hpp"
#include "ListHookedKeyboard.hpp"
#include "ListHookedPointing.hpp"
#include "PressDownKeys.hpp"
#include "RemapClass.hpp"
#include "VirtualKey.hpp"
#include "VK_IOHIDPOSTEVENT.hpp"

namespace org_pqrs_Karabiner {
  List* EventOutputQueue::queue_ = NULL;
  TimerWrapper EventOutputQueue::fire_timer_;
  Buttons EventOutputQueue::previousButtons_;

  void
  EventOutputQueue::initialize(IOWorkLoop& workloop)
  {
    queue_ = new List();
    fire_timer_.initialize(&workloop, NULL, EventOutputQueue::fire_timer_callback);
  }

  void
  EventOutputQueue::terminate(void)
  {
    fire_timer_.terminate();

    if (queue_) {
      delete queue_;
    }
  }

  // ----------------------------------------------------------------------
#define PUSH_TO_OUTPUTQUEUE {           \
    if (! queue_) return;               \
                                        \
    queue_->push_back(new Item(p));     \
    fire_timer_.setTimeoutMS(0, false); \
}
  void EventOutputQueue::push(const Params_KeyboardEventCallBack& p) {
    PUSH_TO_OUTPUTQUEUE;
    if (p.eventType == EventType::DOWN) {
      FlagStatus::globalFlagStatus().sticky_clear();
    }
  }
  void EventOutputQueue::push(const Params_KeyboardSpecialEventCallback& p) {
    PUSH_TO_OUTPUTQUEUE;
    if (p.ex_iskeydown) {
      FlagStatus::globalFlagStatus().sticky_clear();
    }
  }
  void EventOutputQueue::push(const Params_RelativePointerEventCallback& p) {
    PUSH_TO_OUTPUTQUEUE;
    if (p.buttons != Buttons(0)) {
      FlagStatus::globalFlagStatus().sticky_clear();
    }
  }
  void EventOutputQueue::push(const Params_ScrollWheelEventCallback& p) {
    PUSH_TO_OUTPUTQUEUE;
    FlagStatus::globalFlagStatus().sticky_clear();
  }
  void EventOutputQueue::push(const Params_Wait& p) {
    PUSH_TO_OUTPUTQUEUE;
  }
#undef PUSH_TO_OUTPUTQUEUE

  // ----------------------------------------------------------------------
  unsigned int
  EventOutputQueue::calcDelay(const ParamsUnion& paramsUnion)
  {
    // ----------------------------------------
    {
      // Delay before and after modifier
      //
      // We need to wait before and after a modifier event because
      // events will be dropped by window server if
      // we send a mouse click event and a modifier event at the same time.
      //
      // For example, change middle click to command-left click by this autogen.
      // We need to put a wait on this setting in Mail.app.
      //    <autogen>
      //      __KeyToKey__
      //      PointingButton::MIDDLE, ModifierFlag::NONE,
      //      PointingButton::LEFT, ModifierFlag::COMMAND_L,
      //    </autogen>
      //
      if (paramsUnion.isModifier()) {
        return Config::get_wait_before_and_after_a_modifier_key_event();
      }
    }

    // ----------------------------------------
    {
      // Delay before and after modifier
      //
      // We need to wait before and after a pointing device click event because
      // events will be dropped by processes (eg. NetBeans) if
      // we send press button event and release button event at the same time.
      //

      Params_RelativePointerEventCallback* params = paramsUnion.get_Params_RelativePointerEventCallback();
      if (params) {
        if (params->buttons != previousButtons_) {
          previousButtons_ = params->buttons;
          return Config::get_wait_before_and_after_a_click_event();
        }
      }
    }

    // ----------------------------------------
    return 0;
  }

  namespace {
    unsigned int maxDelay(unsigned int v1, unsigned int v2) {
      if (v1 > v2) {
        return v1;
      } else {
        return v2;
      }
    }
  }

  void
  EventOutputQueue::fire_timer_callback(OSObject* /* owner */, IOTimerEventSource* /* sender */)
  {
    if (! queue_) return;

    // IOLOG_DEVEL("EventOutputQueue::fire queue_->size = %d\n", static_cast<int>(queue_->size()));

    Item* p = static_cast<Item*>(queue_->front());
    if (! p) return;

    // Delay after modifier or click.
    unsigned int delay = calcDelay(p->params);

    // ----------------------------------------
    switch ((p->params).type) {
      case ParamsUnion::KEYBOARD:
      {
        Params_KeyboardEventCallBack* params = (p->params).get_Params_KeyboardEventCallBack();
        if (params) {
          bool handled = VirtualKey::handleAfterEnqueued(*params);
          if (! handled) {
            ListHookedKeyboard::instance().apply(*params);
          }
        }

        // We need to wait at least 3ms in order to avoid changing key sequence order randomly.
        // (If VMware Fusion's driver is installed, the wrong order issue will be happen.)
        delay = maxDelay(delay, 3);
        break;
      }
      case ParamsUnion::KEYBOARD_SPECIAL:
      {
        Params_KeyboardSpecialEventCallback* params = (p->params).get_Params_KeyboardSpecialEventCallback();
        if (params) {
          if (! ListHookedConsumer::instance().apply(*params)) {
            // If there is no consumer device, we send an event as a software key.
            VirtualKey::VK_IOHIDPOSTEVENT::post(*params);
          }
        }

        // We need to wait at least 3ms in order to avoid changing key sequence order randomly.
        // (If VMware Fusion's driver is installed, the wrong order issue will be happen.)
        delay = maxDelay(delay, 3);
        break;
      }
      case ParamsUnion::RELATIVE_POINTER:
      {
        Params_RelativePointerEventCallback* params = (p->params).get_Params_RelativePointerEventCallback();
        if (params) {
          ListHookedPointing::instance().apply(*params);
        }
        break;
      }
      case ParamsUnion::SCROLL_WHEEL:
      {
        Params_ScrollWheelEventCallback* params = (p->params).get_Params_ScrollWheelEventCallback();
        if (params) {
          ListHookedPointing::instance().apply(*params);
        }
        break;
      }
      case ParamsUnion::WAIT:
      {
        Params_Wait* params = (p->params).get_Params_Wait();
        if (params) {
          delay = maxDelay(delay, static_cast<unsigned int>(params->milliseconds));
        }
        break;
      }
      case ParamsUnion::UPDATE_FLAGS:
        // do nothing
        break;
    }

    queue_->pop_front();

    // ----------------------------------------
    // Set timeout for next event.

    // Delay before modifier and click.
    Item* next = static_cast<Item*>(queue_->front());
    if (! next) return;
    delay = maxDelay(delay, calcDelay(next->params));

    fire_timer_.setTimeoutMS(delay);
  }

  // ======================================================================
  Flags EventOutputQueue::FireModifiers::lastFlags_(0);

  void
  EventOutputQueue::FireModifiers::fire(Flags toFlags, KeyboardType keyboardType)
  {
    if (lastFlags_ == toFlags) return;

    // ------------------------------------------------------------
    // At first we handle KeyUp events and handle KeyDown events next.
    // We need to end KeyDown at Command+Space to Option_L+Shift_L.
    //
    // When Option_L+Shift_L has a meaning (switch input language at Windows),
    // it does not works well when the last is KeyUp of Command.

    // ------------------------------------------------------------
    // KeyUp
    for (size_t i = 0; i < FlagStatus::globalFlagStatus().itemSize(); ++i) {
      ModifierFlag flag = FlagStatus::globalFlagStatus().getFlag(i);

      // Skipping ModifierFlag::NUMPAD
      if (flag.getKeyCode() == KeyCode::VK_NONE) continue;
      // Skipping virtual modifiers.
      if (flag.getRawBits() == 0) continue;

      // ----------------------------------------
      if (! lastFlags_.isOn(flag)) continue;
      if (toFlags.isOn(flag)) continue;

      lastFlags_.remove(flag);

      Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(EventType::MODIFY, lastFlags_, flag.getKeyCode(), keyboardType, false));
      if (! ptr) continue;
      EventOutputQueue::push(*ptr);
    }

    // KeyDown
    for (size_t i = 0; i < FlagStatus::globalFlagStatus().itemSize(); ++i) {
      ModifierFlag flag = FlagStatus::globalFlagStatus().getFlag(i);

      // Skipping ModifierFlag::NUMPAD
      if (flag.getKeyCode() == KeyCode::VK_NONE) continue;
      // Skipping virtual modifiers.
      if (flag.getRawBits() == 0) continue;

      // ----------------------------------------
      if (! toFlags.isOn(flag)) continue;
      if (lastFlags_.isOn(flag)) continue;

      lastFlags_.add(flag);

      Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(EventType::MODIFY, lastFlags_, flag.getKeyCode(), keyboardType, false));
      if (! ptr) continue;
      EventOutputQueue::push(*ptr);
    }

    lastFlags_ = toFlags;
  }

  // ======================================================================
  void
  EventOutputQueue::FireKey::fire(const Params_KeyboardEventCallBack& params)
  {
    if (VirtualKey::handle(params)) return;

    // ------------------------------------------------------------
    KeyCode newkeycode = params.key;
    Flags newflags = params.flags;

    KeyCode::reverseNormalizeKey(newkeycode, newflags, params.eventType, params.keyboardType);

    // skip no-outputable keycodes.
    // Note: check before FireModifiers to avoid meaningless modifier event.
    if (newkeycode == KeyCode::VK_NONE ||
        newkeycode == KeyCode::VK_PSEUDO_KEY) {
      return;
    }
    if (newkeycode.isModifier() && newkeycode.getModifierFlag().getRawBits() == 0) {
      // virtual modifiers.
      return;
    }

    FireModifiers::fire(newflags, params.keyboardType);

    if (params.eventType == EventType::DOWN || params.eventType == EventType::UP) {
      Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(params.eventType, newflags, newkeycode,
                                                                                params.charCode, params.charSet, params.origCharCode, params.origCharSet,
                                                                                params.keyboardType, params.repeat));
      if (ptr) {
        EventOutputQueue::push(*ptr);
      }

      if (! params.repeat) {
        if (params.eventType == EventType::DOWN) {
          PressDownKeys::add(newkeycode, params.keyboardType);
        } else {
          PressDownKeys::remove(newkeycode, params.keyboardType);
        }
      }
    }
  }

  // ======================================================================
  void
  EventOutputQueue::FireConsumer::fire(const Params_KeyboardSpecialEventCallback& params)
  {
    // skip no-outputable keycodes.
    // Note: check before FireModifiers to avoid meaningless modifier event.
    if (params.key == ConsumerKeyCode::VK_NONE ||
        params.key == ConsumerKeyCode::VK_PSEUDO_KEY) {
      return;
    }

    FireModifiers::fire();

    Params_KeyboardSpecialEventCallback::auto_ptr ptr(Params_KeyboardSpecialEventCallback::alloc(params.eventType, params.flags, params.key, params.repeat));
    if (ptr) {
      EventOutputQueue::push(*ptr);
    }
  }

  // ======================================================================
  Buttons EventOutputQueue::FireRelativePointer::lastButtons_(0);

  void
  EventOutputQueue::FireRelativePointer::fire(Buttons toButtons, int dx, int dy)
  {
    // When changing space to command+left click,
    //   __KeyToKey__ KeyCode::SPACE, PointingButton::LEFT, ModifierFlag::COMMAND_L
    //
    // We need to release command key after left click was released as follows.
    //   (1) KeyDown   Command_L
    //   (2) MouseDown Left
    //   (3) MouseUp   Left
    //   (4) KeyUp     Command_L
    //
    // For mouse drag support, command modifier is increased as normal (not temporary_increase).
    // Therefore, command modifier is decreased when space key is released.
    // If we call FireModifiers::fire() at the begining of this function,
    // input events are fired as follows order.
    //   (1) KeyDown   Command_L
    //   (2) MouseDown Left
    //   (3) KeyUp     Command_L
    //   (4) MouseUp   Left
    //
    // It's not desired order.
    // So, we call FireModifiers::fire() at the end of this function when button is released.

    Buttons releasedButtons = lastButtons_;
    releasedButtons.remove(toButtons);
    bool isButtonReleased = ! releasedButtons.isNONE();

    if (! isButtonReleased) {
      FireModifiers::fire();
    }

    // Sending button event
    if (lastButtons_ != toButtons) {
      lastButtons_ = toButtons;

      Params_RelativePointerEventCallback::auto_ptr ptr(Params_RelativePointerEventCallback::alloc(toButtons, 0, 0, PointingButton::NONE, false));
      if (! ptr) return;
      Params_RelativePointerEventCallback& params = *ptr;

      EventOutputQueue::push(params);
    }

    // Sending cursor
    if (dx != 0 || dy != 0) {
      Params_RelativePointerEventCallback::auto_ptr ptr(Params_RelativePointerEventCallback::alloc(toButtons, dx, dy, PointingButton::NONE, false));
      if (! ptr) return;
      Params_RelativePointerEventCallback& params = *ptr;

      EventOutputQueue::push(params);
    }

    if (isButtonReleased) {
      FireModifiers::fire();
    }
  }

  // ======================================================================
  void
  EventOutputQueue::FireScrollWheel::fire(const Params_ScrollWheelEventCallback& params)
  {
    FireModifiers::fire();
    EventOutputQueue::push(params);
  }

  void
  EventOutputQueue::FireScrollWheel::fire(int delta1, int delta2)
  {
    short deltaAxis1;
    short deltaAxis2;
    IOFixed fixedDelta1;
    IOFixed fixedDelta2;
    SInt32 pointDelta1;
    SInt32 pointDelta2;

    int relative2scroll_rate = Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_pointing_relative2scroll_rate);

    deltaAxis1 = (delta1 * relative2scroll_rate) / 1024 / DELTA_SCALE;
    deltaAxis2 = (delta2 * relative2scroll_rate) / 1024 / DELTA_SCALE;

    fixedDelta1 = (delta1 * relative2scroll_rate) * (POINTING_FIXED_SCALE / 1024) / DELTA_SCALE;
    fixedDelta2 = (delta2 * relative2scroll_rate) * (POINTING_FIXED_SCALE / 1024) / DELTA_SCALE;

    pointDelta1 = (delta1 * POINTING_POINT_SCALE * relative2scroll_rate) / 1024 / DELTA_SCALE;
    pointDelta2 = (delta2 * POINTING_POINT_SCALE * relative2scroll_rate) / 1024 / DELTA_SCALE;

    // see IOHIDSystem/IOHIDevicePrivateKeys.h about options.
    const int kScrollTypeContinuous_ = 0x0001;
    int options = kScrollTypeContinuous_;

    Params_ScrollWheelEventCallback::auto_ptr ptr(Params_ScrollWheelEventCallback::alloc(deltaAxis1,  deltaAxis2, 0,
                                                                                fixedDelta1, fixedDelta2, 0,
                                                                                pointDelta1, pointDelta2, 0,
                                                                                options));
    if (! ptr) return;
    EventOutputQueue::FireScrollWheel::fire(*ptr);
  }

  // ======================================================================
  void
  EventOutputQueue::FireWait::fire(const Params_Wait& params)
  {
    EventOutputQueue::push(params);
  }
}
<commit_msg>revert min delay (-> 1ms)<commit_after>#include "Config.hpp"
#include "EventOutputQueue.hpp"
#include "ListHookedConsumer.hpp"
#include "ListHookedKeyboard.hpp"
#include "ListHookedPointing.hpp"
#include "PressDownKeys.hpp"
#include "RemapClass.hpp"
#include "VirtualKey.hpp"
#include "VK_IOHIDPOSTEVENT.hpp"

namespace org_pqrs_Karabiner {
  List* EventOutputQueue::queue_ = NULL;
  TimerWrapper EventOutputQueue::fire_timer_;
  Buttons EventOutputQueue::previousButtons_;

  void
  EventOutputQueue::initialize(IOWorkLoop& workloop)
  {
    queue_ = new List();
    fire_timer_.initialize(&workloop, NULL, EventOutputQueue::fire_timer_callback);
  }

  void
  EventOutputQueue::terminate(void)
  {
    fire_timer_.terminate();

    if (queue_) {
      delete queue_;
    }
  }

  // ----------------------------------------------------------------------
#define PUSH_TO_OUTPUTQUEUE {           \
    if (! queue_) return;               \
                                        \
    queue_->push_back(new Item(p));     \
    fire_timer_.setTimeoutMS(0, false); \
}
  void EventOutputQueue::push(const Params_KeyboardEventCallBack& p) {
    PUSH_TO_OUTPUTQUEUE;
    if (p.eventType == EventType::DOWN) {
      FlagStatus::globalFlagStatus().sticky_clear();
    }
  }
  void EventOutputQueue::push(const Params_KeyboardSpecialEventCallback& p) {
    PUSH_TO_OUTPUTQUEUE;
    if (p.ex_iskeydown) {
      FlagStatus::globalFlagStatus().sticky_clear();
    }
  }
  void EventOutputQueue::push(const Params_RelativePointerEventCallback& p) {
    PUSH_TO_OUTPUTQUEUE;
    if (p.buttons != Buttons(0)) {
      FlagStatus::globalFlagStatus().sticky_clear();
    }
  }
  void EventOutputQueue::push(const Params_ScrollWheelEventCallback& p) {
    PUSH_TO_OUTPUTQUEUE;
    FlagStatus::globalFlagStatus().sticky_clear();
  }
  void EventOutputQueue::push(const Params_Wait& p) {
    PUSH_TO_OUTPUTQUEUE;
  }
#undef PUSH_TO_OUTPUTQUEUE

  // ----------------------------------------------------------------------
  unsigned int
  EventOutputQueue::calcDelay(const ParamsUnion& paramsUnion)
  {
    // ----------------------------------------
    {
      // Delay before and after modifier
      //
      // We need to wait before and after a modifier event because
      // events will be dropped by window server if
      // we send a mouse click event and a modifier event at the same time.
      //
      // For example, change middle click to command-left click by this autogen.
      // We need to put a wait on this setting in Mail.app.
      //    <autogen>
      //      __KeyToKey__
      //      PointingButton::MIDDLE, ModifierFlag::NONE,
      //      PointingButton::LEFT, ModifierFlag::COMMAND_L,
      //    </autogen>
      //
      if (paramsUnion.isModifier()) {
        return Config::get_wait_before_and_after_a_modifier_key_event();
      }
    }

    // ----------------------------------------
    {
      // Delay before and after modifier
      //
      // We need to wait before and after a pointing device click event because
      // events will be dropped by processes (eg. NetBeans) if
      // we send press button event and release button event at the same time.
      //

      Params_RelativePointerEventCallback* params = paramsUnion.get_Params_RelativePointerEventCallback();
      if (params) {
        if (params->buttons != previousButtons_) {
          previousButtons_ = params->buttons;
          return Config::get_wait_before_and_after_a_click_event();
        }
      }
    }

    // ----------------------------------------
    return 0;
  }

  namespace {
    unsigned int maxDelay(unsigned int v1, unsigned int v2) {
      if (v1 > v2) {
        return v1;
      } else {
        return v2;
      }
    }
  }

  void
  EventOutputQueue::fire_timer_callback(OSObject* /* owner */, IOTimerEventSource* /* sender */)
  {
    if (! queue_) return;

    // IOLOG_DEVEL("EventOutputQueue::fire queue_->size = %d\n", static_cast<int>(queue_->size()));

    Item* p = static_cast<Item*>(queue_->front());
    if (! p) return;

    // Delay after modifier or click.
    unsigned int delay = calcDelay(p->params);

    // ----------------------------------------
    switch ((p->params).type) {
      case ParamsUnion::KEYBOARD:
      {
        Params_KeyboardEventCallBack* params = (p->params).get_Params_KeyboardEventCallBack();
        if (params) {
          bool handled = VirtualKey::handleAfterEnqueued(*params);
          if (! handled) {
            ListHookedKeyboard::instance().apply(*params);
          }
        }

        // We need to wait at least 1ms in order to avoid changing key sequence order randomly.
        // (If VMware Fusion's driver is installed, the wrong order issue will be happen.)
        delay = maxDelay(delay, 1);
        break;
      }
      case ParamsUnion::KEYBOARD_SPECIAL:
      {
        Params_KeyboardSpecialEventCallback* params = (p->params).get_Params_KeyboardSpecialEventCallback();
        if (params) {
          if (! ListHookedConsumer::instance().apply(*params)) {
            // If there is no consumer device, we send an event as a software key.
            VirtualKey::VK_IOHIDPOSTEVENT::post(*params);
          }
        }

        // We need to wait at least 1ms in order to avoid changing key sequence order randomly.
        // (If VMware Fusion's driver is installed, the wrong order issue will be happen.)
        delay = maxDelay(delay, 1);
        break;
      }
      case ParamsUnion::RELATIVE_POINTER:
      {
        Params_RelativePointerEventCallback* params = (p->params).get_Params_RelativePointerEventCallback();
        if (params) {
          ListHookedPointing::instance().apply(*params);
        }
        break;
      }
      case ParamsUnion::SCROLL_WHEEL:
      {
        Params_ScrollWheelEventCallback* params = (p->params).get_Params_ScrollWheelEventCallback();
        if (params) {
          ListHookedPointing::instance().apply(*params);
        }
        break;
      }
      case ParamsUnion::WAIT:
      {
        Params_Wait* params = (p->params).get_Params_Wait();
        if (params) {
          delay = maxDelay(delay, static_cast<unsigned int>(params->milliseconds));
        }
        break;
      }
      case ParamsUnion::UPDATE_FLAGS:
        // do nothing
        break;
    }

    queue_->pop_front();

    // ----------------------------------------
    // Set timeout for next event.

    // Delay before modifier and click.
    Item* next = static_cast<Item*>(queue_->front());
    if (! next) return;
    delay = maxDelay(delay, calcDelay(next->params));

    fire_timer_.setTimeoutMS(delay);
  }

  // ======================================================================
  Flags EventOutputQueue::FireModifiers::lastFlags_(0);

  void
  EventOutputQueue::FireModifiers::fire(Flags toFlags, KeyboardType keyboardType)
  {
    if (lastFlags_ == toFlags) return;

    // ------------------------------------------------------------
    // At first we handle KeyUp events and handle KeyDown events next.
    // We need to end KeyDown at Command+Space to Option_L+Shift_L.
    //
    // When Option_L+Shift_L has a meaning (switch input language at Windows),
    // it does not works well when the last is KeyUp of Command.

    // ------------------------------------------------------------
    // KeyUp
    for (size_t i = 0; i < FlagStatus::globalFlagStatus().itemSize(); ++i) {
      ModifierFlag flag = FlagStatus::globalFlagStatus().getFlag(i);

      // Skipping ModifierFlag::NUMPAD
      if (flag.getKeyCode() == KeyCode::VK_NONE) continue;
      // Skipping virtual modifiers.
      if (flag.getRawBits() == 0) continue;

      // ----------------------------------------
      if (! lastFlags_.isOn(flag)) continue;
      if (toFlags.isOn(flag)) continue;

      lastFlags_.remove(flag);

      Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(EventType::MODIFY, lastFlags_, flag.getKeyCode(), keyboardType, false));
      if (! ptr) continue;
      EventOutputQueue::push(*ptr);
    }

    // KeyDown
    for (size_t i = 0; i < FlagStatus::globalFlagStatus().itemSize(); ++i) {
      ModifierFlag flag = FlagStatus::globalFlagStatus().getFlag(i);

      // Skipping ModifierFlag::NUMPAD
      if (flag.getKeyCode() == KeyCode::VK_NONE) continue;
      // Skipping virtual modifiers.
      if (flag.getRawBits() == 0) continue;

      // ----------------------------------------
      if (! toFlags.isOn(flag)) continue;
      if (lastFlags_.isOn(flag)) continue;

      lastFlags_.add(flag);

      Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(EventType::MODIFY, lastFlags_, flag.getKeyCode(), keyboardType, false));
      if (! ptr) continue;
      EventOutputQueue::push(*ptr);
    }

    lastFlags_ = toFlags;
  }

  // ======================================================================
  void
  EventOutputQueue::FireKey::fire(const Params_KeyboardEventCallBack& params)
  {
    if (VirtualKey::handle(params)) return;

    // ------------------------------------------------------------
    KeyCode newkeycode = params.key;
    Flags newflags = params.flags;

    KeyCode::reverseNormalizeKey(newkeycode, newflags, params.eventType, params.keyboardType);

    // skip no-outputable keycodes.
    // Note: check before FireModifiers to avoid meaningless modifier event.
    if (newkeycode == KeyCode::VK_NONE ||
        newkeycode == KeyCode::VK_PSEUDO_KEY) {
      return;
    }
    if (newkeycode.isModifier() && newkeycode.getModifierFlag().getRawBits() == 0) {
      // virtual modifiers.
      return;
    }

    FireModifiers::fire(newflags, params.keyboardType);

    if (params.eventType == EventType::DOWN || params.eventType == EventType::UP) {
      Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(params.eventType, newflags, newkeycode,
                                                                                params.charCode, params.charSet, params.origCharCode, params.origCharSet,
                                                                                params.keyboardType, params.repeat));
      if (ptr) {
        EventOutputQueue::push(*ptr);
      }

      if (! params.repeat) {
        if (params.eventType == EventType::DOWN) {
          PressDownKeys::add(newkeycode, params.keyboardType);
        } else {
          PressDownKeys::remove(newkeycode, params.keyboardType);
        }
      }
    }
  }

  // ======================================================================
  void
  EventOutputQueue::FireConsumer::fire(const Params_KeyboardSpecialEventCallback& params)
  {
    // skip no-outputable keycodes.
    // Note: check before FireModifiers to avoid meaningless modifier event.
    if (params.key == ConsumerKeyCode::VK_NONE ||
        params.key == ConsumerKeyCode::VK_PSEUDO_KEY) {
      return;
    }

    FireModifiers::fire();

    Params_KeyboardSpecialEventCallback::auto_ptr ptr(Params_KeyboardSpecialEventCallback::alloc(params.eventType, params.flags, params.key, params.repeat));
    if (ptr) {
      EventOutputQueue::push(*ptr);
    }
  }

  // ======================================================================
  Buttons EventOutputQueue::FireRelativePointer::lastButtons_(0);

  void
  EventOutputQueue::FireRelativePointer::fire(Buttons toButtons, int dx, int dy)
  {
    // When changing space to command+left click,
    //   __KeyToKey__ KeyCode::SPACE, PointingButton::LEFT, ModifierFlag::COMMAND_L
    //
    // We need to release command key after left click was released as follows.
    //   (1) KeyDown   Command_L
    //   (2) MouseDown Left
    //   (3) MouseUp   Left
    //   (4) KeyUp     Command_L
    //
    // For mouse drag support, command modifier is increased as normal (not temporary_increase).
    // Therefore, command modifier is decreased when space key is released.
    // If we call FireModifiers::fire() at the begining of this function,
    // input events are fired as follows order.
    //   (1) KeyDown   Command_L
    //   (2) MouseDown Left
    //   (3) KeyUp     Command_L
    //   (4) MouseUp   Left
    //
    // It's not desired order.
    // So, we call FireModifiers::fire() at the end of this function when button is released.

    Buttons releasedButtons = lastButtons_;
    releasedButtons.remove(toButtons);
    bool isButtonReleased = ! releasedButtons.isNONE();

    if (! isButtonReleased) {
      FireModifiers::fire();
    }

    // Sending button event
    if (lastButtons_ != toButtons) {
      lastButtons_ = toButtons;

      Params_RelativePointerEventCallback::auto_ptr ptr(Params_RelativePointerEventCallback::alloc(toButtons, 0, 0, PointingButton::NONE, false));
      if (! ptr) return;
      Params_RelativePointerEventCallback& params = *ptr;

      EventOutputQueue::push(params);
    }

    // Sending cursor
    if (dx != 0 || dy != 0) {
      Params_RelativePointerEventCallback::auto_ptr ptr(Params_RelativePointerEventCallback::alloc(toButtons, dx, dy, PointingButton::NONE, false));
      if (! ptr) return;
      Params_RelativePointerEventCallback& params = *ptr;

      EventOutputQueue::push(params);
    }

    if (isButtonReleased) {
      FireModifiers::fire();
    }
  }

  // ======================================================================
  void
  EventOutputQueue::FireScrollWheel::fire(const Params_ScrollWheelEventCallback& params)
  {
    FireModifiers::fire();
    EventOutputQueue::push(params);
  }

  void
  EventOutputQueue::FireScrollWheel::fire(int delta1, int delta2)
  {
    short deltaAxis1;
    short deltaAxis2;
    IOFixed fixedDelta1;
    IOFixed fixedDelta2;
    SInt32 pointDelta1;
    SInt32 pointDelta2;

    int relative2scroll_rate = Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_pointing_relative2scroll_rate);

    deltaAxis1 = (delta1 * relative2scroll_rate) / 1024 / DELTA_SCALE;
    deltaAxis2 = (delta2 * relative2scroll_rate) / 1024 / DELTA_SCALE;

    fixedDelta1 = (delta1 * relative2scroll_rate) * (POINTING_FIXED_SCALE / 1024) / DELTA_SCALE;
    fixedDelta2 = (delta2 * relative2scroll_rate) * (POINTING_FIXED_SCALE / 1024) / DELTA_SCALE;

    pointDelta1 = (delta1 * POINTING_POINT_SCALE * relative2scroll_rate) / 1024 / DELTA_SCALE;
    pointDelta2 = (delta2 * POINTING_POINT_SCALE * relative2scroll_rate) / 1024 / DELTA_SCALE;

    // see IOHIDSystem/IOHIDevicePrivateKeys.h about options.
    const int kScrollTypeContinuous_ = 0x0001;
    int options = kScrollTypeContinuous_;

    Params_ScrollWheelEventCallback::auto_ptr ptr(Params_ScrollWheelEventCallback::alloc(deltaAxis1,  deltaAxis2, 0,
                                                                                fixedDelta1, fixedDelta2, 0,
                                                                                pointDelta1, pointDelta2, 0,
                                                                                options));
    if (! ptr) return;
    EventOutputQueue::FireScrollWheel::fire(*ptr);
  }

  // ======================================================================
  void
  EventOutputQueue::FireWait::fire(const Params_Wait& params)
  {
    EventOutputQueue::push(params);
  }
}
<|endoftext|>