microRTPS: client: reduce name set by pthread_setname_np since Linux requires names (with NUL) to fit in 16 chars, otherwise returns ERANGE (34)

@###############################################
@#
@# EmPy template for generating microRTPS_client.cpp file
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - msgs (List) list of all msg files
@#  - multi_topics (List) list of all multi-topic names
@#  - ids (List) list of all RTPS msg ids
@###############################################
@{
import os

import genmsg.msgs

from px_generate_uorb_topic_helper import * # this is in Tools/
from px_generate_uorb_topic_files import MsgScope # this is in Tools/

topic_names = [s.short_name for s in spec]
send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
send_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
receive_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
}@
/****************************************************************************
 *
 * Copyright (c) 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
 * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "microRTPS_transport.h"
#include "microRTPS_client.h"

#include <inttypes.h>
#include <cstdio>
#include <ctime>
#include <pthread.h>

#include <ucdr/microcdr.h>
#include <px4_time.h>
#include <uORB/uORB.h>

#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
@[for topic in list(set(topic_names))]@
#include <uORB/topics/@(topic).h>
#include <uORB_microcdr/topics/@(topic).h>
@[end for]@

void *send(void *data);

uint8_t last_remote_msg_seq = 0;
uint8_t last_msg_seq = 0;

@[if recv_topics]@
// Publishers for received messages
struct RcvTopicsPubs {
@[    for idx, topic in enumerate(recv_topics)]@
	uORB::Publication <@(receive_base_types[idx])_s> @(topic)_pub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
// Subscribers for messages to send
struct SendTopicsSubs {
@[    for idx, topic in enumerate(send_topics)]@
	uORB::Subscription @(topic)_sub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
void *send(void * /*unused*/)
{
	char data_buffer[BUFFER_SIZE] = {};
	uint64_t sent = 0, total_sent = 0;
	int loop = 0, read = 0;
	uint32_t length = 0;
	size_t header_length = 0;
	SendTopicsSubs *subs = new SendTopicsSubs();

	// ucdrBuffer to serialize using the user defined buffer
	ucdrBuffer writer;
	header_length = transport_node->get_header_length();
	ucdr_init_buffer(&writer, reinterpret_cast<uint8_t *>(&data_buffer[header_length]), BUFFER_SIZE - header_length);

	struct timespec begin;
	px4_clock_gettime(CLOCK_REALTIME, &begin);

	while (!_should_exit_task) {
@[    for idx, topic in enumerate(send_topics)]@
		{
			@(send_base_types[idx])_s @(topic)_data;

			if (subs->@(topic)_sub.update(&@(topic)_data))
			{
@[        if topic == 'Timesync' or topic == 'timesync']@
				if (@(topic)_data.sys_id == 0 && @(topic)_data.seq != last_remote_msg_seq && @(topic)_data.tc1 == 0) {
					last_remote_msg_seq = @(topic)_data.seq;

					@(topic)_data.timestamp = hrt_absolute_time();
					@(topic)_data.sys_id = 1;
					@(topic)_data.seq = last_msg_seq;
					@(topic)_data.tc1 = hrt_absolute_time() * 1000ULL;
					@(topic)_data.ts1 = @(topic)_data.ts1;

					last_msg_seq++;
@[        end if]@
					// copy raw data into local buffer. Payload is shifted by header length to make room for header
					serialize_@(send_base_types[idx])(&writer, &@(topic)_data, &data_buffer[header_length], &length);

					if (0 < (read = transport_node->write(static_cast<char>(@(rtps_message_id(ids, topic))), data_buffer, length))) {
						total_sent += read;
						++sent;
					}

@[        if topic == 'Timesync' or topic == 'timesync']@
				}

@[        end if]@
			}
		}
@[    end for]@
		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

	struct timespec end;

	px4_clock_gettime(CLOCK_REALTIME, &end);

	double elapsed_secs = end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9;

	PX4_INFO("SENT: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - %.02fKB/s",
		 sent, loop, total_sent, elapsed_secs, total_sent / (1e3 * elapsed_secs));

	delete subs;

	return nullptr;
}

static int launch_send_thread(pthread_t &sender_thread)
{
	pthread_attr_t sender_thread_attr;
	pthread_attr_init(&sender_thread_attr);
	pthread_attr_setstacksize(&sender_thread_attr, PX4_STACK_ADJUSTED(2250));
	struct sched_param param;
	(void)pthread_attr_getschedparam(&sender_thread_attr, &param);
	param.sched_priority = SCHED_PRIORITY_DEFAULT;
	(void)pthread_attr_setschedparam(&sender_thread_attr, &param);
	pthread_create(&sender_thread, &sender_thread_attr, send, nullptr);
	int rc = pthread_setname_np(sender_thread, "urtpsclient_snd");
	if (rc != 0)
	{
		errno = rc;
		PX4_ERR("Could not set pthread name (%d)", errno);
	}
	pthread_attr_destroy(&sender_thread_attr);

	return 0;
}
@[end if]@

void micrortps_start_topics(struct timespec &begin, uint64_t &total_read, uint64_t &received, int &loop)
{
@[if recv_topics]@
	char data_buffer[BUFFER_SIZE] = {};
	int read = 0;
	uint8_t topic_ID = 255;
	RcvTopicsPubs *pubs = new RcvTopicsPubs();

	// Set the main task name to 'urtpsclient_rcv' in case there is
	// data to receive
	px4_prctl(PR_SET_NAME, "urtpsclient_rcv", px4_getpid());

	// ucdrBuffer to deserialize using the user defined buffer
	ucdrBuffer reader;
	ucdr_init_buffer(&reader, reinterpret_cast<uint8_t *>(data_buffer), BUFFER_SIZE);
@[end if]@

	px4_clock_gettime(CLOCK_REALTIME, &begin);
	_should_exit_task = false;
@[if send_topics]@

	// create a thread for sending data to the simulator
	pthread_t sender_thread;
	launch_send_thread(sender_thread);
@[end if]@

	while (!_should_exit_task) {
@[if recv_topics]@
		while (0 < (read = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE))) {
			total_read += read;
			uint64_t read_time = hrt_absolute_time();

			switch (topic_ID) {
@[    for idx, topic in enumerate(recv_topics)]@
			case @(rtps_message_id(ids, topic)): {
				@(receive_base_types[idx])_s @(topic)_data;
				deserialize_@(receive_base_types[idx])(&reader, &@(topic)_data, data_buffer);

				if (@(topic)_data.timestamp > read_time) {
					// don't allow timestamps from the future
					@(topic)_data.timestamp = read_time;
				}

				pubs->@(topic)_pub.publish(@(topic)_data);
				++received;
			}
			break;
@[    end for]@
			default:
				PX4_WARN("Unexpected topic ID '%hhu' to getMsg. Please make sure the client is capable of parsing the message associated to the topic ID '%hhu'",
					 topic_ID, topic_ID);
				break;
			}
		}
@[end if]@

		// loop forever if informed loop number is negative
		if (_options.loops >= 0 && loop >= _options.loops) { break; }

		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

@[if recv_topics]@
	delete pubs;
@[end if]@
@[if send_topics]@
	_should_exit_task = true;
	pthread_join(sender_thread, nullptr);
@[end if]@
}

@###############################################
@#
@# EmPy template for generating microRTPS_client.cpp file
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - msgs (List) list of all msg files
@#  - multi_topics (List) list of all multi-topic names
@#  - ids (List) list of all RTPS msg ids
@###############################################
@{
import os

import genmsg.msgs

from px_generate_uorb_topic_helper import * # this is in Tools/
from px_generate_uorb_topic_files import MsgScope # this is in Tools/

topic_names = [s.short_name for s in spec]
send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
send_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
receive_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
}@
/****************************************************************************
 *
 * Copyright (c) 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
 * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "microRTPS_transport.h"
#include "microRTPS_client.h"

#include <inttypes.h>
#include <cstdio>
#include <ctime>
#include <pthread.h>

#include <ucdr/microcdr.h>
#include <px4_time.h>
#include <uORB/uORB.h>

#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
@[for topic in list(set(topic_names))]@
#include <uORB/topics/@(topic).h>
#include <uORB_microcdr/topics/@(topic).h>
@[end for]@

void *send(void *data);

uint8_t last_remote_msg_seq = 0;
uint8_t last_msg_seq = 0;

@[if recv_topics]@
// Publishers for received messages
struct RcvTopicsPubs {
@[    for idx, topic in enumerate(recv_topics)]@
	uORB::Publication <@(receive_base_types[idx])_s> @(topic)_pub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
// Subscribers for messages to send
struct SendTopicsSubs {
@[    for idx, topic in enumerate(send_topics)]@
	uORB::Subscription @(topic)_sub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
void *send(void * /*unused*/)
{
	char data_buffer[BUFFER_SIZE] = {};
	uint64_t sent = 0, total_sent = 0;
	int loop = 0, read = 0;
	uint32_t length = 0;
	size_t header_length = 0;
	SendTopicsSubs *subs = new SendTopicsSubs();

	// ucdrBuffer to serialize using the user defined buffer
	ucdrBuffer writer;
	header_length = transport_node->get_header_length();
	ucdr_init_buffer(&writer, reinterpret_cast<uint8_t *>(&data_buffer[header_length]), BUFFER_SIZE - header_length);

	struct timespec begin;
	px4_clock_gettime(CLOCK_REALTIME, &begin);

	while (!_should_exit_task) {
@[    for idx, topic in enumerate(send_topics)]@
		{
			@(send_base_types[idx])_s @(topic)_data;

			if (subs->@(topic)_sub.update(&@(topic)_data))
			{
@[        if topic == 'Timesync' or topic == 'timesync']@
				if (@(topic)_data.sys_id == 0 && @(topic)_data.seq != last_remote_msg_seq && @(topic)_data.tc1 == 0) {
					last_remote_msg_seq = @(topic)_data.seq;

					@(topic)_data.timestamp = hrt_absolute_time();
					@(topic)_data.sys_id = 1;
					@(topic)_data.seq = last_msg_seq;
					@(topic)_data.tc1 = hrt_absolute_time() * 1000ULL;
					@(topic)_data.ts1 = @(topic)_data.ts1;

					last_msg_seq++;
@[        end if]@
					// copy raw data into local buffer. Payload is shifted by header length to make room for header
					serialize_@(send_base_types[idx])(&writer, &@(topic)_data, &data_buffer[header_length], &length);

					if (0 < (read = transport_node->write(static_cast<char>(@(rtps_message_id(ids, topic))), data_buffer, length))) {
						total_sent += read;
						++sent;
					}

@[        if topic == 'Timesync' or topic == 'timesync']@
				}

@[        end if]@
			}
		}
@[    end for]@
		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

	struct timespec end;

	px4_clock_gettime(CLOCK_REALTIME, &end);

	double elapsed_secs = end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9;

	PX4_INFO("SENT: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - %.02fKB/s",
		 sent, loop, total_sent, elapsed_secs, total_sent / (1e3 * elapsed_secs));

	delete subs;

	return nullptr;
}

static int launch_send_thread(pthread_t &sender_thread)
{
	pthread_attr_t sender_thread_attr;
	pthread_attr_init(&sender_thread_attr);
	pthread_attr_setstacksize(&sender_thread_attr, PX4_STACK_ADJUSTED(2250));
	struct sched_param param;
	(void)pthread_attr_getschedparam(&sender_thread_attr, &param);
	param.sched_priority = SCHED_PRIORITY_DEFAULT;
	(void)pthread_attr_setschedparam(&sender_thread_attr, &param);
	pthread_create(&sender_thread, &sender_thread_attr, send, nullptr);

	if (pthread_setname_np(sender_thread, "micrortps_client_send")) {
		PX4_ERR("Could not set pthread name (%d)", errno);
	}

	pthread_attr_destroy(&sender_thread_attr);

	return 0;
}
@[end if]@

void micrortps_start_topics(struct timespec &begin, uint64_t &total_read, uint64_t &received, int &loop)
{
@[if recv_topics]@
	char data_buffer[BUFFER_SIZE] = {};
	int read = 0;
	uint8_t topic_ID = 255;
	RcvTopicsPubs *pubs = new RcvTopicsPubs();

	// Set the main task name to 'micrortps_client_rcv' in case there is
	// data to receive
	px4_prctl(PR_SET_NAME, "micrortps_client_rcv", px4_getpid());

	// ucdrBuffer to deserialize using the user defined buffer
	ucdrBuffer reader;
	ucdr_init_buffer(&reader, reinterpret_cast<uint8_t *>(data_buffer), BUFFER_SIZE);
@[end if]@

	px4_clock_gettime(CLOCK_REALTIME, &begin);
	_should_exit_task = false;
@[if send_topics]@

	// create a thread for sending data to the simulator
	pthread_t sender_thread;
	launch_send_thread(sender_thread);
@[end if]@

	while (!_should_exit_task) {
@[if recv_topics]@
		while (0 < (read = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE))) {
			total_read += read;
			uint64_t read_time = hrt_absolute_time();

			switch (topic_ID) {
@[    for idx, topic in enumerate(recv_topics)]@
			case @(rtps_message_id(ids, topic)): {
				@(receive_base_types[idx])_s @(topic)_data;
				deserialize_@(receive_base_types[idx])(&reader, &@(topic)_data, data_buffer);

				if (@(topic)_data.timestamp > read_time) {
					// don't allow timestamps from the future
					@(topic)_data.timestamp = read_time;
				}

				pubs->@(topic)_pub.publish(@(topic)_data);
				++received;
			}
			break;
@[    end for]@
			default:
				PX4_WARN("Unexpected topic ID '%hhu' to getMsg. Please make sure the client is capable of parsing the message associated to the topic ID '%hhu'",
					 topic_ID, topic_ID);
				break;
			}
		}
@[end if]@

		// loop forever if informed loop number is negative
		if (_options.loops >= 0 && loop >= _options.loops) { break; }

		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

@[if recv_topics]@
	delete pubs;
@[end if]@
@[if send_topics]@
	_should_exit_task = true;
	pthread_join(sender_thread, nullptr);
@[end if]@
}


don't need a new when returned from fromLookAt function

/*  Sirikata
 *  moveAndRotateTo.em
 *
 *  Copyright (c) 2011, Daniel Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

system.require('moveAndRotate.em');
/** MoveAndRotate gives you intuitive control over moving to a position and
 *  attaining a final rotation. The most common use for this is when you want to
 *  send an object to a particular location with a particular facing when it gets there.
 *  A callback is presented when the object arrives at the destination so further movement
 *  can be queued or an action can take place
 */
std.movement.MoveAndRotateTo = std.movement.MoveAndRotate.extend(
    {
        forceUp:function(upVec) {
            this._force_up=upVec;
        },
        setSpeed:function(speed) {
            this._speed=speed;
        },
        setAngularSpeed:function(angular_speed){
            this._angular_speed=angular_speed;
        },
        setTurningDistance:function(turning_distance){
            this._turning_distance=turning_distance;
        },
        init:function(pres,update_cb, destination, destination_facing, speed, angular_speed, callback){
            this.setSpeed(speed);
            this.setAngularSpeed(angular_speed);
            this._force_up=undefined;
            this._turning_distance=.25;
            std.movement.MoveAndRotate.prototype.init.call(this,pres,update_cb,'rotation');
            if (destination) {
                this.goTo(destination,destination_facing,callback);
            }
        },
        goTo:function(destination,destination_facing, callback) {
            var pos =this._pres.getPosition();
            var forward=destination.sub(pos).normal();
            destination_facing=destination_facing||this._pres.getOrientation();
            var goal_orientation=util.Quaternion.fromLookAt(forward,destination_facing.yAxis());
            if (this._force_up) {
               var tright=this._force_up.cross(forward);
               var tforward=tright.cross(this._force_up).normal();
               goal_orientation=util.Quaternion.fromLookAt(tforward,this._force_up);
            }
            var thus=this;
            var destination_delta=destination.sub(pos);
            var destination_length=destination_delta.length();
            var turning_distance=this._turning_distance;
            destination_delta=destination_delta.scale(turning_distance/destination_length);
            this.goToWaypoint(destination_delta.add(pos),goal_orientation,true, function(){
                 thus.goToWaypoint(destination.add(destination_delta.scale(-.1)),goal_orientation,false,function (){
                   thus.goToWaypoint(destination,(destination_facing||this._pres.orientation()),true,function () {
                                         thus.rotateLocalOrientation(new util.Quaternion(0,0,0,1),true);
                                         thus.move(new util.Vec3(0,0,0),0,true);
                                         callback();
                                     });
               });
            });
        },
        abort:function(doClearVelocityAndAngular) {
           if (this._moveTimer) {
               this._moveTimer.clear();
               this._moveTimer=null;
           }
           if (doClearVelocityAndAngular) {
               this.rotateLocalOrientation(new util.Quaternion(0,0,0,1),true);
               this.move(new util.Vec3(0,0,0),0,true);
           }
        },
        goToWaypoint:function(destination,destination_facing,doTurning,callback) {
            this._callback=callback;
            this._destination=destination;
            this._destination_facing=destination_facing;
            var thus=this;
            var pres=this._pres;
            var pos=pres.getPosition();
            var deltapos=this._destination.sub(pos);
            var distance=deltapos.length();
            var inverseOrientation=pres.getOrientation().inverse();
            var rotation=inverseOrientation.mul(this._destination_facing);
            var angle = Math.abs(rotation.angle());
            var time = distance/this._speed;
            if (angle/this._angular_speed>time){
                time = angle/this._angular_speed;
            }
            if (doTurning) {
                this.move(new util.Vec3(0,0,-1),distance/time,true);
            }else {
                var vel=inverseOrientation.mul(deltapos);
                this.move(vel,1.0/time,true);
            }
            this.rotateLocalOrientation(rotation.scale(1.0/time),true);

            this._moveTimer=system.timeout(time,function() {
                           thus._moveTimer=null;
                           pres.setOrientation(destination_facing);
                           if (!doTurning)
                               pres.setPosition(destination);
                           callback.call(thus);
                       });
        }
    }
);

/*  Sirikata
 *  moveAndRotateTo.em
 *
 *  Copyright (c) 2011, Daniel Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

system.require('moveAndRotate.em');
/** MoveAndRotate gives you intuitive control over moving to a position and
 *  attaining a final rotation. The most common use for this is when you want to
 *  send an object to a particular location with a particular facing when it gets there.
 *  A callback is presented when the object arrives at the destination so further movement
 *  can be queued or an action can take place
 */
std.movement.MoveAndRotateTo = std.movement.MoveAndRotate.extend(
    {
        forceUp:function(upVec) {
            this._force_up=upVec;
        },
        setSpeed:function(speed) {
            this._speed=speed;
        },
        setAngularSpeed:function(angular_speed){
            this._angular_speed=angular_speed;
        },
        setTurningDistance:function(turning_distance){
            this._turning_distance=turning_distance;
        },
        init:function(pres,update_cb, destination, destination_facing, speed, angular_speed, callback){
            this.setSpeed(speed);
            this.setAngularSpeed(angular_speed);
            this._force_up=undefined;
            this._turning_distance=.25;
            std.movement.MoveAndRotate.prototype.init.call(this,pres,update_cb,'rotation');
            if (destination) {
                this.goTo(destination,destination_facing,callback);
            }
        },
        goTo:function(destination,destination_facing, callback) {
            var pos =this._pres.getPosition();
            var forward=destination.sub(pos).normal();
            destination_facing=destination_facing||this._pres.getOrientation();
            var goal_orientation=new util.Quaternion.fromLookAt(forward,destination_facing.yAxis());
            if (this._force_up) {
               var tright=this._force_up.cross(forward);
               var tforward=tright.cross(this._force_up).normal();
               goal_orientation=new util.Quaternion.fromLookAt(tforward,this._force_up);
            }
            var thus=this;
            var destination_delta=destination.sub(pos);
            var destination_length=destination_delta.length();
            var turning_distance=this._turning_distance;
            destination_delta=destination_delta.scale(turning_distance/destination_length);
            this.goToWaypoint(destination_delta.add(pos),goal_orientation,true, function(){
                 thus.goToWaypoint(destination.add(destination_delta.scale(-.1)),goal_orientation,false,function (){
                   thus.goToWaypoint(destination,(destination_facing||this._pres.orientation()),true,function () {
                                         thus.rotateLocalOrientation(new util.Quaternion(0,0,0,1),true);
                                         thus.move(new util.Vec3(0,0,0),0,true);
                                         callback();
                                     });
               });
            });
        },
        abort:function(doClearVelocityAndAngular) {
           if (this._moveTimer) {
               this._moveTimer.clear();
               this._moveTimer=null;
           }
           if (doClearVelocityAndAngular) {
               this.rotateLocalOrientation(new util.Quaternion(0,0,0,1),true);
               this.move(new util.Vec3(0,0,0),0,true);
           }
        },
        goToWaypoint:function(destination,destination_facing,doTurning,callback) {
            this._callback=callback;
            this._destination=destination;
            this._destination_facing=destination_facing;
            var thus=this;
            var pres=this._pres;
            var pos=pres.getPosition();
            var deltapos=this._destination.sub(pos);
            var distance=deltapos.length();
            var inverseOrientation=pres.getOrientation().inverse();
            var rotation=inverseOrientation.mul(this._destination_facing);
            var angle = Math.abs(rotation.angle());
            var time = distance/this._speed;
            if (angle/this._angular_speed>time){
                time = angle/this._angular_speed;
            }
            if (doTurning) {
                this.move(new util.Vec3(0,0,-1),distance/time,true);
            }else {
                var vel=inverseOrientation.mul(deltapos);
                this.move(vel,1.0/time,true);
            }
            this.rotateLocalOrientation(rotation.scale(1.0/time),true);

            this._moveTimer=system.timeout(time,function() {
                           thus._moveTimer=null;
                           pres.setOrientation(destination_facing);
                           if (!doTurning)
                               pres.setPosition(destination);
                           callback.call(thus);
                       });
        }
    }
);


fixup fill in more verbosity

fixup fill in more verbosity

Signed-off-by: Tully Foote <22715502305f5acc3bd5bf22a9c406e70a1e1348@osrfoundation.org>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
@(SNIPPET(
    'property_job-priority',
    priority=30,
))@
@(SNIPPET(
    'property_requeue-job',
))@
@(SNIPPET(
    'property_parameters-definition',
    parameters=[
        {
            'type': 'boolean',
            'name': 'dry_run',
            'description': 'Skip the actual reconfiguration but show the diffs',
        },
    ],
))@
  </properties>
@(SNIPPET(
    'scm_git',
    url=ros_buildfarm_repository.url,
    branch_name=ros_buildfarm_repository.version or 'master',
    relative_target_dir='ros_buildfarm',
    refspec=None,
))@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>agent_on_master</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@(SNIPPET(
    'trigger_timer',
    spec='0 23 * * *',
))@
  </triggers>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy',
    command=
"""// USE PARAMETER FOR BUILD DESCRIPTION
repository_names = build.buildVariableResolver.resolve('repository_names')
if (repository_names) {
  build.setDescription(repository_names)
}
""",
    script_file=None,
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@[for ci_build_name in ci_build_names]@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generate_ci_jobs',
        'rm -fr $WORKSPACE/reconfigure_jobs',
        'mkdir -p $WORKSPACE/docker_generate_ci_jobs',
        'mkdir -p $WORKSPACE/reconfigure_jobs',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generate_ci_jobs/docker.cid > $WORKSPACE/docker_generate_ci_jobs/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfiles for the actual CI tasks',
        'echo "# BEGIN SECTION: Generate Dockerfile - reconfigure jobs for %s"' % (ci_build_name),
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'if [ "$dry_run" = "true" ]; then DRY_RUN_FLAG="--dry-run"; fi',
        'if [ "$repository_names" != "" ]; then REPOSITORY_NAMES_FLAG="--repository-names $repository_names"; fi',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/ci/run_ci_reconfigure_job.py' +
        ' ' + config_url +
        ' ' + rosdistro_name +
        ' ' + ci_build_name +
        ' ' + ' '.join(repository_args) +
        ' --groovy-script /tmp/reconfigure_jobs/reconfigure_jobs.groovy' +
        ' --dockerfile-dir $WORKSPACE/docker_generate_ci_jobs' +
        ' $DRY_RUN_FLAG' +
        ' $REPOSITORY_NAMES_FLAG',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - reconfigure jobs for %s"' % (ci_build_name),
        'cd $WORKSPACE/docker_generate_ci_jobs',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t ci_reconfigure_jobs .',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - reconfigure jobs for %s"' % (ci_build_name),
        '# -e=GIT_BRANCH= is required since Jenkins leaves the wc in detached state',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generate_ci_jobs/docker.cid' +
        ' -e=GIT_BRANCH=$GIT_BRANCH' +
        ' --net=host' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v %s:%s:ro' % (credentials_src, credentials_dst) +
        ' -v $WORKSPACE/reconfigure_jobs:/tmp/reconfigure_jobs' +
        ' ci_reconfigure_jobs',
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_system-groovy',
    command=None,
    script_file='$WORKSPACE/reconfigure_jobs/reconfigure_jobs.groovy',
))@
@[end for]@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=recipients,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@(SNIPPET(
    'build-wrapper_timestamper',
))@
  </buildWrappers>
