<commit_before>#include <tamer/tamer.hh>
#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

namespace tamer {
namespace {

class driver_tamer : public driver { public:

    driver_tamer();
    ~driver_tamer();

    virtual void at_fd(int fd, int action, const event<> &e);
    virtual void at_time(const timeval &expiry, const event<> &e);
    virtual void at_asap(const event<> &e);
    virtual void kill_fd(int fd);

    virtual bool empty();
    virtual void once();
    virtual void loop();

  private:

    struct ttimer {
	union {
	    int schedpos;
	    ttimer *next;
	} u;
	timeval expiry;
	event<> trigger;
	ttimer(int schedpos_, const timeval &expiry_, const event<> &trigger_) : expiry(expiry_), trigger(trigger_) { u.schedpos = schedpos_; }
    };

    struct ttimer_group {
	ttimer_group *next;
	ttimer t[1];
    };

    struct tfd {
	int fd : 30;
	unsigned action : 2;
	event<> e;
	tfd *next;
    };

    struct tfd_group {
	tfd_group *next;
	tfd t[1];
    };

    ttimer **_t;
    int _nt;

    tfd *_fd;
    int _nfds;
    fd_set _fdset[2];

    tamerpriv::debuffer<event<> > _asap;

    int _tcap;
    ttimer_group *_tgroup;
    ttimer *_tfree;

    int _fdcap;
    tfd_group *_fdgroup;
    tfd *_fdfree;
    rendezvous<> _fdcancelr;