</project>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
@(SNIPPET(
    'property_job-priority',
    priority=30,
))@
@(SNIPPET(
    'property_requeue-job',
))@
@(SNIPPET(
    'property_parameters-definition',
    parameters=[
        {
            'type': 'boolean',
            'name': 'dry_run',
            'description': 'Skip the actual reconfiguration but show the diffs',
        },
    ],
))@
  </properties>
@(SNIPPET(
    'scm_git',
    url=ros_buildfarm_repository.url,
    branch_name=ros_buildfarm_repository.version or 'master',
    relative_target_dir='ros_buildfarm',
    refspec=None,
))@
  <scmCheckoutRetryCount>2</scmCheckoutRetryCount>
  <assignedNode>agent_on_master</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@(SNIPPET(
    'trigger_timer',
    spec='0 23 * * *',
))@
  </triggers>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy',
    command=
"""// USE PARAMETER FOR BUILD DESCRIPTION
repository_names = build.buildVariableResolver.resolve('repository_names')
if (repository_names) {
  build.setDescription(repository_names)
}
""",
    script_file=None,
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@[for ci_build_name in ci_build_names]@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generate_ci_jobs',
        'rm -fr $WORKSPACE/reconfigure_jobs',
        'mkdir -p $WORKSPACE/docker_generate_ci_jobs',
        'mkdir -p $WORKSPACE/reconfigure_jobs',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generate_ci_jobs/docker.cid > $WORKSPACE/docker_generate_ci_jobs/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfiles for the actual CI tasks',
        'echo "# BEGIN SECTION: Generate Dockerfile - reconfigure jobs for %s"' % (ci_build_name),
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'if [ "$dry_run" = "true" ]; then DRY_RUN_FLAG="--dry-run"; fi',
        'if [ "$repository_names" != "" ]; then REPOSITORY_NAMES_FLAG="--repository-names $repository_names"; fi',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/ci/run_ci_reconfigure_job.py' +
        ' ' + config_url +
        ' ' + rosdistro_name +
        ' ' + ci_build_name +
        ' ' + ' '.join(repository_args) +
        ' --groovy-script /tmp/reconfigure_jobs/reconfigure_jobs.groovy' +
        ' --dockerfile-dir $WORKSPACE/docker_generate_ci_jobs' +
        ' $DRY_RUN_FLAG' +
        ' $REPOSITORY_NAMES_FLAG',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - reconfigure jobs"',
        'cd $WORKSPACE/docker_generate_ci_jobs',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t ci_reconfigure_jobs .',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - reconfigure jobs"',
        '# -e=GIT_BRANCH= is required since Jenkins leaves the wc in detached state',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generate_ci_jobs/docker.cid' +
        ' -e=GIT_BRANCH=$GIT_BRANCH' +
        ' --net=host' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v %s:%s:ro' % (credentials_src, credentials_dst) +
        ' -v $WORKSPACE/reconfigure_jobs:/tmp/reconfigure_jobs' +
        ' ci_reconfigure_jobs',
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_system-groovy',
    command=None,
    script_file='$WORKSPACE/reconfigure_jobs/reconfigure_jobs.groovy',
))@
@[end for]@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=recipients,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@(SNIPPET(
    'build-wrapper_timestamper',
))@
  </buildWrappers>
</project>


Assume ros_buildfarm RPM repos will sign metadata (#777)

Fedora and CentOS repositories contain packages that are individually
GPG signed. Repository metadata is not typically signed. However, YUM
and DNF support signed repository metadata.

There are two reasons that this is a good idea for ros_buildfarm, even
though it deviates from the typical OS strategy:
1. It's what we're already doing in the debian repositories
2. There is repository metadata signing support in the works for pulp.

include('/etc/mock/default.cfg')

# Change the root name since we're modifying the chroot
config_opts['root'] += '-ros-buildfarm'

# Add python3-rpm-macros to resolve %{python3_pkgversion} in rosdep rules
config_opts['chroot_setup_cmd'] += ' python3-rpm-macros'

# Install weak dependencies to get group members
config_opts['yum_builddep_opts'] = config_opts.get('yum_builddep_opts', []) + ['--setopt=install_weak_deps=True']
config_opts['dnf_builddep_opts'] = config_opts.get('dnf_builddep_opts', []) + ['--setopt=install_weak_deps=True']
config_opts['microdnf_builddep_opts'] = config_opts.get('microdnf_builddep_opts', []) + ['--setopt=install_weak_deps=True']

@[if env_vars]@
# Set environment vars from the build config
@[for env_key, env_val in env_vars.items()]@
config_opts['environment']['@env_key'] = '@env_val'
@[end for]
@[end if]@
# Disable debug packages until infrastructure can handle it
config_opts['macros']['debug_package'] = '%{nil}'

# Hack the %{dist} macro to allow release suffixing
config_opts['macros']['%dist'] = '.' + config_opts['dist'] + '%{?dist_suffix}'

# Required for running mock in Docker
config_opts['use_nspawn'] = False

@[if os_name in ['centos', 'rhel'] and os_code_name == '7']@
# Inject g++ 8 into RHEL 7 builds
config_opts['chroot_setup_cmd'] += ' devtoolset-8-gcc-c++ devtoolset-8-make-nonblocking'
config_opts['macros']['_buildshell'] = '/usr/bin/scl enable devtoolset-8 -- /bin/sh'

# Disable weak dependencies on RHEL 7 builds
config_opts['macros']['_without_weak_deps'] = '1'
@[else]@
# Add g++, which is an assumed dependency in ROS
config_opts['chroot_setup_cmd'] += ' gcc-c++ make'
@[end if]@

config_opts['yum.conf'] += """
@[for i, url in enumerate(distribution_repository_urls)]@
[ros-buildfarm-@(i)]
name=ROS Buildfarm Repository @(i) - $basearch
baseurl=@(url)
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ros-buildfarm-@(i)
repo_gpgcheck=@(1 if i < len(distribution_repository_keys) and distribution_repository_keys[i] else 0)
gpgcheck=0
enabled=1

@[end for]@
@[if target_repository]@
[ros-buildfarm-target-source]
name=ROS Buildfarm Repository Target - SRPMS
baseurl=@(target_repository)
repo_gpgcheck=0
gpgcheck=0
enabled=0

@[end if]@
"""

include('/etc/mock/default.cfg')

# Change the root name since we're modifying the chroot
config_opts['root'] += '-ros-buildfarm'

# Add python3-rpm-macros to resolve %{python3_pkgversion} in rosdep rules
config_opts['chroot_setup_cmd'] += ' python3-rpm-macros'

# Install weak dependencies to get group members
config_opts['yum_builddep_opts'] = config_opts.get('yum_builddep_opts', []) + ['--setopt=install_weak_deps=True']
config_opts['dnf_builddep_opts'] = config_opts.get('dnf_builddep_opts', []) + ['--setopt=install_weak_deps=True']
config_opts['microdnf_builddep_opts'] = config_opts.get('microdnf_builddep_opts', []) + ['--setopt=install_weak_deps=True']

@[if env_vars]@
# Set environment vars from the build config
@[for env_key, env_val in env_vars.items()]@
config_opts['environment']['@env_key'] = '@env_val'
@[end for]
@[end if]@
# Disable debug packages until infrastructure can handle it
config_opts['macros']['debug_package'] = '%{nil}'

# Hack the %{dist} macro to allow release suffixing
config_opts['macros']['%dist'] = '.' + config_opts['dist'] + '%{?dist_suffix}'

# Required for running mock in Docker
config_opts['use_nspawn'] = False

@[if os_name in ['centos', 'rhel'] and os_code_name == '7']@
# Inject g++ 8 into RHEL 7 builds
config_opts['chroot_setup_cmd'] += ' devtoolset-8-gcc-c++ devtoolset-8-make-nonblocking'
config_opts['macros']['_buildshell'] = '/usr/bin/scl enable devtoolset-8 -- /bin/sh'

# Disable weak dependencies on RHEL 7 builds
config_opts['macros']['_without_weak_deps'] = '1'
@[else]@
# Add g++, which is an assumed dependency in ROS
config_opts['chroot_setup_cmd'] += ' gcc-c++ make'
@[end if]@

config_opts['yum.conf'] += """
@[for i, url in enumerate(distribution_repository_urls)]@
[ros-buildfarm-@(i)]
name=ROS Buildfarm Repository @(i) - $basearch
baseurl=@(url)
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ros-buildfarm-@(i)
gpgcheck=@(1 if i < len(distribution_repository_keys) and distribution_repository_keys[i] else 0)
enabled=1

@[end for]@
@[if target_repository]@
[ros-buildfarm-target-source]
name=ROS Buildfarm Repository Target - SRPMS
baseurl=@(target_repository)
gpgcheck=0
enabled=0

@[end if]@
"""


fix trigger-jobs job template

fix trigger-jobs job template

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
@(SNIPPET(
    'log-rotator',
    days_to_keep=365,
    num_to_keep=10,
))@
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_job-priority',
    priority=40,
))@
    <hudson.model.ParametersDefinitionProperty>
      <parameterDefinitions>
        <hudson.model.ChoiceParameterDefinition>
          <name>filter</name>
          <description>Select jobs based on their latest build result</description>
          <choices class="java.util.Arrays$ArrayList">
            <a class="string-array">
              <string>not_stable</string>
              <string>worse_than_unstable</string>
              <string>all</string>
              <string>stable</string>
              <string>unstable</string>
              <string>failure</string>
              <string>aborted</string>
              <string>not_built</string>
            </a>
          </choices>
        </hudson.model.ChoiceParameterDefinition>