    void expand_timers();
    void check_timers() const;
    void timer_reheapify_from(int pos, ttimer *t, bool will_delete);
    void expand_fds();

};

driver_tamer::driver_tamer()
    : _t(0), _nt(0),
      _fd(0), _nfds(0),
      _tcap(0), _tgroup(0), _tfree(0),
      _fdcap(0), _fdgroup(0), _fdfree(0)
{
    expand_timers();
    FD_ZERO(&_fdset[fdread]);
    FD_ZERO(&_fdset[fdwrite]);
    set_now();
}

driver_tamer::~driver_tamer()
{
    // destroy all active timers
    for (int i = 0; i < _nt; i++)
	_t[i]->~ttimer();

    // free timer groups
    while (_tgroup) {
	ttimer_group *next = _tgroup->next;
	delete[] reinterpret_cast<unsigned char *>(_tgroup);
	_tgroup = next;
    }
    delete[] _t;

    // destroy all active file descriptors
    while (_fd) {
	_fd->e.~event();
	_fd = _fd->next;
    }

    // free file descriptor groups
    while (_fdgroup) {
	tfd_group *next = _fdgroup->next;
	delete[] reinterpret_cast<unsigned char *>(_fdgroup);
	_fdgroup = next;
    }
}

void driver_tamer::expand_timers()
{
    int ncap = (_tcap ? _tcap * 2 : 16);

    ttimer_group *ngroup = reinterpret_cast<ttimer_group *>(new unsigned char[sizeof(ttimer_group) + sizeof(ttimer) * (ncap - 1)]);
    ngroup->next = _tgroup;
    _tgroup = ngroup;
    for (int i = 0; i < ncap; i++) {
	ngroup->t[i].u.next = _tfree;
	_tfree = &ngroup->t[i];
    }

    ttimer **t = new ttimer *[ncap];
    memcpy(t, _t, sizeof(ttimer *) * _nt);
    delete[] _t;
    _t = t;
    _tcap = ncap;
}

void driver_tamer::timer_reheapify_from(int pos, ttimer *t, bool /*will_delete*/)
{
    int npos;
    while (pos > 0
	   && (npos = (pos-1) >> 1, timercmp(&_t[npos]->expiry, &t->expiry, >))) {
	_t[pos] = _t[npos];
	_t[npos]->u.schedpos = pos;
	pos = npos;
    }

    while (1) {
	ttimer *smallest = t;
	npos = 2*pos + 1;
	if (npos < _nt && !timercmp(&_t[npos]->expiry, &smallest->expiry, >))
	    smallest = _t[npos];
	if (npos + 1 < _nt && !timercmp(&_t[npos+1]->expiry, &smallest->expiry, >))
	    smallest = _t[npos+1], npos++;

	smallest->u.schedpos = pos;
	_t[pos] = smallest;

	if (smallest == t)
	    break;

	pos = npos;
    }
#if 0
    if (_t + 1 < tend || !will_delete)
	_timer_expiry = tbegin[0]->expiry;
    else
	_timer_expiry = Timestamp();
#endif
}

#if 0
void driver_tamer::check_timers() const
{
    fprintf(stderr, "---");
    for (int k = 0; k < _nt; k++)
	fprintf(stderr, " %p/%d.%06d", _t[k], _t[k]->expiry.tv_sec, _t[k]->expiry.tv_usec);
    fprintf(stderr, "\n");

    for (int i = 0; i < _nt / 2; i++)
	for (int j = 2*i + 1; j < 2*i + 3; j++)
	    if (j < _nt && timercmp(&_t[i]->expiry, &_t[j]->expiry, >)) {
		fprintf(stderr, "***");
		for (int k = 0; k < _nt; k++)
		    fprintf(stderr, (k == i || k == j ? " **%d.%06d**" : " %d.%06d"), _t[k]->expiry.tv_sec, _t[k]->expiry.tv_usec);
		fprintf(stderr, "\n");
		assert(0);
	    }
}
#endif

void driver_tamer::expand_fds()
{
    int ncap = (_fdcap ? _fdcap * 2 : 16);

    tfd_group *ngroup = reinterpret_cast<tfd_group *>(new unsigned char[sizeof(tfd_group) + sizeof(tfd) * (ncap - 1)]);
    ngroup->next = _fdgroup;
    _fdgroup = ngroup;
    for (int i = 0; i < ncap; i++) {
	ngroup->t[i].next = _fdfree;
	_fdfree = &ngroup->t[i];
    }
}

void driver_tamer::at_fd(int fd, int action, const event<> &trigger)
{
    if (!_fdfree)
	expand_fds();
    if (fd >= FD_SETSIZE)
	throw tamer::tamer_error("file descriptor too large");
    if (trigger) {
	tfd *t = _fdfree;
	_fdfree = t->next;
	t->next = _fd;
	_fd = t;

	t->fd = fd;
	t->action = action;
	(void) new(static_cast<void *>(&t->e)) event<>(trigger);

	FD_SET(fd, &_fdset[action]);
	if (fd >= _nfds)
	    _nfds = fd + 1;
	if (action <= fdwrite)
	    t->e.at_cancel(make_event(_fdcancelr));
    }
}

void driver_tamer::kill_fd(int fd)
{
    assert(fd >= 0);
    if (fd < _nfds && (FD_ISSET(fd, &_fdset[fdread]) || FD_ISSET(fd, &_fdset[fdwrite]))) {
	FD_CLR(fd, &_fdset[fdread]);
	FD_CLR(fd, &_fdset[fdwrite]);
	tfd **pprev = &_fd, *t;
	while ((t = *pprev))
	    if (t->fd == fd) {
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else
		pprev = &t->next;
    }
}

void driver_tamer::at_time(const timeval &expiry, const event<> &e)
{
    if (!_tfree)
	expand_timers();
    ttimer *t = _tfree;
    _tfree = t->u.next;
    (void) new(static_cast<void *>(t)) ttimer(_nt, expiry, e);
    _t[_nt++] = 0;
    timer_reheapify_from(_nt - 1, t, false);
}

void driver_tamer::at_asap(const event<> &e)
{
    _asap.push_back(e);
}

bool driver_tamer::empty()
{
    if (_nt != 0 || _nfds != 0 || _asap.size() != 0
	|| sig_any_active || tamerpriv::abstract_rendezvous::unblocked)
	return false;
    return true;
}

void driver_tamer::once()
{
    // get rid of initial cancelled timers
    ttimer *t;
    while (_nt > 0 && (t = _t[0], !t->trigger)) {
	timer_reheapify_from(0, _t[_nt - 1], true);
	_nt--;
	t->~ttimer();
	t->u.next = _tfree;
	_tfree = t;
    }

    // determine timeout
    struct timeval to, *toptr;
    if (_asap.size()
	|| (_nt > 0 && !timercmp(&_t[0]->expiry, &now, >))
	|| sig_any_active) {
	timerclear(&to);
	toptr = &to;
    } else if (_nt == 0)
	toptr = 0;
    else {
	timersub(&_t[0]->expiry, &now, &to);
	toptr = &to;
    }

    // get rid of canceled descriptors, if any
    if (_fdcancelr.nready()) {
	while (_fdcancelr.join())
	    /* nada */;
	FD_ZERO(&_fdset[fdread]);
	FD_ZERO(&_fdset[fdwrite]);
	_nfds = 0;
	tfd **pprev = &_fd, *t;
	while ((t = *pprev))
	    if (!t->e) {
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else {
		FD_SET(t->fd, &_fdset[t->action]);
		if (t->fd >= _nfds)
		    _nfds = t->fd + 1;
		pprev = &t->next;
	    }
    }

    // select!
    fd_set fds[2];
    fds[fdread] = _fdset[fdread];
    fds[fdwrite] = _fdset[fdwrite];
    int nfds = _nfds;
    if (sig_pipe[0] >= 0) {
	FD_SET(sig_pipe[0], &fds[fdread]);
	if (sig_pipe[0] > nfds)
	    nfds = sig_pipe[0] + 1;
    }
    nfds = select(nfds, &fds[fdread], &fds[fdwrite], 0, toptr);

    // run signals
    if (sig_any_active)
	dispatch_signals();

    // run asaps
    while (event<> *e = _asap.front()) {
	e->trigger();
	_asap.pop_front();
    }

    // run file descriptors
    if (nfds > 0) {
	tfd **pprev = &_fd, *t;
	while ((t = *pprev))
	    if (t->action <= fdwrite && FD_ISSET(t->fd, &fds[t->action])) {
		FD_CLR(t->fd, &_fdset[t->action]);
		t->e.trigger();
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else if (!t->e) {
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else
		pprev = &t->next;
    }

    // run the timers that worked
    set_now();
    while (_nt > 0 && (t = _t[0], !timercmp(&t->expiry, &now, >))) {
	timer_reheapify_from(0, _t[_nt - 1], true);
	_nt--;
	t->trigger.trigger();
	t->~ttimer();
	t->u.next = _tfree;
	_tfree = t;
    }

    // run active closures
    while (tamerpriv::abstract_rendezvous *r = tamerpriv::abstract_rendezvous::unblocked)
	r->run();
}

#if 0
void driver_tamer::print_fds()
{
    tfd *t = _fd;
    while (t) {
	fprintf(stderr, "%d.%d ", t->fd, t->action);
	t = t->next;
    }
    if (_fd)
	fprintf(stderr, "\n");
}
#endif

void driver_tamer::loop()
{
    while (1)
	once();
}

}

driver *driver::make_tamer()
{
    return new driver_tamer;
}

}
<commit_msg>duh: allocate enough ttimer*s<commit_after>#include <tamer/tamer.hh>
#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

namespace tamer {
namespace {

class driver_tamer : public driver { public:

    driver_tamer();
    ~driver_tamer();

    virtual void at_fd(int fd, int action, const event<> &e);
    virtual void at_time(const timeval &expiry, const event<> &e);
    virtual void at_asap(const event<> &e);
    virtual void kill_fd(int fd);

    virtual bool empty();
    virtual void once();
    virtual void loop();

  private:

    struct ttimer {
	union {
	    int schedpos;
	    ttimer *next;
	} u;
	timeval expiry;
	event<> trigger;
	ttimer(int schedpos_, const timeval &expiry_, const event<> &trigger_) : expiry(expiry_), trigger(trigger_) { u.schedpos = schedpos_; }
    };

    struct ttimer_group {
	ttimer_group *next;
	ttimer t[1];
    };

    struct tfd {
	int fd : 30;
	unsigned action : 2;
	event<> e;
	tfd *next;
    };

    struct tfd_group {
	tfd_group *next;
	tfd t[1];
    };

    ttimer **_t;
    int _nt;

    tfd *_fd;
    int _nfds;
    fd_set _fdset[2];

    tamerpriv::debuffer<event<> > _asap;

    int _tcap;
    ttimer_group *_tgroup;
    ttimer *_tfree;

    int _fdcap;
    tfd_group *_fdgroup;
    tfd *_fdfree;
    rendezvous<> _fdcancelr;

    void expand_timers();
    void check_timers() const;
    void timer_reheapify_from(int pos, ttimer *t, bool will_delete);
    void expand_fds();

};

driver_tamer::driver_tamer()
    : _t(0), _nt(0),
      _fd(0), _nfds(0),
      _tcap(0), _tgroup(0), _tfree(0),
      _fdcap(0), _fdgroup(0), _fdfree(0)
{
    expand_timers();
    FD_ZERO(&_fdset[fdread]);
    FD_ZERO(&_fdset[fdwrite]);
    set_now();
}

driver_tamer::~driver_tamer()
{
    // destroy all active timers
    for (int i = 0; i < _nt; i++)
	_t[i]->~ttimer();

    // free timer groups
    while (_tgroup) {
	ttimer_group *next = _tgroup->next;
	delete[] reinterpret_cast<unsigned char *>(_tgroup);
	_tgroup = next;
    }
    delete[] _t;

    // destroy all active file descriptors
    while (_fd) {
	_fd->e.~event();
	_fd = _fd->next;
    }

    // free file descriptor groups
    while (_fdgroup) {
	tfd_group *next = _fdgroup->next;
	delete[] reinterpret_cast<unsigned char *>(_fdgroup);
	_fdgroup = next;
    }
}

void driver_tamer::expand_timers()
{
    int ncap = (_tcap ? _tcap * 2 : 16);

    ttimer_group *ngroup = reinterpret_cast<ttimer_group *>(new unsigned char[sizeof(ttimer_group) + sizeof(ttimer) * (ncap - 1)]);
    ngroup->next = _tgroup;
    _tgroup = ngroup;
    for (int i = 0; i < ncap; i++) {
	ngroup->t[i].u.next = _tfree;
	_tfree = &ngroup->t[i];
    }

    ttimer **t = new ttimer *[_tcap + ncap];
    memcpy(t, _t, sizeof(ttimer *) * _nt);
    delete[] _t;
    _t = t;
    _tcap = ncap;
}

void driver_tamer::timer_reheapify_from(int pos, ttimer *t, bool /*will_delete*/)
{
    int npos;
    while (pos > 0
	   && (npos = (pos-1) >> 1, timercmp(&_t[npos]->expiry, &t->expiry, >))) {
	_t[pos] = _t[npos];
	_t[npos]->u.schedpos = pos;
	pos = npos;
    }

    while (1) {
	ttimer *smallest = t;
	npos = 2*pos + 1;
	if (npos < _nt && !timercmp(&_t[npos]->expiry, &smallest->expiry, >))
	    smallest = _t[npos];
	if (npos + 1 < _nt && !timercmp(&_t[npos+1]->expiry, &smallest->expiry, >))
	    smallest = _t[npos+1], npos++;

	smallest->u.schedpos = pos;
	_t[pos] = smallest;

	if (smallest == t)
	    break;

	pos = npos;
    }
#if 0
    if (_t + 1 < tend || !will_delete)
	_timer_expiry = tbegin[0]->expiry;
    else
	_timer_expiry = Timestamp();
#endif
}

#if 0
void driver_tamer::check_timers() const
{
    fprintf(stderr, "---");
    for (int k = 0; k < _nt; k++)
	fprintf(stderr, " %p/%d.%06d", _t[k], _t[k]->expiry.tv_sec, _t[k]->expiry.tv_usec);
    fprintf(stderr, "\n");

    for (int i = 0; i < _nt / 2; i++)
	for (int j = 2*i + 1; j < 2*i + 3; j++)
	    if (j < _nt && timercmp(&_t[i]->expiry, &_t[j]->expiry, >)) {
		fprintf(stderr, "***");
		for (int k = 0; k < _nt; k++)
		    fprintf(stderr, (k == i || k == j ? " **%d.%06d**" : " %d.%06d"), _t[k]->expiry.tv_sec, _t[k]->expiry.tv_usec);
		fprintf(stderr, "\n");
		assert(0);
	    }
}
#endif

void driver_tamer::expand_fds()
{
    int ncap = (_fdcap ? _fdcap * 2 : 16);

    tfd_group *ngroup = reinterpret_cast<tfd_group *>(new unsigned char[sizeof(tfd_group) + sizeof(tfd) * (ncap - 1)]);
    ngroup->next = _fdgroup;
    _fdgroup = ngroup;
    for (int i = 0; i < ncap; i++) {
	ngroup->t[i].next = _fdfree;
	_fdfree = &ngroup->t[i];
    }
}

void driver_tamer::at_fd(int fd, int action, const event<> &trigger)
{
    if (!_fdfree)
	expand_fds();
    if (fd >= FD_SETSIZE)
	throw tamer::tamer_error("file descriptor too large");
    if (trigger) {
	tfd *t = _fdfree;
	_fdfree = t->next;
	t->next = _fd;
	_fd = t;

	t->fd = fd;
	t->action = action;
	(void) new(static_cast<void *>(&t->e)) event<>(trigger);

	FD_SET(fd, &_fdset[action]);
	if (fd >= _nfds)
	    _nfds = fd + 1;
	if (action <= fdwrite)
	    t->e.at_cancel(make_event(_fdcancelr));
    }
}

void driver_tamer::kill_fd(int fd)
{
    assert(fd >= 0);
    if (fd < _nfds && (FD_ISSET(fd, &_fdset[fdread]) || FD_ISSET(fd, &_fdset[fdwrite]))) {
	FD_CLR(fd, &_fdset[fdread]);
	FD_CLR(fd, &_fdset[fdwrite]);
	tfd **pprev = &_fd, *t;
	while ((t = *pprev))
	    if (t->fd == fd) {
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else
		pprev = &t->next;
    }
}

void driver_tamer::at_time(const timeval &expiry, const event<> &e)
{
    if (!_tfree)
	expand_timers();
    ttimer *t = _tfree;
    _tfree = t->u.next;
    (void) new(static_cast<void *>(t)) ttimer(_nt, expiry, e);
    _t[_nt++] = 0;
    timer_reheapify_from(_nt - 1, t, false);
}

void driver_tamer::at_asap(const event<> &e)
{
    _asap.push_back(e);
}

bool driver_tamer::empty()
{
    if (_nt != 0 || _nfds != 0 || _asap.size() != 0
	|| sig_any_active || tamerpriv::abstract_rendezvous::unblocked)
	return false;
    return true;
}

void driver_tamer::once()
{
    // get rid of initial cancelled timers
    ttimer *t;
    while (_nt > 0 && (t = _t[0], !t->trigger)) {
	timer_reheapify_from(0, _t[_nt - 1], true);
	_nt--;
	t->~ttimer();
	t->u.next = _tfree;
	_tfree = t;
    }

    // determine timeout
    struct timeval to, *toptr;
    if (_asap.size()
	|| (_nt > 0 && !timercmp(&_t[0]->expiry, &now, >))
	|| sig_any_active) {
	timerclear(&to);
	toptr = &to;
    } else if (_nt == 0)
	toptr = 0;
    else {
	timersub(&_t[0]->expiry, &now, &to);
	toptr = &to;
    }

    // get rid of canceled descriptors, if any
    if (_fdcancelr.nready()) {
	while (_fdcancelr.join())
	    /* nada */;
	FD_ZERO(&_fdset[fdread]);
	FD_ZERO(&_fdset[fdwrite]);
	_nfds = 0;
	tfd **pprev = &_fd, *t;
	while ((t = *pprev))
	    if (!t->e) {
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else {
		FD_SET(t->fd, &_fdset[t->action]);
		if (t->fd >= _nfds)
		    _nfds = t->fd + 1;
		pprev = &t->next;
	    }
    }

    // select!
    fd_set fds[2];
    fds[fdread] = _fdset[fdread];
    fds[fdwrite] = _fdset[fdwrite];
    int nfds = _nfds;
    if (sig_pipe[0] >= 0) {
	FD_SET(sig_pipe[0], &fds[fdread]);
	if (sig_pipe[0] > nfds)
	    nfds = sig_pipe[0] + 1;
    }
    nfds = select(nfds, &fds[fdread], &fds[fdwrite], 0, toptr);

    // run signals
    if (sig_any_active)
	dispatch_signals();

    // run asaps
    while (event<> *e = _asap.front()) {
	e->trigger();
	_asap.pop_front();
    }

    // run file descriptors
    if (nfds > 0) {
	tfd **pprev = &_fd, *t;
	while ((t = *pprev))
	    if (t->action <= fdwrite && FD_ISSET(t->fd, &fds[t->action])) {
		FD_CLR(t->fd, &_fdset[t->action]);
		t->e.trigger();
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else if (!t->e) {
		t->e.~event();
		*pprev = t->next;
		t->next = _fdfree;
		_fdfree = t;
	    } else
		pprev = &t->next;
    }

    // run the timers that worked
    set_now();
    while (_nt > 0 && (t = _t[0], !timercmp(&t->expiry, &now, >))) {
	timer_reheapify_from(0, _t[_nt - 1], true);
	_nt--;
	t->trigger.trigger();
	t->~ttimer();
	t->u.next = _tfree;
	_tfree = t;
    }

    // run active closures
    while (tamerpriv::abstract_rendezvous *r = tamerpriv::abstract_rendezvous::unblocked)
	r->run();
}

#if 0
void driver_tamer::print_fds()
{
    tfd *t = _fd;
    while (t) {
	fprintf(stderr, "%d.%d ", t->fd, t->action);
	t = t->next;
    }
    if (_fd)
	fprintf(stderr, "\n");
}
#endif

void driver_tamer::loop()
{
    while (1)
	once();
}

}

driver *driver::make_tamer()
{
    return new driver_tamer;
}

}
<|endoftext|>


<commit_before>#include "tangram.h"
#include "platform_osx.h"
#include "data/clientGeoJsonSource.h"
#include "debug/textDisplay.h"
#include <cmath>
#include <memory>
#include <signal.h>
#include <stdlib.h>

// Forward declaration
void init_main_window(bool recreate);

std::string sceneFile = "scene.yaml";

GLFWwindow* main_window = nullptr;
Tangram::Map* map = nullptr;
int width = 800;
int height = 600;
float density = 1.0;
bool recreate_context = false;
float pixel_scale = 1.0;

// Input handling
// ==============

const double double_tap_time = 0.5; // seconds
const double scroll_span_multiplier = 0.05; // scaling for zoom and rotation
const double scroll_distance_multiplier = 5.0; // scaling for shove
const double single_tap_time = 0.25; //seconds (to avoid a long press being considered as a tap)

bool was_panning = false;
double last_time_released = -double_tap_time; // First click should never trigger a double tap
double last_time_pressed = 0.0;
double last_time_moved = 0.0;
double last_x_down = 0.0;
double last_y_down = 0.0;
double last_x_velocity = 0.0;
double last_y_velocity = 0.0;

using namespace Tangram;

std::shared_ptr<ClientGeoJsonSource> data_source;
LngLat last_point;

template<typename T>
static constexpr T clamp(T val, T min, T max) {
    return val > max ? max : val < min ? min : val;
}

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {

    if (button != GLFW_MOUSE_BUTTON_1) {
        return; // This event is for a mouse button that we don't care about
    }

    double x, y;
    glfwGetCursorPos(window, &x, &y);
    x *= density;
    y *= density;
    double time = glfwGetTime();

    if (was_panning && action == GLFW_RELEASE) {
        was_panning = false;
        auto vx = clamp(last_x_velocity, -2000.0, 2000.0);
        auto vy = clamp(last_y_velocity, -2000.0, 2000.0);
        map->handleFlingGesture(x, y, vx, vy);
        return; // Clicks with movement don't count as taps, so stop here
    }

    if (action == GLFW_PRESS) {
        map->handlePanGesture(0.0f, 0.0f, 0.0f, 0.0f);
        last_x_down = x;
        last_y_down = y;
        last_time_pressed = time;
        return;
    }

    if ((time - last_time_released) < double_tap_time) {
        // Double tap recognized
        LngLat p;
        map->screenPositionToLngLat(x, y, &p.longitude, &p.latitude);
        map->setPositionEased(p.longitude, p.latitude, 1.f);

        logMsg("pick feature\n");
        map->clearDataSource(*data_source, true, true);

        auto picks = map->pickFeaturesAt(x, y);
        std::string name;
        logMsg("picked %d features\n", picks.size());
        for (const auto& it : picks) {
            if (it.properties->getString("name", name)) {
                logMsg(" - %f\t %s\n", it.distance, name.c_str());
            }
        }
    } else if ((time - last_time_pressed) < single_tap_time) {
        // Single tap recognized
        LngLat p1;
        map->screenPositionToLngLat(x, y, &p1.longitude, &p1.latitude);

        if (!(last_point == LngLat{0, 0})) {
            LngLat p2 = last_point;
            logMsg("add line %f %f - %f %f\n",
                   p1.longitude, p1.latitude,
                   p2.longitude, p2.latitude);

            // data_source->addLine(Properties{{"type", "line" }}, {p1, p2});
            // data_source->addPoint(Properties{{"type", "point" }}, p2);
            Properties prop1;
            prop1.set("type", "line");
            data_source->addLine(prop1, {p1, p2});
            Properties prop2;
            prop2.set("type", "point");
            data_source->addPoint(prop2, p2);
        }
        last_point = p1;

        // Tangram::clearDataSource(*data_source, false, true);
        // This updates the tiles (maybe we need a recalcTiles())
        requestRender();
    }

    last_time_released = time;

}

void cursor_pos_callback(GLFWwindow* window, double x, double y) {

    x *= density;
    y *= density;

    int action = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1);
    double time = glfwGetTime();

    if (action == GLFW_PRESS) {

        if (was_panning) {
            map->handlePanGesture(last_x_down, last_y_down, x, y);
        }

        was_panning = true;
        last_x_velocity = (x - last_x_down) / (time - last_time_moved);
        last_y_velocity = (y - last_y_down) / (time - last_time_moved);
        last_x_down = x;
        last_y_down = y;

    }

    last_time_moved = time;

}

void scroll_callback(GLFWwindow* window, double scrollx, double scrolly) {

    double x, y;
    glfwGetCursorPos(window, &x, &y);
    x *= density;
    y *= density;

    bool rotating = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;
    bool shoving = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;

    if (shoving) {
        map->handleShoveGesture(scroll_distance_multiplier * scrolly);
    } else if (rotating) {
        map->handleRotateGesture(x, y, scroll_span_multiplier * scrolly);
    } else {
        map->handlePinchGesture(x, y, 1.0 + scroll_span_multiplier * scrolly, 0.f);
    }

}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {

    if (action == GLFW_PRESS) {
        switch (key) {
            case GLFW_KEY_1:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::freeze_tiles);
                break;
            case GLFW_KEY_2:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::proxy_colors);
                break;
            case GLFW_KEY_3:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tile_bounds);
                break;
            case GLFW_KEY_4:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tile_infos);
                break;
            case GLFW_KEY_5:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::labels);
                break;
            case GLFW_KEY_6:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::draw_all_labels);
                break;
            case GLFW_KEY_7:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tangram_infos);
                break;
            case GLFW_KEY_8:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tangram_stats);
                break;
            case GLFW_KEY_BACKSPACE:
                recreate_context = true;
                break;
            case GLFW_KEY_R:
                map->loadSceneAsync(sceneFile.c_str());
                break;
            case GLFW_KEY_Z:
                map->setZoomEased(map->getZoom() + 1.f, 1.5f);
                break;
            case GLFW_KEY_N:
                map->setRotationEased(0.f, 1.f);
                break;
            case GLFW_KEY_S:
                if (pixel_scale == 1.0) {
                    pixel_scale = 2.0;
                } else if (pixel_scale == 2.0) {
                    pixel_scale = 0.75;
                } else {
                    pixel_scale = 1.0;
                }
                map->loadSceneAsync(sceneFile.c_str());
                map->setPixelScale(pixel_scale);
                break;
            case GLFW_KEY_P:
                map->queueSceneUpdate("cameras", "{ main_camera: { type: perspective } }");
                map->applySceneUpdates();
                break;
            case GLFW_KEY_I:
                map->queueSceneUpdate("cameras", "{ main_camera: { type: isometric } }");
                map->applySceneUpdates();
                break;
            case GLFW_KEY_G:
                static bool geoJSON = false;
                if (!geoJSON) {
                    LOGS("Switching to GeoJSON data source");
                    map->queueSceneUpdate("sources.osm.type", "GeoJSON");
                    map->queueSceneUpdate("sources.osm.url", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.json");
                } else {
                    LOGS("Switching to MVT data source");
                    map->queueSceneUpdate("sources.osm.type", "MVT");
                    map->queueSceneUpdate("sources.osm.url", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.mvt");
                }
                geoJSON = !geoJSON;
                map->applySceneUpdates();
                break;
            case GLFW_KEY_ESCAPE:
                glfwSetWindowShouldClose(main_window, true);
                break;
            case GLFW_KEY_F1:
                map->setPosition(-74.00976419448854, 40.70532700869127);
                map->setZoom(16);
                break;
            case GLFW_KEY_F2:
                map->setPosition(8.82, 53.08);
                map->setZoom(14);
                break;
        default:
                break;
        }
    }
}