@[if has_force_parameter]@
        <hudson.model.BooleanParameterDefinition>
          <name>force</name>
          <description>Run documentation generation even if neither the source repository nor any of the tools have changes</description>
          <defaultValue>false</defaultValue>
        </hudson.model.BooleanParameterDefinition>
@[end if]@
      </parameterDefinitions>
    </hudson.model.ParametersDefinitionProperty>
@(SNIPPET(
    'property_requeue-job',
))@
@(SNIPPET(
    'property_disk-usage',
))@
  </properties>
@(SNIPPET(
    'scm_null',
))@
  <assignedNode>master</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy',
    command=
"""import java.util.regex.Matcher
import java.util.regex.Pattern
import hudson.model.AbstractProject
import hudson.model.BooleanParameterValue
import hudson.model.Cause.UserIdCause
import hudson.model.ParametersAction
import hudson.model.Result

build = Thread.currentThread().executable
resolver = build.buildVariableResolver

filter = resolver.resolve("filter")
println "Filter: " + filter

has_force_parameter = %s
boolean force = false
if (has_force_parameter) {
    force = resolver.resolve("force")
    println "Force: " + force
}

pattern = Pattern.compile("%s")
for (p in hudson.model.Hudson.instance.getAllItems(AbstractProject)) {
    if (!pattern.matcher(p.name).matches()) continue
    if (p.isDisabled()) continue
    if (p.isBuilding()) continue
    if (p.isInQueue()) continue

    if (filter != "all") {
        def lb = p.lastBuild
        if (!lb) {
            // job has never been built
            if (filter != "not_built" && filter != "not_stable" && filter != "worse_than_unstable") continue
        } else {
            def r = lb.result
            def pb = lb
            while (!r) {
              pb = pb.previousBuild
              if (!pb) {
                // no finished build yet
                break
              }
              r = pb.result
            }
            if (r == Result.SUCCESS) {
                if (filter != "stable") continue
            } else if (r == Result.UNSTABLE) {
                if (filter != "unstable" && filter != "not_stable") continue
            } else if (r == Result.FAILURE) {
                if (filter != "failure" && filter != "not_stable" && filter != "worse_than_unstable") continue
            } else if (r == Result.ABORTED) {
                if (filter != "aborted" && filter != "not_stable" && filter != "worse_than_unstable") continue
            } else if (!r) {
                if (filter != "not_built" && filter != "not_stable" && filter != "worse_than_unstable") continue
            } else if (r == Result.NOT_BUILT) {
              // this should never happen for the result of a build
              assert false
            } else {
              // unknown result value
              println("Job '" + p.name + "'' has unknown result: " + r)
              assert false
            }
        }
    }
    println p.name
    if (has_force_parameter) {
        p.scheduleBuild(1, new UserIdCause(), new ParametersAction(new BooleanParameterValue("force", force)))
    } else {
        p.scheduleBuild()
    }
}
""" % ('true' if has_force_parameter else 'false', project_name_pattern),
    script_file=None,
    classpath='',
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=recipients,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@(SNIPPET(
    'build-wrapper_timestamper',
))@
  </buildWrappers>