void drop_callback(GLFWwindow* window, int count, const char** paths) {

    sceneFile = std::string(paths[0]);
    map->loadSceneAsync(sceneFile.c_str());

}

// Window handling
// ===============

void framebuffer_size_callback(GLFWwindow* window, int fWidth, int fHeight) {

    int wWidth = 0, wHeight = 0;
    glfwGetWindowSize(main_window, &wWidth, &wHeight);
    float new_density = (float)fWidth / (float)wWidth;
    if (new_density != density) {
        recreate_context = true;
        density = new_density;
    }
    map->setPixelScale(density);
    map->resize(fWidth, fHeight);

}

void init_main_window(bool recreate) {

    // Setup tangram
    if (!map) {
        map = new Tangram::Map();
        map->loadSceneAsync(sceneFile.c_str(), true);
    }

    if (!recreate) {
        // Destroy old window
        if (main_window != nullptr) {
            glfwDestroyWindow(main_window);
        }

        // Create a windowed mode window and its OpenGL context
        glfwWindowHint(GLFW_SAMPLES, 2);
        main_window = glfwCreateWindow(width, height, "Tangram ES", NULL, NULL);
        if (!main_window) {
            glfwTerminate();
        }

        // Make the main_window's context current
        glfwMakeContextCurrent(main_window);

        // Set input callbacks
        glfwSetFramebufferSizeCallback(main_window, framebuffer_size_callback);
        glfwSetMouseButtonCallback(main_window, mouse_button_callback);
        glfwSetCursorPosCallback(main_window, cursor_pos_callback);
        glfwSetScrollCallback(main_window, scroll_callback);
        glfwSetKeyCallback(main_window, key_callback);
        glfwSetDropCallback(main_window, drop_callback);
    }

    // Setup graphics
    map->setupGL();
    int fWidth = 0, fHeight = 0;
    glfwGetFramebufferSize(main_window, &fWidth, &fHeight);
    framebuffer_size_callback(main_window, fWidth, fHeight);

    data_source = std::make_shared<ClientGeoJsonSource>("touch", "");
    map->addDataSource(data_source);

}

// Main program
// ============

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

    static bool keepRunning = true;

    // Give it a chance to shutdown cleanly on CTRL-C
    signal(SIGINT, [](int) {
            if (keepRunning) {
                logMsg("shutdown\n");
                keepRunning = false;
                glfwPostEmptyEvent();
            } else {
                logMsg("killed!\n");
                exit(1);
            }});

    int argi = 0;
    while (++argi < argc) {
        if (strcmp(argv[argi - 1], "-f") == 0) {
            sceneFile = std::string(argv[argi]);
            logMsg("File from command line: %s\n", argv[argi]);
            break;
        }
    }

    // Initialize networking
    NSurlInit();

    // Initialize the windowing library
    if (!glfwInit()) {
        return -1;
    }

    init_main_window(false);

    double lastTime = glfwGetTime();

    // Loop until the user closes the window
    while (keepRunning && !glfwWindowShouldClose(main_window)) {

        double currentTime = glfwGetTime();
        double delta = currentTime - lastTime;
        lastTime = currentTime;

        // Render
        map->update(delta);
        map->render();

        // Swap front and back buffers
        glfwSwapBuffers(main_window);

        // Poll for and process events
        if (isContinuousRendering()) {
            glfwPollEvents();
        } else {
            glfwWaitEvents();
        }

        if (recreate_context) {
            logMsg("recreate context\n");
             // Simulate GL context loss
            init_main_window(true);
            recreate_context = false;
        }

    }

    finishUrlRequests();

    if (map) {
        delete map;
        map = nullptr;
    }

    glfwTerminate();
    return 0;
}
<commit_msg>Marker testing<commit_after>#include "tangram.h"
#include "platform_osx.h"
#include "data/clientGeoJsonSource.h"
#include "debug/textDisplay.h"
#include <cmath>
#include <memory>
#include <signal.h>
#include <stdlib.h>

// Forward declaration
void init_main_window(bool recreate);

std::string sceneFile = "scene.yaml";

std::string markerStyling = "{ style: 'lines', color: 'purple', width: 10px, order: 100 }";

GLFWwindow* main_window = nullptr;
Tangram::Map* map = nullptr;
int width = 800;
int height = 600;
float density = 1.0;
bool recreate_context = false;
float pixel_scale = 1.0;

// Input handling
// ==============

const double double_tap_time = 0.5; // seconds
const double scroll_span_multiplier = 0.05; // scaling for zoom and rotation
const double scroll_distance_multiplier = 5.0; // scaling for shove
const double single_tap_time = 0.25; //seconds (to avoid a long press being considered as a tap)

bool was_panning = false;
double last_time_released = -double_tap_time; // First click should never trigger a double tap
double last_time_pressed = 0.0;
double last_time_moved = 0.0;
double last_x_down = 0.0;
double last_y_down = 0.0;
double last_x_velocity = 0.0;
double last_y_velocity = 0.0;

using namespace Tangram;

std::shared_ptr<ClientGeoJsonSource> data_source;
LngLat last_point;
std::vector<LngLat> taps;
Marker* marker = nullptr;

template<typename T>
static constexpr T clamp(T val, T min, T max) {
    return val > max ? max : val < min ? min : val;
}

void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {

    if (button != GLFW_MOUSE_BUTTON_1) {
        return; // This event is for a mouse button that we don't care about
    }

    double x, y;
    glfwGetCursorPos(window, &x, &y);
    x *= density;
    y *= density;
    double time = glfwGetTime();

    if (was_panning && action == GLFW_RELEASE) {
        was_panning = false;
        auto vx = clamp(last_x_velocity, -2000.0, 2000.0);
        auto vy = clamp(last_y_velocity, -2000.0, 2000.0);
        map->handleFlingGesture(x, y, vx, vy);
        return; // Clicks with movement don't count as taps, so stop here
    }

    if (action == GLFW_PRESS) {
        map->handlePanGesture(0.0f, 0.0f, 0.0f, 0.0f);
        last_x_down = x;
        last_y_down = y;
        last_time_pressed = time;
        return;
    }

    if ((time - last_time_released) < double_tap_time) {
        // Double tap recognized
        LngLat p;
        map->screenPositionToLngLat(x, y, &p.longitude, &p.latitude);
        map->setPositionEased(p.longitude, p.latitude, 1.f);

        logMsg("pick feature\n");
        map->clearDataSource(*data_source, true, true);

        auto picks = map->pickFeaturesAt(x, y);
        std::string name;
        logMsg("picked %d features\n", picks.size());
        for (const auto& it : picks) {
            if (it.properties->getString("name", name)) {
                logMsg(" - %f\t %s\n", it.distance, name.c_str());
            }
        }
    } else if ((time - last_time_pressed) < single_tap_time) {
        // Single tap recognized
        LngLat p1;
        map->screenPositionToLngLat(x, y, &p1.longitude, &p1.latitude);
        taps.push_back(p1);

        if (!(last_point == LngLat{0, 0})) {
            LngLat p2 = last_point;
            logMsg("add line (%f, %f)  (%f, %f)\n",
                   p1.longitude, p1.latitude,
                   p2.longitude, p2.latitude);

            if (!marker) {
                marker = map->markerAdd(markerStyling.c_str());
            }
            map->markerSetPolyline(marker, taps.data(), taps.size());
        }
        last_point = p1;

        // Tangram::clearDataSource(*data_source, false, true);
        // This updates the tiles (maybe we need a recalcTiles())
        requestRender();
    }

    last_time_released = time;

}

void cursor_pos_callback(GLFWwindow* window, double x, double y) {

    x *= density;
    y *= density;

    int action = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1);
    double time = glfwGetTime();

    if (action == GLFW_PRESS) {

        if (was_panning) {
            map->handlePanGesture(last_x_down, last_y_down, x, y);
        }

        was_panning = true;
        last_x_velocity = (x - last_x_down) / (time - last_time_moved);
        last_y_velocity = (y - last_y_down) / (time - last_time_moved);
        last_x_down = x;
        last_y_down = y;

    }

    last_time_moved = time;

}

void scroll_callback(GLFWwindow* window, double scrollx, double scrolly) {

    double x, y;
    glfwGetCursorPos(window, &x, &y);
    x *= density;
    y *= density;

    bool rotating = glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;
    bool shoving = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;

    if (shoving) {
        map->handleShoveGesture(scroll_distance_multiplier * scrolly);
    } else if (rotating) {
        map->handleRotateGesture(x, y, scroll_span_multiplier * scrolly);
    } else {
        map->handlePinchGesture(x, y, 1.0 + scroll_span_multiplier * scrolly, 0.f);
    }

}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {

    if (action == GLFW_PRESS) {
        switch (key) {
            case GLFW_KEY_1:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::freeze_tiles);
                break;
            case GLFW_KEY_2:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::proxy_colors);
                break;
            case GLFW_KEY_3:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tile_bounds);
                break;
            case GLFW_KEY_4:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tile_infos);
                break;
            case GLFW_KEY_5:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::labels);
                break;
            case GLFW_KEY_6:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::draw_all_labels);
                break;
            case GLFW_KEY_7:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tangram_infos);
                break;
            case GLFW_KEY_8:
                Tangram::toggleDebugFlag(Tangram::DebugFlags::tangram_stats);
                break;
            case GLFW_KEY_BACKSPACE:
                recreate_context = true;
                break;
            case GLFW_KEY_R:
                map->loadSceneAsync(sceneFile.c_str());
                break;
            case GLFW_KEY_Z:
                map->setZoomEased(map->getZoom() + 1.f, 1.5f);
                break;
            case GLFW_KEY_N:
                map->setRotationEased(0.f, 1.f);
                break;
            case GLFW_KEY_S:
                if (pixel_scale == 1.0) {
                    pixel_scale = 2.0;
                } else if (pixel_scale == 2.0) {
                    pixel_scale = 0.75;
                } else {
                    pixel_scale = 1.0;
                }
                map->loadSceneAsync(sceneFile.c_str());
                map->setPixelScale(pixel_scale);
                break;
            case GLFW_KEY_P:
                map->queueSceneUpdate("cameras", "{ main_camera: { type: perspective } }");
                map->applySceneUpdates();
                break;
            case GLFW_KEY_I:
                map->queueSceneUpdate("cameras", "{ main_camera: { type: isometric } }");
                map->applySceneUpdates();
                break;
            case GLFW_KEY_G:
                static bool geoJSON = false;
                if (!geoJSON) {
                    LOGS("Switching to GeoJSON data source");
                    map->queueSceneUpdate("sources.osm.type", "GeoJSON");
                    map->queueSceneUpdate("sources.osm.url", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.json");
                } else {
                    LOGS("Switching to MVT data source");
                    map->queueSceneUpdate("sources.osm.type", "MVT");
                    map->queueSceneUpdate("sources.osm.url", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.mvt");
                }
                geoJSON = !geoJSON;
                map->applySceneUpdates();
                break;
            case GLFW_KEY_ESCAPE:
                glfwSetWindowShouldClose(main_window, true);
                break;
            case GLFW_KEY_F1:
                map->setPosition(-74.00976419448854, 40.70532700869127);
                map->setZoom(16);
                break;
            case GLFW_KEY_F2:
                map->setPosition(8.82, 53.08);
                map->setZoom(14);
                break;
        default:
                break;
        }
    }
}

void drop_callback(GLFWwindow* window, int count, const char** paths) {

    sceneFile = std::string(paths[0]);
    map->loadSceneAsync(sceneFile.c_str());

}

// Window handling
// ===============

void framebuffer_size_callback(GLFWwindow* window, int fWidth, int fHeight) {

    int wWidth = 0, wHeight = 0;
    glfwGetWindowSize(main_window, &wWidth, &wHeight);
    float new_density = (float)fWidth / (float)wWidth;
    if (new_density != density) {
        recreate_context = true;
        density = new_density;
    }
    map->setPixelScale(density);
    map->resize(fWidth, fHeight);

}

void init_main_window(bool recreate) {

    // Setup tangram
    if (!map) {
        map = new Tangram::Map();
        map->loadSceneAsync(sceneFile.c_str(), true);
    }

    if (!recreate) {
        // Destroy old window
        if (main_window != nullptr) {
            glfwDestroyWindow(main_window);
        }

        // Create a windowed mode window and its OpenGL context
        glfwWindowHint(GLFW_SAMPLES, 2);
        main_window = glfwCreateWindow(width, height, "Tangram ES", NULL, NULL);
        if (!main_window) {
            glfwTerminate();
        }

        // Make the main_window's context current
        glfwMakeContextCurrent(main_window);

        // Set input callbacks
        glfwSetFramebufferSizeCallback(main_window, framebuffer_size_callback);
        glfwSetMouseButtonCallback(main_window, mouse_button_callback);
        glfwSetCursorPosCallback(main_window, cursor_pos_callback);
        glfwSetScrollCallback(main_window, scroll_callback);
        glfwSetKeyCallback(main_window, key_callback);
        glfwSetDropCallback(main_window, drop_callback);
    }

    // Setup graphics
    map->setupGL();
    int fWidth = 0, fHeight = 0;
    glfwGetFramebufferSize(main_window, &fWidth, &fHeight);
    framebuffer_size_callback(main_window, fWidth, fHeight);

    data_source = std::make_shared<ClientGeoJsonSource>("touch", "");
    map->addDataSource(data_source);

}

// Main program
// ============

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

    static bool keepRunning = true;

    // Give it a chance to shutdown cleanly on CTRL-C
    signal(SIGINT, [](int) {
            if (keepRunning) {
                logMsg("shutdown\n");
                keepRunning = false;
                glfwPostEmptyEvent();
            } else {
                logMsg("killed!\n");
                exit(1);
            }});

    int argi = 0;
    while (++argi < argc) {
        if (strcmp(argv[argi - 1], "-f") == 0) {
            sceneFile = std::string(argv[argi]);
            logMsg("File from command line: %s\n", argv[argi]);
            break;
        }
    }

    // Initialize networking
    NSurlInit();

    // Initialize the windowing library
    if (!glfwInit()) {
        return -1;
    }

    init_main_window(false);

    double lastTime = glfwGetTime();

    // Loop until the user closes the window
    while (keepRunning && !glfwWindowShouldClose(main_window)) {

        double currentTime = glfwGetTime();
        double delta = currentTime - lastTime;
        lastTime = currentTime;

        // Render
        map->update(delta);
        map->render();

        // Swap front and back buffers
        glfwSwapBuffers(main_window);

        // Poll for and process events
        if (isContinuousRendering()) {
            glfwPollEvents();
        } else {
            glfwWaitEvents();
        }

        if (recreate_context) {
            logMsg("recreate context\n");
             // Simulate GL context loss
            init_main_window(true);
            recreate_context = false;
        }

    }

    finishUrlRequests();

    if (map) {
        delete map;
        map = nullptr;
    }

    glfwTerminate();
    return 0;
}
<|endoftext|>


<commit_before>/*
 *  Phusion Passenger - https://www.phusionpassenger.com/
 *  Copyright (c) 2011-2016 Phusion Holding B.V.
 *
 *  "Passenger", "Phusion Passenger" and "Union Station" are registered
 *  trademarks of Phusion Holding B.V.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
#include <Core/Controller.h>

/*************************************************************************
 *
 * Implements Core::Controller methods pertaining selecting an application
 * process to handle the current request.
 *
 *************************************************************************/