</project>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
@(SNIPPET(
    'log-rotator',
    days_to_keep=365,
    num_to_keep=10,
))@
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_job-priority',
    priority=40,
))@
    <hudson.model.ParametersDefinitionProperty>
      <parameterDefinitions>
        <hudson.model.ChoiceParameterDefinition>
          <name>filter</name>
          <description>Select jobs based on their latest build result</description>
          <choices class="java.util.Arrays$ArrayList">
            <a class="string-array">
              <string>not_stable</string>
              <string>worse_than_unstable</string>
              <string>all</string>
              <string>stable</string>
              <string>unstable</string>
              <string>failure</string>
              <string>aborted</string>
              <string>not_built</string>
            </a>
          </choices>
        </hudson.model.ChoiceParameterDefinition>
@[if has_force_parameter]@
        <hudson.model.BooleanParameterDefinition>
          <name>force</name>
          <description>Run documentation generation even if neither the source repository nor any of the tools have changes</description>
          <defaultValue>false</defaultValue>
        </hudson.model.BooleanParameterDefinition>
@[end if]@
      </parameterDefinitions>
    </hudson.model.ParametersDefinitionProperty>
@(SNIPPET(
    'property_requeue-job',
))@
@(SNIPPET(
    'property_disk-usage',
))@
  </properties>
@(SNIPPET(
    'scm_null',
))@
  <assignedNode>master</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy',
    command=
"""import java.util.regex.Matcher
import java.util.regex.Pattern
import hudson.model.AbstractProject
import hudson.model.BooleanParameterValue
import hudson.model.Cause.UserIdCause
import hudson.model.ParametersAction
import hudson.model.Result

build = Thread.currentThread().executable
resolver = build.buildVariableResolver

filter = resolver.resolve("filter")
println "Filter: " + filter

has_force_parameter = true
boolean force = false
if (has_force_parameter) {
    force = resolver.resolve("force")
    println "Force: " + force
}

pattern = Pattern.compile("%s")
for (p in hudson.model.Hudson.instance.getAllItems(AbstractProject)) {
    if (!pattern.matcher(p.name).matches()) continue
    if (p.isDisabled()) continue
    if (p.isBuilding()) continue
    if (p.isInQueue()) continue

    if (filter != "all") {
        def lb = p.lastBuild
        if (!lb) {
            // job has never been built
            if (filter != "not_built" && filter != "not_stable" && filter != "worse_than_unstable") continue
        } else {
            def r = lb.result
            def pb = lb
            while (!r) {
              pb = pb.previousBuild
              if (!pb) {
                // no finished build yet
                break
              }
              r = pb.result
            }
            if (r == Result.SUCCESS) {
                if (filter != "stable") continue
            } else if (r == Result.UNSTABLE) {
                if (filter != "unstable" && filter != "not_stable") continue
            } else if (r == Result.FAILURE) {
                if (filter != "failure" && filter != "not_stable" && filter != "worse_than_unstable") continue
            } else if (r == Result.ABORTED) {
                if (filter != "aborted" && filter != "not_stable" && filter != "worse_than_unstable") continue
            } else if (!r) {
                if (filter != "not_built" && filter != "not_stable" && filter != "worse_than_unstable") continue
            } else if (r == Result.NOT_BUILT) {
              // this should never happen for the result of a build
              assert false
            } else {
              // unknown result value
              println("Job '" + p.name + "'' has unknown result: " + r)
              assert false
            }
        }
    }
    println p.name
    if (has_force_parameter) {
        p.scheduleBuild(1, new UserIdCause(), new ParametersAction(new BooleanParameterValue("force", force)))
    } else {
        p.scheduleBuild()
    }
}
""" % ('true' if has_force_parameter else 'false', project_name_pattern),
    script_file=None,
    classpath='',
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=recipients,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@(SNIPPET(
    'build-wrapper_timestamper',
))@
  </buildWrappers>
</project>


Buttons: Get back grey border for pressed state.

Otherwise the buttons are not visible if they are in white background,
like in the Control Panel or the Intro Window.

Signed-off-by: Manuel Quiñones <6f5069c5b6be23302a13accec56587944be09079@laptop.org>

${
import math

def my_floor(num):
    return int(math.floor(num))

def my_ceil(num):
    return int(math.ceil(num))

# Should we set the line width in the engine to 2.25, and draw non pixel aligned lines?
# Are these already the correct sizes for the XO?

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 7      # 7.68

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26

# FIXME this should be calculated with the radio size and the bullet
# size:
scale_trough_margin = 8

scale_slider_width = my_floor(2 * subcell_size + line_width)
thickness = my_ceil(line_width)

icon_small = icon_base * 3
icon_large = icon_base * 5

}

* {
    -sugar-focus-line: @white;

    /* A lot of these will probably need to be changed, but this has to
       be done when the exact sizes are known */
    -GtkWidget-interior-focus: 0;

    /* we have to disable focus border for GtkTreeView, see #1261 */
    -GtkTreeView-interior-focus: 1;
    -GtkTreeView-focus-line-width: 0;

    -GtkTextView-interior-focus: 1;

    -GtkWidget-focus-line-width: 0;  /* Prevents some drawing glitches */
    -GtkEntry-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkWidget-focus-padding: 0;
    /* 0.05 works good for both the sugar and sugar-xo themes */
    -GtkWidget-cursor-aspect-ratio: 0.05;

    -GtkWidget-wide-separators: false;
    -GtkWidget-separator-height: $thickness;
    -GtkWidget-separator-width: $thickness;

    -GtkWidget-scroll-arrow-hlength: $subcell_size;
    -GtkWidget-scroll-arrow-vlength: $subcell_size;

    -GtkRange-activate-slider: 1;

    /* We fake the default border in the theme */
    -GtkButton-default-border: 0 0 0 0;
    -GtkButton-default-outside-border: 0 0 0 0;
    -GtkButton-image-spacing: $subcell_size;

    -GtkEntry-progress-border: $thickness $thickness $thickness $thickness;

    -GtkScrolledWindow-scrollbar-spacing: 0;

    -GtkExpander-expander-size: $font_height;
    -GtkExpander-expander-spacing: 2;

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

    -GtkToolbar-space-size: $(2*subcell_size);
    -GtkToolbar-shadow-type: none;

    -GtkProgressBar-min-horizontal-bar-height: $subcell_size;
    -GtkProgressBar-min-vertical-bar-width: $subcell_size;

    -GtkButtonBox-child-min-height: $(3*subcell_size);
    -GtkButtonBox-child-min-width: $(3*subcell_size);
    -GtkButtonBox-child-internal-pad-x: 0;
    -GtkButtonBox-child-internal-pad-y: 0;

    -GtkCheckButton-indicator-size: $radio_size;
    -GtkCheckButton-indicator-spacing: 3;

    -GtkWidget-text-handle-width: 55px;
    -GtkWidget-text-handle-height: 55px;
}

/* Backgrounds and windows */

GtkWindow {
    background-color: @panel_grey;
    color: @black;
}

GtkDialog {
    background-color: @black;
    color: @white;
}

SugarIntroWindow {
    background-color: @white;
}

SugarIntroWindow {
    color: @black;
}

GtkLabel, GtkLabel:insensitive {
    background-color: transparent;
}

/* Handle this differently? */
.window *:insensitive {
    background-color: @panel_grey;
}

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    /* It would be nicer to just set the inner-border, but that does not work
       for combo boxes ... The combobox ignores it, so set it to 0px
       See http://bugzilla.gnome.org/show_bug.cgi?id=485762 */

    -GtkButton-inner-border: 0 0 0 0;
    padding: $(border)px $(border)px $(border)px $(border)px;

    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

.button:focused {
    border-color: @white;
}

.button:active {
    background-color: @white;
}

.button:active:focused {
    color: @black;
    border-color: @button_grey;
}

/* Spin buttons */

.spinbutton.button {
    border-radius: 0px;
    border-width: 0px;
    border-style: solid;
    background-color: @button_grey;
}

.spinbutton.button:last-child {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
    border-width: 0px 0px 0px $(thickness)px;
    border-style: solid;
    border-color: @selection_grey;
}

.spinbutton.button:active {
    background-color: @black;
}

.spinbutton.button:insensitive {
    background-color: @selection_grey;
}

/* Toggle buttons */

GtkToggleButton.button:active {
    background-color: @white;
    color: @black;
}

/* Entries and views */

.view {
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background-color: @white;
    color: @black;
}

GtkTreeView {
    background-color: @button_grey;
}

GtkTreeView row:nth-child(even) {
    background-color: @row_even;
}
GtkTreeView row:nth-child(odd) {
    background-color: @row_odd;
}

.entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @text_field_grey;
    border-style: solid;
    background-color: @text_field_grey;
    color: @black;
    ${ entry_ythickness = my_ceil(0.2 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    ${ entry_xthickness = my_ceil(0.32 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    padding: $(2 * max(my_ceil((3*subcell_size - font_height - entry_ythickness*2)/2.0),0))px $(2 * max(subcell_size - entry_xthickness, 0))px $(2 * max(my_floor((3*subcell_size - font_height - entry_ythickness*2)/2.0), 0))px $(2 * max(subcell_size - entry_xthickness, 0))px;
}

.entry.progressbar {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    background-color: @selection_grey;
}

.entry:focused {
    background-color: @white;
}

.toolbar .entry:focused {
    border-color: @white;
}

.entry:insensitive, .view:insensitive {
    background-color: @button_grey;
}

.entry:insensitive {
    border-color: @button_grey;
}

.view:selected {
    background-color: @panel_grey;
    color: @black;
}

.entry:selected,
.entry:selected:focused,
.view:selected:focused {
    background-color: @selection_grey;
    color: @black;
}

.entry:selected,
.entry:selected:focused {
    border-color: @selection_grey;
}

/* Frames */

GtkScrolledWindow.frame {
    border-style: solid;
    border-color: @selection_grey;
    border-width: $(thickness)px;
    border-radius: 0;
    padding: $(default_padding)px;
}

/* Combo boxes */

GtkComboBox {
    color: @white;
}

GtkComboBox .separator {
    /* Remove the separator turning it transparent */
    color: alpha(@theme_base_color, 0.0);
}

/* Notebooks */

.notebook {
    background-color: @selection_grey;
    color: @black;
    padding: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
}

.notebook tab {
    background-color: @selection_grey;
}

/* Setting white color to the tab labels using only the previous rule */
/* doesn't work, so we have to set white color to the GtkLabel, and make */
/* sure the buttons get black color in the next two rules: */
.notebook tab GtkLabel {
    color: @white;
}

.notebook tab .button GtkLabel {
    color: @black;
}

.notebook tab:active {
    background-color: @toolbar_grey;
}

.notebook tab .button {
    border-radius: $(toolbutton_padding)px;
}

/* Browse Widgets */

BrowseTabPage {
    background-color: @black;
}

/* Control panel */

SugarSectionView {
    background-color: @white;
    color: @black;
}

SugarSectionView *:insensitive {
    background-color: @white;
}

/* Alert */

SugarAlert {
    background-color: @black;
    color: @white;
}

SugarAlert *:insensitive {
    background-color: @black;
}

.button SugarTimeoutIcon GtkLabel,
.button SugarTimeoutIcon GtkLabel:prelight {
    background-color: @white;
    color: @button_grey;
    border-radius: $(2 * subcell_size)px;
}

.button SugarTimeoutIcon GtkLabel:active {
    background-color: @toolbar_grey;
    color: @white;
}

/* Tray */

SugarHTray * , SugarVTray * { background-color: @toolbar_grey;}

/* Menus and palettes */

SugarPaletteWindowWidget {
    background-color: @black;
    color: @white;
}

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

SugarPaletteWindow SugarGroupBox {
    background-color: @toolbar_grey;
    color: @white;
}

SugarPaletteWindow SugarGroupBox *:insensitive {
    background-color: @toolbar_grey;
}

.menu {
    background-color: @black;
    color: @white;

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*thickness));
    -GtkMenu-horizontal-padding: $thickness;
    -GtkMenu-vertical-padding  : $thickness;
    /* This means the outline of the submenu overlaps with a palette.
     * However in the case of two normal menus, they are next to each other.
     * It is not possible to be smarter about this, because the style comes from
     * the submenu. */
    -GtkMenu-horizontal-offset : 0;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px 0px $(subcell_size)px 0px;
    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

.menu :active {
    background-color: @button_grey;
}

.menuitem {
    padding: $(subcell_size)px $((subcell_size * 3 - font_height) / 2)px;
}

.menuitem:prelight {
    background-color: @button_grey;
}

.menuitem.separator {
    padding: $(subcell_size)px 0px;
    color: @button_grey;
}

SugarPaletteHeader.menuitem {
    padding: 0px $((subcell_size * 3 - font_height) / 2)px;
}

SugarPaletteHeader.menuitem:prelight {
    background-color: @black;
}

SugarPaletteHeaderSeparator.menuitem.separator {
    padding: 0px 0px $(subcell_size)px 0px;
}

.tooltip {
    background-color: @black;
    border-style: solid;
    border-width: $(thickness)px;
    border-color: @button_grey;
}

.tooltip * {
    color: @white;
}

/* Scrollbars */

.scrollbar {
    -GtkRange-slider-width: $subcell_size;
    -GtkRange-trough-border: 0;
    -GtkRange-stepper-size: 0;
    -GtkScrollbar-min-slider-length: $(3*subcell_size);
    -GtkScrollbar-has-forward-stepper: 0;
    -GtkScrollbar-has-backward-stepper: 0;
    -GtkScrollbar-has-secondary-forward-stepper: 0;
    -GtkScrollbar-has-secondary-backward-stepper: 0;
}

.scrollbar.trough {
    background-color: @button_grey;
    border-width: 0px;
}

.scrollbar.slider {
    background-color: @white;
    border-radius: $(2*subcell_size)px;
    border-width: 0px;
}

.scrollbar.slider:active {
    background-color: @text_field_grey;
}

/* Progress bars */

/* Scrollbar padding hack? What was that about? */

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @white;
    border-radius: 10px;
    border-style: solid;
    border-width: 0px;
}

GtkProgressBar.trough {
    background-color: alpha (@black, 0.0);
    border-style: solid;
    border-radius: 10px;
    border-color: @button_grey;
    border-width: 2px;
}

/* Separators */

GtkVSeparator, GtkHSeparator,
.toolbar GtkSeparatorToolItem {
    color: @button_grey;
}

/* Tool buttons */

.toolbar {
padding: 0px;
background-color: @toolbar_grey;
color: @white;
}

.toolbar .button,
SugarPaletteWindowWidget SugarRadioToolButton .button {
    border-color: transparent;
    border-radius: 0px;
    border-style: none;
}

.toolbar GtkToolButton .button,
.toolbar SugarRadioToolButton *,
SugarPaletteWindowWidget SugarRadioToolButton *,
SugarPaletteWindowWidget GtkToolButton .button {
    background-color: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding)px;
}

.toolbar GtkToolButton .button:prelight,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    background-color: @black;
}

.toolbar SugarRadioToolButton *:active,
SugarPaletteWindowWidget SugarRadioToolButton *:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkToolButton .button:active {
    background-color: @transparent;
}

.toolbar GtkToolButton .button:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkScrolledWindow * {
    background-color: @black;
}

.toolbar GtkComboBox .button {
    border-radius: $(2*subcell_size)px;
}

/* Scales */

.scale {
    -GtkScale-slider-length: $scale_slider_width;
    -GtkRange-slider-width: $scale_slider_width;
}

.scale.trough {
    background-color: @button_grey;
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
    margin: $(scale_trough_margin)px 0;
}

.scale.trough.vertical {
    margin: 0 $(scale_trough_margin)px;
}