namespace Passenger {
namespace Core {

using namespace std;
using namespace boost;

/****************************
 *
 * Private methods
 *
 ****************************/

void
Controller::checkoutSession(Client *client, Request *req) {
	GetCallback callback;
	Options &options = req->options;

	CC_BENCHMARK_POINT(client, req, BM_BEFORE_CHECKOUT);
	SKC_TRACE(client, 2, "Checking out session: appRoot=" << options.appRoot);
	req->state = Request::CHECKING_OUT_SESSION;

	if (req->requestBodyBuffering) {
		assert(!req->bodyBuffer.isStarted());
	} else {
		assert(!req->bodyChannel.isStarted());
	}

	callback.func = sessionCheckedOut;
	callback.userData = req;

	options.currentTime = SystemTime::getUsec();

	refRequest(req, __FILE__, __LINE__);
	#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
		req->timeBeforeAccessingApplicationPool = ev_now(getLoop());
	#endif
	asyncGetFromApplicationPool(req, callback);
	#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
		if (!req->timedAppPoolGet) {
			req->timedAppPoolGet = true;
			ev_now_update(getLoop());
			reportLargeTimeDiff(client, "ApplicationPool get until return",
				req->timeBeforeAccessingApplicationPool, ev_now(getLoop()));
		}
	#endif
}

void
Controller::asyncGetFromApplicationPool(Request *req, ApplicationPool2::GetCallback callback) {
	appPool->asyncGet(req->options, callback, true,
		req->useUnionStation()
		? &req->stopwatchLogs.getFromPool
		: NULL);
}

void
Controller::sessionCheckedOut(const AbstractSessionPtr &session, const ExceptionPtr &e,
	void *userData)
{
	Request *req = static_cast<Request *>(userData);
	Client *client = static_cast<Client *>(req->client);
	Controller *self = static_cast<Controller *>(getServerFromClient(client));

	if (self->getContext()->libev->onEventLoopThread()) {
		self->sessionCheckedOutFromEventLoopThread(client, req, session, e);
		self->unrefRequest(req, __FILE__, __LINE__);
	} else {
		self->getContext()->libev->runLater(
			boost::bind(&Controller::sessionCheckedOutFromAnotherThread,
				self, client, req, session, e));
	}
}

void
Controller::sessionCheckedOutFromAnotherThread(Client *client, Request *req,
	AbstractSessionPtr session, ExceptionPtr e)
{
	SKC_LOG_EVENT(Controller, client, "sessionCheckedOutFromAnotherThread");
	sessionCheckedOutFromEventLoopThread(client, req, session, e);
	unrefRequest(req, __FILE__, __LINE__);
}

void
Controller::sessionCheckedOutFromEventLoopThread(Client *client, Request *req,
	const AbstractSessionPtr &session, const ExceptionPtr &e)
{
	if (req->ended()) {
		return;
	}

	TRACE_POINT();
	CC_BENCHMARK_POINT(client, req, BM_AFTER_CHECKOUT);

	#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
		if (!req->timedAppPoolGet) {
			req->timedAppPoolGet = true;
			ev_now_update(getLoop());
			reportLargeTimeDiff(client, "ApplicationPool get until return",
				req->timeBeforeAccessingApplicationPool, ev_now(getLoop()));
		}
	#endif

	if (e == NULL) {
		SKC_DEBUG(client, "Session checked out: pid=" << session->getPid() <<
			", gupid=" << session->getGupid());
		req->session = session;
		UPDATE_TRACE_POINT();
		maybeSend100Continue(client, req);
		UPDATE_TRACE_POINT();
		initiateSession(client, req);
	} else {
		UPDATE_TRACE_POINT();
		req->endStopwatchLog(&req->stopwatchLogs.getFromPool, false);
		reportSessionCheckoutError(client, req, e);
	}
}

void
Controller::maybeSend100Continue(Client *client, Request *req) {
	int httpVersion = req->httpMajor * 1000 + req->httpMinor * 10;
	if (httpVersion >= 1010 && req->hasBody() && !req->strip100ContinueHeader) {
		// Apps with the "session" protocol don't respond with 100-Continue,
		// so we do it for them.
		const LString *value = req->headers.lookup(HTTP_EXPECT);
		if (value != NULL
		 && psg_lstr_cmp(value, P_STATIC_STRING("100-continue"))
		 && req->session->getProtocol() == P_STATIC_STRING("session"))
		{
			const unsigned int BUFSIZE = 32;
			char *buf = (char *) psg_pnalloc(req->pool, BUFSIZE);
			int size = snprintf(buf, BUFSIZE, "HTTP/%d.%d 100 Continue\r\n",
				(int) req->httpMajor, (int) req->httpMinor);
			writeResponse(client, buf, size);
			if (!req->ended()) {
				// Allow sending more response headers.
				req->responseBegun = false;
			}
		}
	}
}

void
Controller::initiateSession(Client *client, Request *req) {
	TRACE_POINT();
	req->sessionCheckoutTry++;
	try {
		req->session->initiate(false);
	} catch (const SystemException &e2) {
		if (req->sessionCheckoutTry < MAX_SESSION_CHECKOUT_TRY) {
			SKC_DEBUG(client, "Error checking out session (" << e2.what() <<
				"); retrying (attempt " << req->sessionCheckoutTry << ")");
			refRequest(req, __FILE__, __LINE__);
			getContext()->libev->runLater(boost::bind(checkoutSessionLater, req));
		} else {
			string message = "could not initiate a session (";
			message.append(e2.what());
			message.append(")");
			disconnectWithError(&client, message);
		}
		return;
	}

	UPDATE_TRACE_POINT();
	if (req->useUnionStation()) {
		req->endStopwatchLog(&req->stopwatchLogs.getFromPool);
		req->logMessage("Application PID: " +
			toString(req->session->getPid()) +
			" (GUPID: " + req->session->getGupid() + ")");
		req->beginStopwatchLog(&req->stopwatchLogs.requestProxying, "request proxying");
	}

	UPDATE_TRACE_POINT();
	SKC_DEBUG(client, "Session initiated: fd=" << req->session->fd());
	req->appSink.reinitialize(req->session->fd());
	req->appSource.reinitialize(req->session->fd());
	/***************/
	/***************/
	reinitializeAppResponse(client, req);
	sendHeaderToApp(client, req);
}

void
Controller::checkoutSessionLater(Request *req) {
	Client *client = static_cast<Client *>(req->client);
	Controller *self = static_cast<Controller *>(
		Controller::getServerFromClient(client));
	SKC_LOG_EVENT_FROM_STATIC(self, Controller, client, "checkoutSessionLater");

	if (!req->ended()) {
		self->checkoutSession(client, req);
	}
	self->unrefRequest(req, __FILE__, __LINE__);
}

void
Controller::reportSessionCheckoutError(Client *client, Request *req,
	const ExceptionPtr &e)
{
	TRACE_POINT();
	{
		boost::shared_ptr<RequestQueueFullException> e2 =
			dynamic_pointer_cast<RequestQueueFullException>(e);
		if (e2 != NULL) {
			writeRequestQueueFullExceptionErrorResponse(client, req, e2);
			return;
		}
	}
	{
		boost::shared_ptr<SpawnException> e2 = dynamic_pointer_cast<SpawnException>(e);
		if (e2 != NULL) {
			writeSpawnExceptionErrorResponse(client, req, e2);
			return;
		}
	}
	writeOtherExceptionErrorResponse(client, req, e);
}

void
Controller::writeRequestQueueFullExceptionErrorResponse(Client *client, Request *req,
	const boost::shared_ptr<RequestQueueFullException> &e)
{
	TRACE_POINT();
	const LString *value = req->secureHeaders.lookup(
		"!~PASSENGER_REQUEST_QUEUE_OVERFLOW_STATUS_CODE");
	int requestQueueOverflowStatusCode = 503;
	if (value != NULL && value->size > 0) {
		value = psg_lstr_make_contiguous(value, req->pool);
		requestQueueOverflowStatusCode = stringToInt(
			StaticString(value->start->data, value->size));
	}

	SKC_WARN(client, "Returning HTTP " << requestQueueOverflowStatusCode <<
		" due to: " << e->what());

	endRequestWithSimpleResponse(&client, &req,
		"
This website is under heavy load (queue full)
"
		"
We're sorry, too many people are accessing this website at the same "
		"time. We're working on this problem. Please try again later.
",
		requestQueueOverflowStatusCode);
}

void
Controller::writeSpawnExceptionErrorResponse(Client *client, Request *req,
	const boost::shared_ptr<SpawnException> &e)
{
	TRACE_POINT();
	SKC_ERROR(client, "Cannot checkout session because a spawning error occurred. " <<
		"The identifier of the error is " << e->get("error_id") << ". Please see earlier logs for " <<
		"details about the error.");
	endRequestWithErrorResponse(&client, &req, e->getErrorPage(), e.get());
}

void
Controller::writeOtherExceptionErrorResponse(Client *client, Request *req, const ExceptionPtr &e) {
	TRACE_POINT();
	string typeName;
	#ifdef CXX_ABI_API_AVAILABLE
		int status;
		char *tmp = abi::__cxa_demangle(typeid(*e).name(), 0, 0, &status);
		if (tmp != NULL) {
			typeName = tmp;
			free(tmp);
		} else {
			typeName = typeid(*e).name();
		}
	#else
		typeName = typeid(*e).name();
	#endif

	const unsigned int exceptionMessageLen = strlen(e->what());
	string backtrace;
	boost::shared_ptr<tracable_exception> e3 = dynamic_pointer_cast<tracable_exception>(e);
	if (e3 != NULL) {
		backtrace = e3->backtrace();
	}

	SKC_WARN(client, "Cannot checkout session due to " <<
		typeName << ": " << e->what() << (!backtrace.empty() ? "\n" + backtrace : ""));

	if (friendlyErrorPagesEnabled(req)) {
		const unsigned int BUFFER_SIZE = 512 + typeName.size() +
			exceptionMessageLen + backtrace.size();
		char *buf = (char *) psg_pnalloc(req->pool, BUFFER_SIZE);
		char *pos = buf;
		const char *end = buf + BUFFER_SIZE;

		pos = appendData(pos, end, "
Internal server error
");
		pos = appendData(pos, end, "
Application could not be started.
");
		pos = appendData(pos, end, "
Exception type: ");
		pos = appendData(pos, end, typeName);
		pos = appendData(pos, end, "
Error message: ");
		pos = appendData(pos, end, e->what(), exceptionMessageLen);
		if (!backtrace.empty()) {
			pos = appendData(pos, end, "
Backtrace:
");
			pos = appendData(pos, end, backtrace);
		}
		pos = appendData(pos, end, "
");

		endRequestWithSimpleResponse(&client, &req, StaticString(buf, pos - buf), 500);
	} else {
		endRequestWithSimpleResponse(&client, &req, "
Internal server error
"
			"Application could not be started. Please try again later.", 500);
	}
}

/**
 * `message` will be copied and doesn't need to outlive the request.
 */
void
Controller::endRequestWithErrorResponse(Client **c, Request **r, const StaticString &message,
	const SpawnException *e)
{
	TRACE_POINT();
	Client *client = *c;
	Request *req = *r;
	ErrorRenderer renderer(*resourceLocator);
	string data;

	if (friendlyErrorPagesEnabled(req)) {
		try {
			data = renderer.renderWithDetails(message, req->options, e);
		} catch (const SystemException &e2) {
			SKC_ERROR(client, "Cannot render an error page: " << e2.what() <<
				"\n" << e2.backtrace());
			data = message;
		}
	} else {
		try {
			data = renderer.renderWithoutDetails(e);
		} catch (const SystemException &e2) {
			SKC_ERROR(client, "Cannot render an error page: " << e2.what() <<
				"\n" << e2.backtrace());
			data = "
Internal server error
";
		}
	}

	endRequestWithSimpleResponse(c, r, psg_pstrdup(req->pool, data), 500);
}

bool
Controller::friendlyErrorPagesEnabled(Request *req) {
	bool defaultValue;
	string defaultStr = agentsOptions->get("friendly_error_pages");
	if (defaultStr == "auto") {
		defaultValue = (req->options.environment == "development");
	} else {
		defaultValue = defaultStr == "true";
	}
	return getBoolOption(req, "!~PASSENGER_FRIENDLY_ERROR_PAGES", defaultValue);
}

/***************/

/***************/

} // namespace Core
} // namespace Passenger
<commit_msg>Fix compilation warning on Clang<commit_after>/*
 *  Phusion Passenger - https://www.phusionpassenger.com/
 *  Copyright (c) 2011-2016 Phusion Holding B.V.
 *
 *  "Passenger", "Phusion Passenger" and "Union Station" are registered
 *  trademarks of Phusion Holding B.V.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
#include <Core/Controller.h>

/*************************************************************************
 *
 * Implements Core::Controller methods pertaining selecting an application
 * process to handle the current request.
 *
 *************************************************************************/

namespace Passenger {
namespace Core {

using namespace std;
using namespace boost;

/****************************
 *
 * Private methods
 *
 ****************************/

void
Controller::checkoutSession(Client *client, Request *req) {
	GetCallback callback;
	Options &options = req->options;

	CC_BENCHMARK_POINT(client, req, BM_BEFORE_CHECKOUT);
	SKC_TRACE(client, 2, "Checking out session: appRoot=" << options.appRoot);
	req->state = Request::CHECKING_OUT_SESSION;

	if (req->requestBodyBuffering) {
		assert(!req->bodyBuffer.isStarted());
	} else {
		assert(!req->bodyChannel.isStarted());
	}

	callback.func = sessionCheckedOut;
	callback.userData = req;

	options.currentTime = SystemTime::getUsec();

	refRequest(req, __FILE__, __LINE__);
	#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
		req->timeBeforeAccessingApplicationPool = ev_now(getLoop());
	#endif
	asyncGetFromApplicationPool(req, callback);
	#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
		if (!req->timedAppPoolGet) {
			req->timedAppPoolGet = true;
			ev_now_update(getLoop());
			reportLargeTimeDiff(client, "ApplicationPool get until return",
				req->timeBeforeAccessingApplicationPool, ev_now(getLoop()));
		}
	#endif
}

void
Controller::asyncGetFromApplicationPool(Request *req, ApplicationPool2::GetCallback callback) {
	appPool->asyncGet(req->options, callback, true,
		req->useUnionStation()
		? &req->stopwatchLogs.getFromPool
		: NULL);
}

void
Controller::sessionCheckedOut(const AbstractSessionPtr &session, const ExceptionPtr &e,
	void *userData)
{
	Request *req = static_cast<Request *>(userData);
	Client *client = static_cast<Client *>(req->client);
	Controller *self = static_cast<Controller *>(getServerFromClient(client));

	if (self->getContext()->libev->onEventLoopThread()) {
		self->sessionCheckedOutFromEventLoopThread(client, req, session, e);
		self->unrefRequest(req, __FILE__, __LINE__);
	} else {
		self->getContext()->libev->runLater(
			boost::bind(&Controller::sessionCheckedOutFromAnotherThread,
				self, client, req, session, e));
	}
}

void
Controller::sessionCheckedOutFromAnotherThread(Client *client, Request *req,
	AbstractSessionPtr session, ExceptionPtr e)
{
	SKC_LOG_EVENT(Controller, client, "sessionCheckedOutFromAnotherThread");
	sessionCheckedOutFromEventLoopThread(client, req, session, e);
	unrefRequest(req, __FILE__, __LINE__);
}

void
Controller::sessionCheckedOutFromEventLoopThread(Client *client, Request *req,
	const AbstractSessionPtr &session, const ExceptionPtr &e)
{
	if (req->ended()) {
		return;
	}

	TRACE_POINT();
	CC_BENCHMARK_POINT(client, req, BM_AFTER_CHECKOUT);

	#ifdef DEBUG_CC_EVENT_LOOP_BLOCKING
		if (!req->timedAppPoolGet) {
			req->timedAppPoolGet = true;
			ev_now_update(getLoop());
			reportLargeTimeDiff(client, "ApplicationPool get until return",
				req->timeBeforeAccessingApplicationPool, ev_now(getLoop()));
		}
	#endif

	if (e == NULL) {
		SKC_DEBUG(client, "Session checked out: pid=" << session->getPid() <<
			", gupid=" << session->getGupid());
		req->session = session;
		UPDATE_TRACE_POINT();
		maybeSend100Continue(client, req);
		UPDATE_TRACE_POINT();
		initiateSession(client, req);
	} else {
		UPDATE_TRACE_POINT();
		req->endStopwatchLog(&req->stopwatchLogs.getFromPool, false);
		reportSessionCheckoutError(client, req, e);
	}
}

void
Controller::maybeSend100Continue(Client *client, Request *req) {
	int httpVersion = req->httpMajor * 1000 + req->httpMinor * 10;
	if (httpVersion >= 1010 && req->hasBody() && !req->strip100ContinueHeader) {
		// Apps with the "session" protocol don't respond with 100-Continue,
		// so we do it for them.
		const LString *value = req->headers.lookup(HTTP_EXPECT);
		if (value != NULL
		 && psg_lstr_cmp(value, P_STATIC_STRING("100-continue"))
		 && req->session->getProtocol() == P_STATIC_STRING("session"))
		{
			const unsigned int BUFSIZE = 32;
			char *buf = (char *) psg_pnalloc(req->pool, BUFSIZE);
			int size = snprintf(buf, BUFSIZE, "HTTP/%d.%d 100 Continue\r\n",
				(int) req->httpMajor, (int) req->httpMinor);
			writeResponse(client, buf, size);
			if (!req->ended()) {
				// Allow sending more response headers.
				req->responseBegun = false;
			}
		}
	}
}

void
Controller::initiateSession(Client *client, Request *req) {
	TRACE_POINT();
	req->sessionCheckoutTry++;
	try {
		req->session->initiate(false);
	} catch (const SystemException &e2) {
		if (req->sessionCheckoutTry < MAX_SESSION_CHECKOUT_TRY) {
			SKC_DEBUG(client, "Error checking out session (" << e2.what() <<
				"); retrying (attempt " << req->sessionCheckoutTry << ")");
			refRequest(req, __FILE__, __LINE__);
			getContext()->libev->runLater(boost::bind(checkoutSessionLater, req));
		} else {
			string message = "could not initiate a session (";
			message.append(e2.what());
			message.append(")");
			disconnectWithError(&client, message);
		}
		return;
	}

	UPDATE_TRACE_POINT();
	if (req->useUnionStation()) {
		req->endStopwatchLog(&req->stopwatchLogs.getFromPool);
		req->logMessage("Application PID: " +
			toString(req->session->getPid()) +
			" (GUPID: " + req->session->getGupid() + ")");
		req->beginStopwatchLog(&req->stopwatchLogs.requestProxying, "request proxying");
	}

	UPDATE_TRACE_POINT();
	SKC_DEBUG(client, "Session initiated: fd=" << req->session->fd());
	req->appSink.reinitialize(req->session->fd());
	req->appSource.reinitialize(req->session->fd());
	/***************/
	/***************/
	reinitializeAppResponse(client, req);
	sendHeaderToApp(client, req);
}

void
Controller::checkoutSessionLater(Request *req) {
	Client *client = static_cast<Client *>(req->client);
	Controller *self = static_cast<Controller *>(
		Controller::getServerFromClient(client));
	SKC_LOG_EVENT_FROM_STATIC(self, Controller, client, "checkoutSessionLater");

	if (!req->ended()) {
		self->checkoutSession(client, req);
	}
	self->unrefRequest(req, __FILE__, __LINE__);
}

void
Controller::reportSessionCheckoutError(Client *client, Request *req,
	const ExceptionPtr &e)
{
	TRACE_POINT();
	{
		boost::shared_ptr<RequestQueueFullException> e2 =
			dynamic_pointer_cast<RequestQueueFullException>(e);
		if (e2 != NULL) {
			writeRequestQueueFullExceptionErrorResponse(client, req, e2);
			return;
		}
	}
	{
		boost::shared_ptr<SpawnException> e2 = dynamic_pointer_cast<SpawnException>(e);
		if (e2 != NULL) {
			writeSpawnExceptionErrorResponse(client, req, e2);
			return;
		}
	}
	writeOtherExceptionErrorResponse(client, req, e);
}

void
Controller::writeRequestQueueFullExceptionErrorResponse(Client *client, Request *req,
	const boost::shared_ptr<RequestQueueFullException> &e)
{
	TRACE_POINT();
	const LString *value = req->secureHeaders.lookup(
		"!~PASSENGER_REQUEST_QUEUE_OVERFLOW_STATUS_CODE");
	int requestQueueOverflowStatusCode = 503;
	if (value != NULL && value->size > 0) {
		value = psg_lstr_make_contiguous(value, req->pool);
		requestQueueOverflowStatusCode = stringToInt(
			StaticString(value->start->data, value->size));
	}

	SKC_WARN(client, "Returning HTTP " << requestQueueOverflowStatusCode <<
		" due to: " << e->what());

	endRequestWithSimpleResponse(&client, &req,
		"
This website is under heavy load (queue full)
"
		"
We're sorry, too many people are accessing this website at the same "
		"time. We're working on this problem. Please try again later.
",
		requestQueueOverflowStatusCode);
}

void
Controller::writeSpawnExceptionErrorResponse(Client *client, Request *req,
	const boost::shared_ptr<SpawnException> &e)
{
	TRACE_POINT();
	SKC_ERROR(client, "Cannot checkout session because a spawning error occurred. " <<
		"The identifier of the error is " << e->get("error_id") << ". Please see earlier logs for " <<
		"details about the error.");
	endRequestWithErrorResponse(&client, &req, e->getErrorPage(), e.get());
}

void
Controller::writeOtherExceptionErrorResponse(Client *client, Request *req, const ExceptionPtr &e) {
	TRACE_POINT();
	string typeName;
	const oxt::tracable_exception &eptr = *e;
	#ifdef CXX_ABI_API_AVAILABLE
		int status;
		char *tmp = abi::__cxa_demangle(typeid(eptr).name(), 0, 0, &status);
		if (tmp != NULL) {
			typeName = tmp;
			free(tmp);
		} else {
			typeName = typeid(eptr).name();
		}
	#else
		typeName = typeid(eptr).name();
	#endif

	const unsigned int exceptionMessageLen = strlen(e->what());
	string backtrace;
	boost::shared_ptr<tracable_exception> e3 = dynamic_pointer_cast<tracable_exception>(e);
	if (e3 != NULL) {
		backtrace = e3->backtrace();
	}

	SKC_WARN(client, "Cannot checkout session due to " <<
		typeName << ": " << e->what() << (!backtrace.empty() ? "\n" + backtrace : ""));

	if (friendlyErrorPagesEnabled(req)) {
		const unsigned int BUFFER_SIZE = 512 + typeName.size() +
			exceptionMessageLen + backtrace.size();
		char *buf = (char *) psg_pnalloc(req->pool, BUFFER_SIZE);
		char *pos = buf;
		const char *end = buf + BUFFER_SIZE;

		pos = appendData(pos, end, "
Internal server error
");
		pos = appendData(pos, end, "
Application could not be started.
");
		pos = appendData(pos, end, "
Exception type: ");
		pos = appendData(pos, end, typeName);
		pos = appendData(pos, end, "
Error message: ");
		pos = appendData(pos, end, e->what(), exceptionMessageLen);
		if (!backtrace.empty()) {
			pos = appendData(pos, end, "
Backtrace:
");
			pos = appendData(pos, end, backtrace);
		}
		pos = appendData(pos, end, "
");

		endRequestWithSimpleResponse(&client, &req, StaticString(buf, pos - buf), 500);
	} else {
		endRequestWithSimpleResponse(&client, &req, "
Internal server error
"
			"Application could not be started. Please try again later.", 500);
	}
}

/**
 * `message` will be copied and doesn't need to outlive the request.
 */
void
Controller::endRequestWithErrorResponse(Client **c, Request **r, const StaticString &message,
	const SpawnException *e)
{
	TRACE_POINT();
	Client *client = *c;
	Request *req = *r;
	ErrorRenderer renderer(*resourceLocator);
	string data;

	if (friendlyErrorPagesEnabled(req)) {
		try {
			data = renderer.renderWithDetails(message, req->options, e);
		} catch (const SystemException &e2) {
			SKC_ERROR(client, "Cannot render an error page: " << e2.what() <<
				"\n" << e2.backtrace());
			data = message;
		}
	} else {
		try {
			data = renderer.renderWithoutDetails(e);
		} catch (const SystemException &e2) {
			SKC_ERROR(client, "Cannot render an error page: " << e2.what() <<
				"\n" << e2.backtrace());
			data = "
Internal server error
";
		}
	}

	endRequestWithSimpleResponse(c, r, psg_pstrdup(req->pool, data), 500);
}

bool
Controller::friendlyErrorPagesEnabled(Request *req) {
	bool defaultValue;
	string defaultStr = agentsOptions->get("friendly_error_pages");
	if (defaultStr == "auto") {
		defaultValue = (req->options.environment == "development");
	} else {
		defaultValue = defaultStr == "true";
	}
	return getBoolOption(req, "!~PASSENGER_FRIENDLY_ERROR_PAGES", defaultValue);
}

/***************/

/***************/

} // namespace Core
} // namespace Passenger
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------------
// HIMG, by Marcus Geelnard, 2015
//
// This is free and unencumbered software released into the public domain.
//
// See LICENSE for details.
//-----------------------------------------------------------------------------

#include "downsampled.h"

#include <algorithm>

namespace himg {

Downsampled::Downsampled() : m_rows(0), m_columns(0) {
}

void Downsampled::SampleImage(const uint8_t *pixels,
                              int stride,
                              int width,
                              int height) {
  // Divide by 8x8, rounding up.
  m_rows = (height + 7) >> 3;
  m_columns = (width + 7) >> 3;

  // Calculate average color for each 8x8 block.
  std::vector<uint8_t> average;
  average.reserve(m_rows * m_columns);
  for (int v = 0; v < m_rows; ++v) {
    int y_min = std::max(0, v * 8 - 3);
    int y_max = std::min(height - 1, v * 8 + 4);
    for (int u = 0; u < m_columns; ++u) {
      int x_min = std::max(0, u * 8 - 3);
      int x_max = std::min(width - 1, u * 8 + 4);
      uint16_t sum = 0;
      for (int y = y_min; y <= y_max; ++y) {
        for (int x = x_min; x <= x_max; ++x) {
          sum += pixels[(y * width + x) * stride];
        }
      }
      int total_count = (x_max - x_min + 1) * (y_max - y_min + 1);
      average.push_back(
          static_cast<uint8_t>((sum + (total_count >> 1)) / total_count));
    }
  }

  // Compensate blocks for lienear interpolation (phase shift 1/16 pixels up &
  // to the left).
  m_data.reserve(m_columns * m_rows);
  for (int v = 0; v < m_rows; ++v) {
    int row1 = std::max(0, v - 1);
    int row2 = v;
    for (int u = 0; u < m_columns; ++u) {
      int col1 = std::max(0, u - 1);
      int col2 = u;
      uint16_t x11 = static_cast<uint16_t>(average[row1 * m_columns + col1]);
      uint16_t x12 = static_cast<uint16_t>(average[row1 * m_columns + col2]);
      uint16_t x21 = static_cast<uint16_t>(average[row2 * m_columns + col1]);
      uint16_t x22 = static_cast<uint16_t>(average[row2 * m_columns + col2]);
      uint16_t a1 = (1 * x11 + 15 * x12 + 8) >> 4;
      uint16_t a2 = (1 * x21 + 15 * x22 + 8) >> 4;
      m_data.push_back(static_cast<uint8_t>((1 * a1 + 15 * a2 + 8) >> 4));
    }
  }
}

void Downsampled::GetLowresBlock(int16_t *out, int u, int v) {
  // Pick out the four values in the corners of the block.
  int row1 = v;
  int row2 = std::min(m_rows - 1, v + 1);
  int col1 = u;
  int col2 = std::min(m_columns - 1, u + 1);
  int16_t x11 = static_cast<int16_t>(m_data[row1 * m_columns + col1]);
  int16_t x12 = static_cast<int16_t>(m_data[row1 * m_columns + col2]);
  int16_t x21 = static_cast<int16_t>(m_data[row2 * m_columns + col1]);
  int16_t x22 = static_cast<int16_t>(m_data[row2 * m_columns + col2]);

  // Liner interpolation to produce the left and right columns of the block.
  int16_t left[9], right[9];
  left[0] = x11;
  left[8] = x21;
  left[4] = (left[0] + left[8]) >> 1;
  left[2] = (left[0] + left[4]) >> 1;
  left[6] = (left[4] + left[8]) >> 1;
  left[1] = (left[0] + left[2]) >> 1;
  left[3] = (left[2] + left[4]) >> 1;
  left[5] = (left[4] + left[6]) >> 1;
  left[7] = (left[6] + left[8]) >> 1;
  right[0] = x12;
  right[8] = x22;
  right[4] = (right[0] + right[8]) >> 1;
  right[2] = (right[0] + right[4]) >> 1;
  right[6] = (right[4] + right[8]) >> 1;
  right[1] = (right[0] + right[2]) >> 1;
  right[3] = (right[2] + right[4]) >> 1;
  right[5] = (right[4] + right[6]) >> 1;
  right[7] = (right[6] + right[8]) >> 1;

  // Liner interpolation to produce the eight rows of the block.
  for (int y = 0; y < 8; ++y) {
    int16_t a0 = left[y];
    int16_t a8 = right[y];
    int16_t a4 = (a0 + a8) >> 1;
    int16_t a2 = (a0 + a4) >> 1;
    int16_t a6 = (a4 + a8) >> 1;
    int16_t a1 = (a0 + a2) >> 1;
    int16_t a3 = (a2 + a4) >> 1;
    int16_t a5 = (a4 + a6) >> 1;
    int16_t a7 = (a6 + a8) >> 1;
    out[0] = a0;
    out[1] = a1;
    out[2] = a2;
    out[3] = a3;
    out[4] = a4;
    out[5] = a5;
    out[6] = a6;
    out[7] = a7;
    out += 8;
  }
}

void Downsampled::GetBlockData(uint8_t *out) const {
  for (int v = 0; v < m_rows; ++v) {
    for (int u = 0; u < m_columns; ++u) {
      uint8_t predicted;
      if (u > 0 && v > 0) {
        predicted = m_data[v * m_columns + u - 1] +
                    m_data[(v - 1) * m_columns + u] -
                    m_data[(v - 1) * m_columns + u - 1];
      } else if (u > 0) {
        predicted = m_data[v * m_columns + u - 1];
      } else if (v > 0) {
        predicted = m_data[(v - 1) * m_columns + u];
      } else {
        predicted = 0;
      }
      *out++ = m_data[v * m_columns + u] - predicted;
    }
  }
}

void Downsampled::SetBlockData(const uint8_t *in, int rows, int columns) {
  m_rows = rows;
  m_columns = columns;
  m_data.resize(m_rows * m_columns);
  for (int v = 0; v < m_rows; ++v) {
    for (int u = 0; u < m_columns; ++u) {
      uint8_t predicted;
      if (u > 0 && v > 0) {
        predicted = m_data[v * m_columns + u - 1] +
                    m_data[(v - 1) * m_columns + u] -
                    m_data[(v - 1) * m_columns + u - 1];
      } else if (u > 0) {
        predicted = m_data[v * m_columns + u - 1];
      } else if (v > 0) {
        predicted = m_data[(v - 1) * m_columns + u];
      } else {
        predicted = 0;
      }
      m_data[v * m_columns + u] = *in++ + predicted;
    }
  }
}

}  // namespace himg
<commit_msg>Proper rounding in the linear interpolation<commit_after>//-----------------------------------------------------------------------------
// HIMG, by Marcus Geelnard, 2015
//
// This is free and unencumbered software released into the public domain.
//
// See LICENSE for details.
//-----------------------------------------------------------------------------

#include "downsampled.h"

#include <algorithm>

namespace himg {

Downsampled::Downsampled() : m_rows(0), m_columns(0) {
}

void Downsampled::SampleImage(const uint8_t *pixels,
                              int stride,
                              int width,
                              int height) {
  // Divide by 8x8, rounding up.
  m_rows = (height + 7) >> 3;
  m_columns = (width + 7) >> 3;

  // Calculate average color for each 8x8 block.
  std::vector<uint8_t> average;
  average.reserve(m_rows * m_columns);
  for (int v = 0; v < m_rows; ++v) {
    int y_min = std::max(0, v * 8 - 3);
    int y_max = std::min(height - 1, v * 8 + 4);
    for (int u = 0; u < m_columns; ++u) {
      int x_min = std::max(0, u * 8 - 3);
      int x_max = std::min(width - 1, u * 8 + 4);
      uint16_t sum = 0;
      for (int y = y_min; y <= y_max; ++y) {
        for (int x = x_min; x <= x_max; ++x) {
          sum += pixels[(y * width + x) * stride];
        }
      }
      int total_count = (x_max - x_min + 1) * (y_max - y_min + 1);
      average.push_back(
          static_cast<uint8_t>((sum + (total_count >> 1)) / total_count));
    }
  }

  // Compensate blocks for lienear interpolation (phase shift 1/16 pixels up &
  // to the left).
  m_data.reserve(m_columns * m_rows);
  for (int v = 0; v < m_rows; ++v) {
    int row1 = std::max(0, v - 1);
    int row2 = v;
    for (int u = 0; u < m_columns; ++u) {
      int col1 = std::max(0, u - 1);
      int col2 = u;
      uint16_t x11 = static_cast<uint16_t>(average[row1 * m_columns + col1]);
      uint16_t x12 = static_cast<uint16_t>(average[row1 * m_columns + col2]);
      uint16_t x21 = static_cast<uint16_t>(average[row2 * m_columns + col1]);
      uint16_t x22 = static_cast<uint16_t>(average[row2 * m_columns + col2]);
      uint16_t a1 = (1 * x11 + 15 * x12 + 8) >> 4;
      uint16_t a2 = (1 * x21 + 15 * x22 + 8) >> 4;
      m_data.push_back(static_cast<uint8_t>((1 * a1 + 15 * a2 + 8) >> 4));
    }
  }
}

void Downsampled::GetLowresBlock(int16_t *out, int u, int v) {
  // Pick out the four values in the corners of the block.
  int row1 = v;
  int row2 = std::min(m_rows - 1, v + 1);
  int col1 = u;
  int col2 = std::min(m_columns - 1, u + 1);
  int16_t x11 = static_cast<int16_t>(m_data[row1 * m_columns + col1]);
  int16_t x12 = static_cast<int16_t>(m_data[row1 * m_columns + col2]);
  int16_t x21 = static_cast<int16_t>(m_data[row2 * m_columns + col1]);
  int16_t x22 = static_cast<int16_t>(m_data[row2 * m_columns + col2]);

  // Liner interpolation to produce the left and right columns of the block.
  int16_t left[9], right[9];
  left[0] = x11;
  left[8] = x21;
  left[4] = (left[0] + left[8] + 1) >> 1;
  left[2] = (left[0] + left[4] + 1) >> 1;
  left[6] = (left[4] + left[8] + 1) >> 1;
  left[1] = (left[0] + left[2] + 1) >> 1;
  left[3] = (left[2] + left[4] + 1) >> 1;
  left[5] = (left[4] + left[6] + 1) >> 1;
  left[7] = (left[6] + left[8] + 1) >> 1;
  right[0] = x12;
  right[8] = x22;
  right[4] = (right[0] + right[8] + 1) >> 1;
  right[2] = (right[0] + right[4] + 1) >> 1;
  right[6] = (right[4] + right[8] + 1) >> 1;
  right[1] = (right[0] + right[2] + 1) >> 1;
  right[3] = (right[2] + right[4] + 1) >> 1;
  right[5] = (right[4] + right[6] + 1) >> 1;
  right[7] = (right[6] + right[8] + 1) >> 1;

  // Liner interpolation to produce the eight rows of the block.
  for (int y = 0; y < 8; ++y) {
    int16_t a0 = left[y];
    int16_t a8 = right[y];
    int16_t a4 = (a0 + a8 + 1) >> 1;
    int16_t a2 = (a0 + a4 + 1) >> 1;
    int16_t a6 = (a4 + a8 + 1) >> 1;
    int16_t a1 = (a0 + a2 + 1) >> 1;
    int16_t a3 = (a2 + a4 + 1) >> 1;
    int16_t a5 = (a4 + a6 + 1) >> 1;
    int16_t a7 = (a6 + a8 + 1) >> 1;
    out[0] = a0;
    out[1] = a1;
    out[2] = a2;
    out[3] = a3;
    out[4] = a4;
    out[5] = a5;
    out[6] = a6;
    out[7] = a7;
    out += 8;
  }
}

void Downsampled::GetBlockData(uint8_t *out) const {
  for (int v = 0; v < m_rows; ++v) {
    for (int u = 0; u < m_columns; ++u) {
      uint8_t predicted;
      if (u > 0 && v > 0) {
        predicted = m_data[v * m_columns + u - 1] +
                    m_data[(v - 1) * m_columns + u] -
                    m_data[(v - 1) * m_columns + u - 1];
      } else if (u > 0) {
        predicted = m_data[v * m_columns + u - 1];
      } else if (v > 0) {
        predicted = m_data[(v - 1) * m_columns + u];
      } else {
        predicted = 0;
      }
      *out++ = m_data[v * m_columns + u] - predicted;
    }
  }
}

void Downsampled::SetBlockData(const uint8_t *in, int rows, int columns) {
  m_rows = rows;
  m_columns = columns;
  m_data.resize(m_rows * m_columns);
  for (int v = 0; v < m_rows; ++v) {
    for (int u = 0; u < m_columns; ++u) {
      uint8_t predicted;
      if (u > 0 && v > 0) {
        predicted = m_data[v * m_columns + u - 1] +
                    m_data[(v - 1) * m_columns + u] -
                    m_data[(v - 1) * m_columns + u - 1];
      } else if (u > 0) {
        predicted = m_data[v * m_columns + u - 1];
      } else if (v > 0) {
        predicted = m_data[(v - 1) * m_columns + u];
      } else {
        predicted = 0;
      }
      m_data[v * m_columns + u] = *in++ + predicted;
    }
  }
}

}  // namespace himg
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <sstream>

#include <wordexp.h>

#include <ros/ros.h>

#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>
#include <image_transport/image_transport.h>
#include <image_transport/subscriber_filter.h>
#include <sensor_msgs/image_encodings.h>

#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>

#include <spencer_tracking_msgs/TrackedPersons2d.h>

extern "C" int mkpath(const char *path);
void subtractbg(cv::Mat &rgb, const cv::Mat &d, float thresh=1.0f, float bgcoeff=0.5f);

using namespace spencer_tracking_msgs;

// These are set by params to the node.
std::string g_dir;
double g_hfactor;
double g_wfactor;
bool g_subbg;
double g_bgcoeff;

// For filename and stats.
size_t g_counter = 0;

template<typename T>
std::string to_s(const T& v)
{
    std::ostringstream oss;
    oss << v;
    return oss.str();
}

void dump_32FC1(std::string fname, cv::Mat img)
{
    if(img.type() != CV_32FC1) {
        ROS_ERROR("Image not of 32FC1 type, but %d", img.type());
        return;
    }

    std::ofstream of(fname);
    if(!of) {
        ROS_ERROR("Error writing depth image %s", (fname + "_d.csv").c_str());
        return;
    }

    for(size_t iy = 0 ; iy < img.rows ; ++iy) {
        float *py = img.ptr<float>(iy);
        for(size_t ix = 0 ; ix < img.cols ; ++ix, ++py) {
            of << *py;
            if(ix + 1 < img.cols)
                of << " ";
        }
        of << std::endl;
    }
}

void cb(const TrackedPersons2d::ConstPtr& t2d, const sensor_msgs::ImageConstPtr& rgb, const sensor_msgs::ImageConstPtr& d)
{
    size_t ndet = t2d->boxes.size();
    if(ndet == 0)
        return;

    cv_bridge::CvImageConstPtr cv_rgb;
    cv_bridge::CvImageConstPtr cv_d;
    try {
        // TODO: Careful, the above is RGB but opencv "thinks" in BGR!
        //cv_rgb = cv_bridge::toCvShare(rgb);
        cv_rgb = cv_bridge::toCvCopy(rgb, sensor_msgs::image_encodings::BGR8);
        cv_d = cv_bridge::toCvShare(d);
    } catch(const cv_bridge::Exception& e) {
        ROS_ERROR("Couldn't convert image: %s", e.what());
        return;
    }

    for(size_t idet = 0 ; idet < ndet ; ++idet) {
        const TrackedPerson2d& p2d = t2d->boxes[idet];
        // Compute this detection's cut-out boxes.
        uint32_t h = g_hfactor > 0 ? std::min(uint32_t(g_hfactor * p2d.w), p2d.h)
                                   : p2d.h;
        uint32_t w = uint32_t(g_wfactor * p2d.w);
        int32_t x = p2d.x + int32_t(.5*(1. - g_wfactor)*p2d.w);
        cv::Rect bbox(x, p2d.y, w, h);
        cv::Rect bbox_rgb = bbox & cv::Rect(0, 0, cv_rgb->image.cols, cv_rgb->image.rows);
        cv::Rect bbox_d = bbox & cv::Rect(0, 0, cv_d->image.cols, cv_d->image.rows);

        // Cut-out and optionally subtract background.
        cv::Mat rgbimg(cv_rgb->image, bbox_rgb);
        cv::Mat dimg(cv_d->image, bbox_d);
        if(g_subbg)
            subtractbg(rgbimg, dimg, 1.0, g_bgcoeff);

        // Save.
        // TODO: setw
        std::string fname = g_dir + "/" + to_s(p2d.track_id) + "_" + to_s(rgb->header.seq);
        if(!cv::imwrite(fname + "_rgb.png", rgbimg)) {
            ROS_ERROR("Error writing image %s", (fname + "_rgb.png").c_str());
        }

        dump_32FC1(fname + "_d.csv", dimg);

        std::cout << "\rDumping: " << rgb->header.seq << std::flush;
        g_counter++;
    }
}

int main(int argc, char* argv[])
{
    ros::init(argc, argv, "dump_tracks");
    ros::NodeHandle nh;

    ros::NodeHandle nh_("~");
    nh_.param("dir", g_dir, std::string("."));
    nh_.param("hfactor", g_hfactor, -1.0);
    nh_.param("wfactor", g_wfactor,  1.0);
    nh_.param("bgcoeff", g_bgcoeff, 0.5);
    g_wfactor = g_wfactor <= 0 ? 1.0 : g_wfactor;
    nh_.param("subbg", g_subbg, false);

    // equivalend to Python's expanduser(dir)
    wordexp_t exp_result;
    if(0 !=wordexp(g_dir.c_str(), &exp_result, WRDE_NOCMD | WRDE_SHOWERR))
        return 1;
    g_dir = exp_result.we_wordv[0];
    wordfree(&exp_result);

    if(mkpath(g_dir.c_str()) != 0) {
        perror("Couldn't create output directory");
        return 2;
    }
    // TODO: warn if non-emtpy?

    ROS_INFO("Dumping %s%s-bodies into %s", g_subbg ? "bg-subtracted, " : " ", g_hfactor > 0 ? to_s(g_hfactor).c_str() : "full", g_dir.c_str());

    std::string topic_t2d, topic_rgb, topic_d;
    nh_.param("track2d", topic_t2d, std::string("/upper_body_detector/detections"));
    nh_.param("rgb", topic_rgb, std::string("/head_xtion/rgb/image_rect_color"));
    nh_.param("d", topic_d, std::string("/head_xtion/depth/image_rect_meters"));

    message_filters::Subscriber<TrackedPersons2d> sub_t2d(nh_, topic_t2d.c_str(), 1);

    image_transport::ImageTransport it(nh_);
    image_transport::SubscriberFilter sub_rgb(it, topic_rgb.c_str(), 1);
    image_transport::SubscriberFilter sub_d(it, topic_d.c_str(), 1);

    typedef message_filters::sync_policies::ApproximateTime<TrackedPersons2d, sensor_msgs::Image, sensor_msgs::Image> SyncType;
    const SyncType sync_policy(20);

    message_filters::Synchronizer<SyncType> sync(sync_policy, sub_t2d, sub_rgb, sub_d);
    sync.registerCallback(boost::bind(&cb, _1, _2, _3));

    ros::spin();

    // TODO: ROS_INFO isn't being output after the spin is done?
    //ROS_INFO("Dumped a total of %d track frames.", g_counter);
    std::cout << "Dumped a total of " << g_counter << " track frames." << std::endl;
    return 0;
}
<commit_msg>Nicer dumper output.<commit_after>#include <iostream>
#include <fstream>
#include <sstream>

#include <wordexp.h>

#include <ros/ros.h>

#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>
#include <image_transport/image_transport.h>
#include <image_transport/subscriber_filter.h>
#include <sensor_msgs/image_encodings.h>

#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>

#include <spencer_tracking_msgs/TrackedPersons2d.h>

extern "C" int mkpath(const char *path);
void subtractbg(cv::Mat &rgb, const cv::Mat &d, float thresh=1.0f, float bgcoeff=0.5f);

using namespace spencer_tracking_msgs;

// These are set by params to the node.
std::string g_dir;
double g_hfactor;
double g_wfactor;
bool g_subbg;
double g_bgcoeff;

// For filename and stats.
size_t g_counter = 0;

template<typename T>
std::string to_s(const T& v)
{
    std::ostringstream oss;
    oss << v;
    return oss.str();
}

void dump_32FC1(std::string fname, cv::Mat img)
{
    if(img.type() != CV_32FC1) {
        ROS_ERROR("Image not of 32FC1 type, but %d", img.type());
        return;
    }

    std::ofstream of(fname);
    if(!of) {
        ROS_ERROR("Error writing depth image %s", (fname + "_d.csv").c_str());
        return;
    }

    for(size_t iy = 0 ; iy < img.rows ; ++iy) {
        float *py = img.ptr<float>(iy);
        for(size_t ix = 0 ; ix < img.cols ; ++ix, ++py) {
            of << *py;
            if(ix + 1 < img.cols)
                of << " ";
        }
        of << std::endl;
    }
}

void cb(const TrackedPersons2d::ConstPtr& t2d, const sensor_msgs::ImageConstPtr& rgb, const sensor_msgs::ImageConstPtr& d)
{
    size_t ndet = t2d->boxes.size();
    if(ndet == 0)
        return;

    cv_bridge::CvImageConstPtr cv_rgb;
    cv_bridge::CvImageConstPtr cv_d;
    try {
        // TODO: Careful, the above is RGB but opencv "thinks" in BGR!
        //cv_rgb = cv_bridge::toCvShare(rgb);
        cv_rgb = cv_bridge::toCvCopy(rgb, sensor_msgs::image_encodings::BGR8);
        cv_d = cv_bridge::toCvShare(d);
    } catch(const cv_bridge::Exception& e) {
        ROS_ERROR("Couldn't convert image: %s", e.what());
        return;
    }

    for(size_t idet = 0 ; idet < ndet ; ++idet) {
        const TrackedPerson2d& p2d = t2d->boxes[idet];
        // Compute this detection's cut-out boxes.
        uint32_t h = g_hfactor > 0 ? std::min(uint32_t(g_hfactor * p2d.w), p2d.h)
                                   : p2d.h;
        uint32_t w = uint32_t(g_wfactor * p2d.w);
        int32_t x = p2d.x + int32_t(.5*(1. - g_wfactor)*p2d.w);
        cv::Rect bbox(x, p2d.y, w, h);
        cv::Rect bbox_rgb = bbox & cv::Rect(0, 0, cv_rgb->image.cols, cv_rgb->image.rows);
        cv::Rect bbox_d = bbox & cv::Rect(0, 0, cv_d->image.cols, cv_d->image.rows);

        // Cut-out and optionally subtract background.
        cv::Mat rgbimg(cv_rgb->image, bbox_rgb);
        cv::Mat dimg(cv_d->image, bbox_d);
        if(g_subbg)
            subtractbg(rgbimg, dimg, 1.0, g_bgcoeff);

        // Save.
        // TODO: setw
        std::string fname = g_dir + "/" + to_s(p2d.track_id) + "_" + to_s(rgb->header.seq);
        if(!cv::imwrite(fname + "_rgb.png", rgbimg)) {
            ROS_ERROR("Error writing image %s", (fname + "_rgb.png").c_str());
        }

        dump_32FC1(fname + "_d.csv", dimg);

        std::cout << "\rDump #" << g_counter << ": track " << p2d.track_id << "@seq" << rgb->header.seq << std::flush;
        g_counter++;
    }
}

int main(int argc, char* argv[])
{
    ros::init(argc, argv, "dump_tracks");
    ros::NodeHandle nh;

    ros::NodeHandle nh_("~");
    nh_.param("dir", g_dir, std::string("."));
    nh_.param("hfactor", g_hfactor, -1.0);
    nh_.param("wfactor", g_wfactor,  1.0);
    nh_.param("bgcoeff", g_bgcoeff, 0.5);
    g_wfactor = g_wfactor <= 0 ? 1.0 : g_wfactor;
    nh_.param("subbg", g_subbg, false);

    // equivalend to Python's expanduser(dir)
    wordexp_t exp_result;
    if(0 !=wordexp(g_dir.c_str(), &exp_result, WRDE_NOCMD | WRDE_SHOWERR))
        return 1;
    g_dir = exp_result.we_wordv[0];
    wordfree(&exp_result);

    if(mkpath(g_dir.c_str()) != 0) {
        perror("Couldn't create output directory");
        return 2;
    }
    // TODO: warn if non-emtpy?

    ROS_INFO("Dumping %s%s-bodies into %s", g_subbg ? "bg-subtracted, " : " ", g_hfactor > 0 ? to_s(g_hfactor).c_str() : "full", g_dir.c_str());

    std::string topic_t2d, topic_rgb, topic_d;
    nh_.param("track2d", topic_t2d, std::string("/upper_body_detector/detections"));
    nh_.param("rgb", topic_rgb, std::string("/head_xtion/rgb/image_rect_color"));
    nh_.param("d", topic_d, std::string("/head_xtion/depth/image_rect_meters"));

    message_filters::Subscriber<TrackedPersons2d> sub_t2d(nh_, topic_t2d.c_str(), 1);

    image_transport::ImageTransport it(nh_);
    image_transport::SubscriberFilter sub_rgb(it, topic_rgb.c_str(), 1);
    image_transport::SubscriberFilter sub_d(it, topic_d.c_str(), 1);

    typedef message_filters::sync_policies::ApproximateTime<TrackedPersons2d, sensor_msgs::Image, sensor_msgs::Image> SyncType;
    const SyncType sync_policy(20);

    message_filters::Synchronizer<SyncType> sync(sync_policy, sub_t2d, sub_rgb, sub_d);
    sync.registerCallback(boost::bind(&cb, _1, _2, _3));

    ros::spin();

    // TODO: ROS_INFO isn't being output after the spin is done?
    //ROS_INFO("Dumped a total of %d track frames.", g_counter);
    std::cout << "Dumped a total of " << g_counter << " track frames." << std::endl;
    return 0;
}
<|endoftext|>