.scale.trough:focused {
    border-color: @white;
}

.scale.trough.top,
.scale.trough.left {
    background-color: @white;
}

.scale.trough.top:focused,
.scale.trough.left:focused {
    border-color: @selection_grey;
}

.scale.trough {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
}

.scale.trough.vertical {
    border-radius: 0px 0px $(2*subcell_size)px $(2*subcell_size)px;
}

.scale.trough.top {
    border-radius: $(2*subcell_size)px $(2*subcell_size)px 0px 0px;
}

.scale.trough.left {
    border-radius: $(2*subcell_size)px 0px 0px $(2*subcell_size)px;
}

.scale.slider,
.scale.slider:active {
    background-color: transparent;
}

.scale.slider {
    background-image: url("assets/scale-slider.svg");
}

.scale.slider:active {
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton {
    color: @black;
}

GtkCheckButton:prelight {
    background-color: alpha(@theme_base_color, 0.0);
}

.toolbar GtkCheckButton *:active,
SugarPaletteWindowWidget GtkCheckButton *:active {
    color: @theme_base_color;
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.radio:selected,
.radio:selected row:selected,
.radio:selected row:selected:focused {
    background-image: url("assets/radio-selected.svg");
}

.radio:active,
.radio row:selected:active,
.radio row:selected:focused:active {
    background-image: url("assets/radio-active.svg");
}

.radio:active:selected,
.radio:selected row:selected:active,
.radio:selected row:selected:focused:active {
    background-image: url("assets/radio-active-selected.svg");
}

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.check:selected,
.check:selected row:selected,
.check:selected row:selected:focused {
    background-image: url("assets/checkbox-unchecked-selected.svg");
}

.check:active,
.check row:selected:active,
.check row:selected:focused:active {
    background-image: url("assets/checkbox-checked.svg");
}

.check:active:selected,
.check:selected row:selected:active,
.check:selected row:selected:focused:active {
    background-image: url("assets/checkbox-checked-selected.svg");
}

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}

/* GtkImage */

GtkImage {
    background-color: transparent;
}

/* Sugar Frame Window */

SugarFrameWindow {
    background-color: @toolbar_grey;
}

/* Sugar Canvas icon */

SugarCanvasIcon {
    border-color: transparent;
    border-radius: $(4 * thickness)px;
    border-width: 2px;
    border-style: solid;
}

SugarCanvasIcon:prelight {
    border-color: @zoom_views_prelight;
    background-color: @zoom_views_prelight;
}

SugarCanvasIcon:active {
    border-color: @zoom_views_active;
    background-color: @zoom_views_active;
}

/* Text cursor handles */

.cursor-handle.top,
.cursor-handle.bottom {
    background-color: transparent;
    box-shadow: none;
    border-style: none;
    border-image: none;
    border-radius: 0px;
    border-width: 0px;
}

.cursor-handle.bottom {
    background-image: url("assets/cursor-handle-bottom.svg");
}

.cursor-handle.top {
    background-image: url("assets/cursor-handle-top.svg");
}

.cursor-handle.insert-cursor {
    background-image: url("assets/cursor-handle-insert.svg");
}

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

EvView:selected,
ApDocView:selected {
    background-color: @selection_grey;
}

${
import math

def my_floor(num):
    return int(math.floor(num))

def my_ceil(num):
    return int(math.ceil(num))

# Should we set the line width in the engine to 2.25, and draw non pixel aligned lines?
# Are these already the correct sizes for the XO?

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 7      # 7.68

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26

# FIXME this should be calculated with the radio size and the bullet
# size:
scale_trough_margin = 8

scale_slider_width = my_floor(2 * subcell_size + line_width)
thickness = my_ceil(line_width)

icon_small = icon_base * 3
icon_large = icon_base * 5

}

* {
    -sugar-focus-line: @white;

    /* A lot of these will probably need to be changed, but this has to
       be done when the exact sizes are known */
    -GtkWidget-interior-focus: 0;

    /* we have to disable focus border for GtkTreeView, see #1261 */
    -GtkTreeView-interior-focus: 1;
    -GtkTreeView-focus-line-width: 0;

    -GtkTextView-interior-focus: 1;

    -GtkWidget-focus-line-width: 0;  /* Prevents some drawing glitches */
    -GtkEntry-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkWidget-focus-padding: 0;
    /* 0.05 works good for both the sugar and sugar-xo themes */
    -GtkWidget-cursor-aspect-ratio: 0.05;

    -GtkWidget-wide-separators: false;
    -GtkWidget-separator-height: $thickness;
    -GtkWidget-separator-width: $thickness;

    -GtkWidget-scroll-arrow-hlength: $subcell_size;
    -GtkWidget-scroll-arrow-vlength: $subcell_size;

    -GtkRange-activate-slider: 1;

    /* We fake the default border in the theme */
    -GtkButton-default-border: 0 0 0 0;
    -GtkButton-default-outside-border: 0 0 0 0;
    -GtkButton-image-spacing: $subcell_size;

    -GtkEntry-progress-border: $thickness $thickness $thickness $thickness;

    -GtkScrolledWindow-scrollbar-spacing: 0;

    -GtkExpander-expander-size: $font_height;
    -GtkExpander-expander-spacing: 2;

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

    -GtkToolbar-space-size: $(2*subcell_size);
    -GtkToolbar-shadow-type: none;

    -GtkProgressBar-min-horizontal-bar-height: $subcell_size;
    -GtkProgressBar-min-vertical-bar-width: $subcell_size;

    -GtkButtonBox-child-min-height: $(3*subcell_size);
    -GtkButtonBox-child-min-width: $(3*subcell_size);
    -GtkButtonBox-child-internal-pad-x: 0;
    -GtkButtonBox-child-internal-pad-y: 0;

    -GtkCheckButton-indicator-size: $radio_size;
    -GtkCheckButton-indicator-spacing: 3;

    -GtkWidget-text-handle-width: 55px;
    -GtkWidget-text-handle-height: 55px;
}

/* Backgrounds and windows */

GtkWindow {
    background-color: @panel_grey;
    color: @black;
}

GtkDialog {
    background-color: @black;
    color: @white;
}

SugarIntroWindow {
    background-color: @white;
}

SugarIntroWindow {
    color: @black;
}

GtkLabel, GtkLabel:insensitive {
    background-color: transparent;
}

/* Handle this differently? */
.window *:insensitive {
    background-color: @panel_grey;
}

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    /* It would be nicer to just set the inner-border, but that does not work
       for combo boxes ... The combobox ignores it, so set it to 0px
       See http://bugzilla.gnome.org/show_bug.cgi?id=485762 */

    -GtkButton-inner-border: 0 0 0 0;
    padding: $(border)px $(border)px $(border)px $(border)px;

    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

.button:focused,
.button:active {
    border-color: @white;
}

.button:active {
    background-color: @white;
}

.button:active:focused {
    color: @black;
}

/* Spin buttons */

.spinbutton.button {
    border-radius: 0px;
    border-width: 0px;
    border-style: solid;
    background-color: @button_grey;
}

.spinbutton.button:last-child {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
    border-width: 0px 0px 0px $(thickness)px;
    border-style: solid;
    border-color: @selection_grey;
}

.spinbutton.button:active {
    background-color: @black;
}

.spinbutton.button:insensitive {
    background-color: @selection_grey;
}

/* Toggle buttons */

GtkToggleButton.button:active {
    background-color: @white;
    color: @black;
}

/* Entries and views */

.view {
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background-color: @white;
    color: @black;
}

GtkTreeView {
    background-color: @button_grey;
}

GtkTreeView row:nth-child(even) {
    background-color: @row_even;
}
GtkTreeView row:nth-child(odd) {
    background-color: @row_odd;
}

.entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @text_field_grey;
    border-style: solid;
    background-color: @text_field_grey;
    color: @black;
    ${ entry_ythickness = my_ceil(0.2 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    ${ entry_xthickness = my_ceil(0.32 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    padding: $(2 * max(my_ceil((3*subcell_size - font_height - entry_ythickness*2)/2.0),0))px $(2 * max(subcell_size - entry_xthickness, 0))px $(2 * max(my_floor((3*subcell_size - font_height - entry_ythickness*2)/2.0), 0))px $(2 * max(subcell_size - entry_xthickness, 0))px;
}

.entry.progressbar {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    background-color: @selection_grey;
}

.entry:focused {
    background-color: @white;
}

.toolbar .entry:focused {
    border-color: @white;
}

.entry:insensitive, .view:insensitive {
    background-color: @button_grey;
}

.entry:insensitive {
    border-color: @button_grey;
}

.view:selected {
    background-color: @panel_grey;
    color: @black;
}

.entry:selected,
.entry:selected:focused,
.view:selected:focused {
    background-color: @selection_grey;
    color: @black;
}

.entry:selected,
.entry:selected:focused {
    border-color: @selection_grey;
}

/* Frames */

GtkScrolledWindow.frame {
    border-style: solid;
    border-color: @selection_grey;
    border-width: $(thickness)px;
    border-radius: 0;
    padding: $(default_padding)px;
}

/* Combo boxes */

GtkComboBox {
    color: @white;
}

GtkComboBox .separator {
    /* Remove the separator turning it transparent */
    color: alpha(@theme_base_color, 0.0);
}

/* Notebooks */

.notebook {
    background-color: @selection_grey;
    color: @black;
    padding: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
}

.notebook tab {
    background-color: @selection_grey;
}

/* Setting white color to the tab labels using only the previous rule */
/* doesn't work, so we have to set white color to the GtkLabel, and make */
/* sure the buttons get black color in the next two rules: */
.notebook tab GtkLabel {
    color: @white;
}

.notebook tab .button GtkLabel {
    color: @black;
}

.notebook tab:active {
    background-color: @toolbar_grey;
}

.notebook tab .button {
    border-radius: $(toolbutton_padding)px;
}

/* Browse Widgets */

BrowseTabPage {
    background-color: @black;
}

/* Control panel */

SugarSectionView {
    background-color: @white;
    color: @black;
}

SugarSectionView *:insensitive {
    background-color: @white;
}

/* Alert */

SugarAlert {
    background-color: @black;
    color: @white;
}

SugarAlert *:insensitive {
    background-color: @black;
}

.button SugarTimeoutIcon GtkLabel,
.button SugarTimeoutIcon GtkLabel:prelight {
    background-color: @white;
    color: @button_grey;
    border-radius: $(2 * subcell_size)px;
}

.button SugarTimeoutIcon GtkLabel:active {
    background-color: @toolbar_grey;
    color: @white;
}

/* Tray */

SugarHTray * , SugarVTray * { background-color: @toolbar_grey;}

/* Menus and palettes */

SugarPaletteWindowWidget {
    background-color: @black;
    color: @white;
}

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

SugarPaletteWindow SugarGroupBox {
    background-color: @toolbar_grey;
    color: @white;
}

SugarPaletteWindow SugarGroupBox *:insensitive {
    background-color: @toolbar_grey;
}

.menu {
    background-color: @black;
    color: @white;

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*thickness));
    -GtkMenu-horizontal-padding: $thickness;
    -GtkMenu-vertical-padding  : $thickness;
    /* This means the outline of the submenu overlaps with a palette.
     * However in the case of two normal menus, they are next to each other.
     * It is not possible to be smarter about this, because the style comes from
     * the submenu. */
    -GtkMenu-horizontal-offset : 0;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px 0px $(subcell_size)px 0px;
    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

.menu :active {
    background-color: @button_grey;
}

.menuitem {
    padding: $(subcell_size)px $((subcell_size * 3 - font_height) / 2)px;
}

.menuitem:prelight {
    background-color: @button_grey;
}

.menuitem.separator {
    padding: $(subcell_size)px 0px;
    color: @button_grey;
}

SugarPaletteHeader.menuitem {
    padding: 0px $((subcell_size * 3 - font_height) / 2)px;
}

SugarPaletteHeader.menuitem:prelight {
    background-color: @black;
}

SugarPaletteHeaderSeparator.menuitem.separator {
    padding: 0px 0px $(subcell_size)px 0px;
}

.tooltip {
    background-color: @black;
    border-style: solid;
    border-width: $(thickness)px;
    border-color: @button_grey;
}

.tooltip * {
    color: @white;
}

/* Scrollbars */

.scrollbar {
    -GtkRange-slider-width: $subcell_size;
    -GtkRange-trough-border: 0;
    -GtkRange-stepper-size: 0;
    -GtkScrollbar-min-slider-length: $(3*subcell_size);
    -GtkScrollbar-has-forward-stepper: 0;
    -GtkScrollbar-has-backward-stepper: 0;
    -GtkScrollbar-has-secondary-forward-stepper: 0;
    -GtkScrollbar-has-secondary-backward-stepper: 0;
}

.scrollbar.trough {
    background-color: @button_grey;
    border-width: 0px;
}

.scrollbar.slider {
    background-color: @white;
    border-radius: $(2*subcell_size)px;
    border-width: 0px;
}

.scrollbar.slider:active {
    background-color: @text_field_grey;
}

/* Progress bars */

/* Scrollbar padding hack? What was that about? */

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @white;
    border-radius: 10px;
    border-style: solid;
    border-width: 0px;
}

GtkProgressBar.trough {
    background-color: alpha (@black, 0.0);
    border-style: solid;
    border-radius: 10px;
    border-color: @button_grey;
    border-width: 2px;
}

/* Separators */

GtkVSeparator, GtkHSeparator,
.toolbar GtkSeparatorToolItem {
    color: @button_grey;
}

/* Tool buttons */

.toolbar {
padding: 0px;
background-color: @toolbar_grey;
color: @white;
}

.toolbar .button,
SugarPaletteWindowWidget SugarRadioToolButton .button {
    border-color: transparent;
    border-radius: 0px;
    border-style: none;
}

.toolbar GtkToolButton .button,
.toolbar SugarRadioToolButton *,
SugarPaletteWindowWidget SugarRadioToolButton *,
SugarPaletteWindowWidget GtkToolButton .button {
    background-color: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding)px;
}

.toolbar GtkToolButton .button:prelight,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    background-color: @black;
}

.toolbar SugarRadioToolButton *:active,
SugarPaletteWindowWidget SugarRadioToolButton *:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkToolButton .button:active {
    background-color: @transparent;
}

.toolbar GtkToolButton .button:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkScrolledWindow * {
    background-color: @black;
}

.toolbar GtkComboBox .button {
    border-radius: $(2*subcell_size)px;
}

/* Scales */

.scale {
    -GtkScale-slider-length: $scale_slider_width;
    -GtkRange-slider-width: $scale_slider_width;
}

.scale.trough {
    background-color: @button_grey;
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
    margin: $(scale_trough_margin)px 0;
}

.scale.trough.vertical {
    margin: 0 $(scale_trough_margin)px;
}

.scale.trough:focused {
    border-color: @white;
}

.scale.trough.top,
.scale.trough.left {
    background-color: @white;
}

.scale.trough.top:focused,
.scale.trough.left:focused {
    border-color: @selection_grey;
}

.scale.trough {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
}

.scale.trough.vertical {
    border-radius: 0px 0px $(2*subcell_size)px $(2*subcell_size)px;
}

.scale.trough.top {
    border-radius: $(2*subcell_size)px $(2*subcell_size)px 0px 0px;
}

.scale.trough.left {
    border-radius: $(2*subcell_size)px 0px 0px $(2*subcell_size)px;
}

.scale.slider,
.scale.slider:active {
    background-color: transparent;
}

.scale.slider {
    background-image: url("assets/scale-slider.svg");
}

.scale.slider:active {
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton {
    color: @black;
}

GtkCheckButton:prelight {
    background-color: alpha(@theme_base_color, 0.0);
}

.toolbar GtkCheckButton *:active,
SugarPaletteWindowWidget GtkCheckButton *:active {
    color: @theme_base_color;
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.radio:selected,
.radio:selected row:selected,
.radio:selected row:selected:focused {
    background-image: url("assets/radio-selected.svg");
}

.radio:active,
.radio row:selected:active,
.radio row:selected:focused:active {
    background-image: url("assets/radio-active.svg");
}

.radio:active:selected,
.radio:selected row:selected:active,
.radio:selected row:selected:focused:active {
    background-image: url("assets/radio-active-selected.svg");
}

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.check:selected,
.check:selected row:selected,
.check:selected row:selected:focused {
    background-image: url("assets/checkbox-unchecked-selected.svg");
}

.check:active,
.check row:selected:active,
.check row:selected:focused:active {
    background-image: url("assets/checkbox-checked.svg");
}

.check:active:selected,
.check:selected row:selected:active,
.check:selected row:selected:focused:active {
    background-image: url("assets/checkbox-checked-selected.svg");
}

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}

/* GtkImage */

GtkImage {
    background-color: transparent;
}

/* Sugar Frame Window */

SugarFrameWindow {
    background-color: @toolbar_grey;
}

/* Sugar Canvas icon */

SugarCanvasIcon {
    border-color: transparent;
    border-radius: $(4 * thickness)px;
    border-width: 2px;
    border-style: solid;
}

SugarCanvasIcon:prelight {
    border-color: @zoom_views_prelight;
    background-color: @zoom_views_prelight;
}

SugarCanvasIcon:active {
    border-color: @zoom_views_active;
    background-color: @zoom_views_active;
}

/* Text cursor handles */

.cursor-handle.top,
.cursor-handle.bottom {
    background-color: transparent;
    box-shadow: none;
    border-style: none;
    border-image: none;
    border-radius: 0px;
    border-width: 0px;
}

.cursor-handle.bottom {
    background-image: url("assets/cursor-handle-bottom.svg");
}

.cursor-handle.top {
    background-image: url("assets/cursor-handle-top.svg");
}

.cursor-handle.insert-cursor {
    background-image: url("assets/cursor-handle-insert.svg");
}

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

EvView:selected,
ApDocView:selected {
    background-color: @selection_grey;
}