<commit_before>/*
 * Animated GIFs Display Code for 32x32 RGB LED Matrix
 *
 * This file contains code to decompress the LZW encoded animated GIF data
 *
 * Written by: Craig A. Lindley, Fabrice Bellard and Steven A. Bennett
 * See my book, "Practical Image Processing in C", John Wiley & Sons, Inc.
 *
 * Version: 1.2
 * Last Update: 07/04/2014
 *
 * Copyright (c) 2014 Craig A. Lindley
 *
 * 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 "SmartMatrix.h"

extern SmartMatrix matrix;

extern int lsdWidth;
extern int lsdHeight;
extern int lsdBackgroundIndex;

// Table based image attributes
extern int tbiImageX;
extern int tbiImageY;
extern int tbiWidth;
extern int tbiHeight;
extern boolean tbiInterlaced;

extern int frameDelay;
extern int transparentColorIndex;
extern int disposalMethod;

const int WIDTH  = 32;
const int HEIGHT = 32;

// RGB data structure
typedef struct {
    byte Red;
    byte Green;
    byte Blue;
}
RGB;

extern RGB palette[];

// LZW constants
// NOTE: LZW_MAXBITS set to 10 to save memory
#define LZW_MAXBITS    10
#define LZW_SIZTABLE  (1 << LZW_MAXBITS)

// Masks for 0 .. 16 bits
unsigned int mask[17] = {
    0x0000, 0x0001, 0x0003, 0x0007,
    0x000F, 0x001F, 0x003F, 0x007F,
    0x00FF, 0x01FF, 0x03FF, 0x07FF,
    0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
    0xFFFF
};

// LZW variables
byte *pbuf;
int bbits;
int bbuf;
int cursize;                // The current code size
int curmask;
int codesize;
int clear_code;
int end_code;
int newcodes;               // First available code
int top_slot;               // Highest code for current size
int extra_slot;
int slot;                   // Last read code
int fc, oc;
int bs;                     // Current buffer size for GIF
byte *sp;
byte stack  [LZW_SIZTABLE];
byte suffix [LZW_SIZTABLE];
unsigned int prefix [LZW_SIZTABLE];

// Buffer image data is decoded into
byte imageData[1024];

// Backup image data buffer for saving portions of image disposal method == 3
byte imageDataBU[1024];

// Initialize LZW decoder
//   csize initial code size in bits
//   buf input data
void lzw_decode_init (int csize, byte *buf) {

    // Initialize read buffer variables
    pbuf = buf;
    bbuf = 0;
    bbits = 0;
    bs = 0;

    // Initialize decoder variables
    codesize = csize;
    cursize = codesize + 1;
    curmask = mask[cursize];
    top_slot = 1 << cursize;
    clear_code = 1 << codesize;
    end_code = clear_code + 1;
    slot = newcodes = clear_code + 2;
    oc = fc = -1;
    sp = stack;
}

//  Get one code of given number of bits from stream
int lzw_get_code() {

    while (bbits < cursize) {
        if (!bs) {
            bs = *pbuf++;
        }
        bbuf |= (*pbuf++) << bbits;
        bbits += 8;
        bs--;
    }
    int c = bbuf;
    bbuf >>= cursize;
    bbits -= cursize;
    return c & curmask;
}

// Decode given number of bytes
//   buf 8 bit output buffer
//   len number of pixels to decode
//   returns the number of bytes decoded
int lzw_decode(byte *buf, int len) {
    int l, c, code;

    if (end_code < 0) {
        return 0;
    }
    l = len;

    for (;;) {
        while (sp > stack) {
            *buf++ = *(--sp);
            if ((--l) == 0) {
                goto the_end;
            }
        }
        c = lzw_get_code();
        if (c == end_code) {
            break;

        }
        else if (c == clear_code) {
            cursize = codesize + 1;
            curmask = mask[cursize];
            slot = newcodes;
            top_slot = 1 << cursize;
            fc= oc= -1;

        }
        else	{

            code = c;
            if ((code == slot) && (fc >= 0)) {
                *sp++ = fc;
                code = oc;
            }
            else if (code >= slot) {
                break;
            }
            while (code >= newcodes) {
                *sp++ = suffix[code];
                code = prefix[code];
            }
            *sp++ = code;
            if ((slot < top_slot) && (oc >= 0)) {
                suffix[slot] = code;
                prefix[slot++] = oc;
            }
            fc = code;
            oc = c;
            if (slot >= top_slot) {
                if (cursize < LZW_MAXBITS) {
                    top_slot <<= 1;
                    curmask = mask[++cursize];
                }
            }
        }
    }
    end_code = -1;
the_end:
    return len - l;
}

// Decompress LZW data and display animation frame
void decompressAndDisplayFrame() {

    // Each pixel of image is 8 bits and is an index into the palette

        // How the image is decoded depends upon whether it is interlaced or not
    // Decode the interlaced LZW data into the image buffer
    if (tbiInterlaced) {
        // Decode every 8th line starting at line 0
        for (int line = 0; line < tbiHeight; line += 8) {
            lzw_decode(imageData + (line * WIDTH), tbiWidth);
        }
        // Decode every 8th line starting at line 4
        for (int line = 4; line < tbiHeight; line += 8) {
            lzw_decode(imageData + (line * WIDTH), tbiWidth);
        }
        // Decode every 4th line starting at line 2
        for (int line = 2; line < tbiHeight; line += 4) {
            lzw_decode(imageData + (line * WIDTH), tbiWidth);
        }
        // Decode every 2nd line starting at line 1
        for (int line = 1; line < tbiHeight; line += 2) {
            lzw_decode(imageData + (line * WIDTH), tbiWidth);
        }
    }
    else	{
        // Decode the non interlaced LZW data into the image data buffer
        for (int line = tbiImageY; line < tbiHeight + tbiImageY; line++) {
            lzw_decode(imageData  + (line * WIDTH) + tbiImageX, tbiWidth);
        }
    }

    // Image data is decompressed, now display portion of image affected by frame

    rgb24 color;
    int yOffset, pixel;
    for (int y = tbiImageY; y < tbiHeight + tbiImageY; y++) {
        yOffset = y * WIDTH;
        for (int x = tbiImageX; x < tbiWidth + tbiImageX; x++) {
            // Get the next pixel
            pixel = imageData[yOffset + x];

            // Check pixel transparency
            if (pixel == transparentColorIndex) {
                continue;
            }

            // Pixel not transparent so get color from palette
            color.red   = palette[pixel].Red;
            color.green = palette[pixel].Green;
            color.blue  = palette[pixel].Blue;

            // Draw the pixel
            matrix.drawPixel(x, y, color);
        }
    }
    // Make animation frame visible
    matrix.swapBuffers();
}

<commit_msg>Fix to allow drawing interlaced frames that are offset from (0,0)<commit_after>/*
 * Animated GIFs Display Code for 32x32 RGB LED Matrix
 *
 * This file contains code to decompress the LZW encoded animated GIF data
 *
 * Written by: Craig A. Lindley, Fabrice Bellard and Steven A. Bennett
 * See my book, "Practical Image Processing in C", John Wiley & Sons, Inc.
 *
 * Version: 1.2
 * Last Update: 07/04/2014
 *
 * Copyright (c) 2014 Craig A. Lindley
 *
 * 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 "SmartMatrix.h"

extern SmartMatrix matrix;

extern int lsdWidth;
extern int lsdHeight;
extern int lsdBackgroundIndex;

// Table based image attributes
extern int tbiImageX;
extern int tbiImageY;
extern int tbiWidth;
extern int tbiHeight;
extern boolean tbiInterlaced;

extern int frameDelay;
extern int transparentColorIndex;
extern int disposalMethod;

const int WIDTH  = 32;
const int HEIGHT = 32;

// RGB data structure
typedef struct {
    byte Red;
    byte Green;
    byte Blue;
}
RGB;

extern RGB palette[];

// LZW constants
// NOTE: LZW_MAXBITS set to 10 to save memory
#define LZW_MAXBITS    10
#define LZW_SIZTABLE  (1 << LZW_MAXBITS)

// Masks for 0 .. 16 bits
unsigned int mask[17] = {
    0x0000, 0x0001, 0x0003, 0x0007,
    0x000F, 0x001F, 0x003F, 0x007F,
    0x00FF, 0x01FF, 0x03FF, 0x07FF,
    0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
    0xFFFF
};

// LZW variables
byte *pbuf;
int bbits;
int bbuf;
int cursize;                // The current code size
int curmask;
int codesize;
int clear_code;
int end_code;
int newcodes;               // First available code
int top_slot;               // Highest code for current size
int extra_slot;
int slot;                   // Last read code
int fc, oc;
int bs;                     // Current buffer size for GIF
byte *sp;
byte stack  [LZW_SIZTABLE];
byte suffix [LZW_SIZTABLE];
unsigned int prefix [LZW_SIZTABLE];

// Buffer image data is decoded into
byte imageData[1024];

// Backup image data buffer for saving portions of image disposal method == 3
byte imageDataBU[1024];

// Initialize LZW decoder
//   csize initial code size in bits
//   buf input data
void lzw_decode_init (int csize, byte *buf) {

    // Initialize read buffer variables
    pbuf = buf;
    bbuf = 0;
    bbits = 0;
    bs = 0;

    // Initialize decoder variables
    codesize = csize;
    cursize = codesize + 1;
    curmask = mask[cursize];
    top_slot = 1 << cursize;
    clear_code = 1 << codesize;
    end_code = clear_code + 1;
    slot = newcodes = clear_code + 2;
    oc = fc = -1;
    sp = stack;
}

//  Get one code of given number of bits from stream
int lzw_get_code() {

    while (bbits < cursize) {
        if (!bs) {
            bs = *pbuf++;
        }
        bbuf |= (*pbuf++) << bbits;
        bbits += 8;
        bs--;
    }
    int c = bbuf;
    bbuf >>= cursize;
    bbits -= cursize;
    return c & curmask;
}

// Decode given number of bytes
//   buf 8 bit output buffer
//   len number of pixels to decode
//   returns the number of bytes decoded
int lzw_decode(byte *buf, int len) {
    int l, c, code;

    if (end_code < 0) {
        return 0;
    }
    l = len;

    for (;;) {
        while (sp > stack) {
            *buf++ = *(--sp);
            if ((--l) == 0) {
                goto the_end;
            }
        }
        c = lzw_get_code();
        if (c == end_code) {
            break;

        }
        else if (c == clear_code) {
            cursize = codesize + 1;
            curmask = mask[cursize];
            slot = newcodes;
            top_slot = 1 << cursize;
            fc= oc= -1;

        }
        else	{

            code = c;
            if ((code == slot) && (fc >= 0)) {
                *sp++ = fc;
                code = oc;
            }
            else if (code >= slot) {
                break;
            }
            while (code >= newcodes) {
                *sp++ = suffix[code];
                code = prefix[code];
            }
            *sp++ = code;
            if ((slot < top_slot) && (oc >= 0)) {
                suffix[slot] = code;
                prefix[slot++] = oc;
            }
            fc = code;
            oc = c;
            if (slot >= top_slot) {
                if (cursize < LZW_MAXBITS) {
                    top_slot <<= 1;
                    curmask = mask[++cursize];
                }
            }
        }
    }
    end_code = -1;
the_end:
    return len - l;
}

// Decompress LZW data and display animation frame
void decompressAndDisplayFrame() {

    // Each pixel of image is 8 bits and is an index into the palette

        // How the image is decoded depends upon whether it is interlaced or not
    // Decode the interlaced LZW data into the image buffer
    if (tbiInterlaced) {
        // Decode every 8th line starting at line 0
        for (int line = tbiImageY + 0; line < tbiHeight + tbiImageY; line += 8) {
            lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth);
        }
        // Decode every 8th line starting at line 4
        for (int line = tbiImageY + 4; line < tbiHeight + tbiImageY; line += 8) {
            lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth);
        }
        // Decode every 4th line starting at line 2
        for (int line = tbiImageY + 2; line < tbiHeight + tbiImageY; line += 4) {
            lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth);
        }
        // Decode every 2nd line starting at line 1
        for (int line = tbiImageY + 1; line < tbiHeight + tbiImageY; line += 2) {
            lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth);
        }
    }
    else	{
        // Decode the non interlaced LZW data into the image data buffer
        for (int line = tbiImageY; line < tbiHeight + tbiImageY; line++) {
            lzw_decode(imageData  + (line * WIDTH) + tbiImageX, tbiWidth);
        }
    }

    // Image data is decompressed, now display portion of image affected by frame

    rgb24 color;
    int yOffset, pixel;
    for (int y = tbiImageY; y < tbiHeight + tbiImageY; y++) {
        yOffset = y * WIDTH;
        for (int x = tbiImageX; x < tbiWidth + tbiImageX; x++) {
            // Get the next pixel
            pixel = imageData[yOffset + x];

            // Check pixel transparency
            if (pixel == transparentColorIndex) {
                continue;
            }

            // Pixel not transparent so get color from palette
            color.red   = palette[pixel].Red;
            color.green = palette[pixel].Green;
            color.blue  = palette[pixel].Blue;

            // Draw the pixel
            matrix.drawPixel(x, y, color);
        }
    }
    // Make animation frame visible
    matrix.swapBuffers();
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2008, Arvid Norberg
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 the author 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.

*/

#include <libtorrent/enum_net.hpp>
#include <libtorrent/socket.hpp>
#include <libtorrent/broadcast_socket.hpp>
#include <vector>
#include <iomanip>
#include <iostream>

using namespace libtorrent;

int main()
{
	io_service ios;
	error_code ec;

	address local = guess_local_address(ios);
	std::cout << "Local address: " << local << std::endl;

	address def_gw = get_default_gateway(ios, ec);
	if (ec)
	{
		std::cerr << ec.message() << std::endl;
		return 1;
	}
	std::cout << "Default gateway: " << def_gw << std::endl;

	std::cout << "=========== Routes ===========\n";
	std::vector<ip_route> routes = enum_routes(ios, ec);
	if (ec)
	{
		std::cerr << ec.message() << std::endl;
		return 1;
	}

	std::cout << std::setiosflags(std::ios::left)
		<< std::setw(18) << "destination"
		<< std::setw(18) << "netmask"
		<< std::setw(35) << "gateway"
		<< "interface name"
		<< std::endl;

	for (std::vector<ip_route>::const_iterator i = routes.begin()
		, end(routes.end()); i != end; ++i)
	{
		std::cout << std::setiosflags(std::ios::left)
			<< std::setw(18) << i->destination
			<< std::setw(18) << i->netmask
			<< std::setw(35) << i->gateway
			<< i->name
			<< std::endl;
	}

	std::cout << "========= Interfaces =========\n";

	std::vector<ip_interface> const& net = enum_net_interfaces(ios, ec);
	if (ec)
	{
		std::cerr << ec.message() << std::endl;
		return 1;
	}

	std::cout << std::setiosflags(std::ios::left)
		<< std::setw(35) << "address"
		<< std::setw(18) << "netmask"
		<< std::setw(18) << "name"
		<< "flags"
		<< std::endl;

	for (std::vector<ip_interface>::const_iterator i = net.begin()
		, end(net.end()); i != end; ++i)
	{
		std::cout << std::setiosflags(std::ios::left)
			<< std::setw(35) << i->interface_address
			<< std::setw(18) << i->netmask
			<< std::setw(18) << i->name
			<< (is_multicast(i->interface_address)?"multicast ":"")
			<< (is_local(i->interface_address)?"local ":"")
			<< (is_loopback(i->interface_address)?"loopback ":"")

			<< std::endl;
	}

}

<commit_msg>replaced iostream in example<commit_after>/*

Copyright (c) 2008, Arvid Norberg
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 the author 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.

*/

#include <stdio.h>
#include <libtorrent/enum_net.hpp>
#include <libtorrent/socket.hpp>
#include <libtorrent/broadcast_socket.hpp>

using namespace libtorrent;

int main()
{
	io_service ios;
	error_code ec;
	address local = guess_local_address(ios);
	printf("Local address: %s\n", local.to_string(ec).c_str());

	address def_gw = get_default_gateway(ios, ec);
	if (ec)
	{
		fprintf(stderr, "%s\n", ec.message().c_str());
		return 1;
	}

	printf("Default gateway: %s\n", def_gw.to_string(ec).c_str());

	printf("=========== Routes ===========\n");
	std::vector<ip_route> routes = enum_routes(ios, ec);
	if (ec)
	{
		printf("%s\n", ec.message().c_str());
		return 1;
	}

	printf("%-18s%-18s%-35sinterface name\n", "destination", "network", "gateway");

	for (std::vector<ip_route>::const_iterator i = routes.begin()
		, end(routes.end()); i != end; ++i)
	{
		printf("%-18s%-18s%-35s%s\n"
			, i->destination.to_string(ec).c_str()
			, i->netmask.to_string(ec).c_str()
			, i->gateway.to_string(ec).c_str()
			, i->name);
	}

	printf("========= Interfaces =========\n");

	std::vector<ip_interface> const& net = enum_net_interfaces(ios, ec);
	if (ec)
	{
		printf("%s\n", ec.message().c_str());
		return 1;
	}

	printf("%-18s%-18s%-35sflags\n", "address", "netmask", "name");

	for (std::vector<ip_interface>::const_iterator i = net.begin()
		, end(net.end()); i != end; ++i)
	{
		printf("%-18s%-18s%-35s%s%s%s\n"
			, i->interface_address.to_string(ec).c_str()
			, i->netmask.to_string(ec).c_str()
			, i->name
			, (is_multicast(i->interface_address)?"multicast ":"")
			, (is_local(i->interface_address)?"local ":"")
			, (is_loopback(i->interface_address)?"loopback ":"")
			);
	}
}

<|endoftext|>


<commit_before>// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2015 Intel Corporation. All Rights Reserved.

#pragma once

#define GLFW_INCLUDE_GLU
#include <GLFW/glfw3.h>

#include <string>
#include <sstream>
#include <iostream>

//////////////////////////////
// Basic Data Types         //
//////////////////////////////

struct float3 { float x, y, z; };
struct float2 { float x, y; };

struct rect
{
    float x, y;
    float w, h;

    // Create new rect within original boundaries with give aspect ration
    rect adjust_ratio(float2 size) const
    {
        auto H = static_cast<float>(h), W = static_cast<float>(h) * size.x / size.y;
        if (W > w)
        {
            auto scale = w / W;
            W *= scale;
            H *= scale;
        }

        return{ x + (w - W) / 2, y + (h - H) / 2, W, H };
    }
};

//////////////////////////////
// Simple font loading code //
//////////////////////////////

#include "../third-party/stb_easy_font.h"

inline void draw_text(int x, int y, const char * text)
{
    char buffer[60000]; // ~300 chars
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, GL_FLOAT, 16, buffer);
    glDrawArrays(GL_QUADS, 0, 4 * stb_easy_font_print((float)x, (float)(y - 7), (char *)text, nullptr, buffer, sizeof(buffer)));
    glDisableClientState(GL_VERTEX_ARRAY);
}

////////////////////////
// Image display code //
////////////////////////
class texture
{
public:
    void render(const rs2::frameset& frames, int window_width, int window_height)
    {
        std::vector<rs2::video_frame> supported_frames;
        for (auto f : frames)
        {
            if (can_render(f))
                supported_frames.push_back(f);
        }
        if (supported_frames.empty())
            return;

        std::sort(supported_frames.begin(), supported_frames.end(), [](rs2::frame first, rs2::frame second)
            { return first.get_profile().stream_type() < second.get_profile().stream_type();  });

        auto image_grid = calc_grid(float2{ (float)window_width, (float)window_height }, supported_frames);

        int image_index = 0;
        for (auto f : supported_frames)
        {
            upload(f);
            show(image_grid.at(image_index));
            image_index++;
        }
    }

    void render(const rs2::video_frame& frame, const rect& r)
    {
        upload(frame);
        show(r.adjust_ratio({ float(width), float(height) }));
    }

    void upload(const rs2::video_frame& frame)
    {
        if (!frame) return;

        if (!gl_handle)
            glGenTextures(1, &gl_handle);
        GLenum err = glGetError();

        auto format = frame.get_profile().format();
        width = frame.get_width();
        height = frame.get_height();
        stream = frame.get_profile().stream_type();

        glBindTexture(GL_TEXTURE_2D, gl_handle);

        switch (format)
        {
        case RS2_FORMAT_RGB8:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, frame.get_data());
            break;
        case RS2_FORMAT_RGBA8:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame.get_data());
            break;
        case RS2_FORMAT_Y8:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame.get_data());
            break;
        default:
            throw std::runtime_error("The requested format is not supported by this demo!");
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
    }

    GLuint get_gl_handle() { return gl_handle; }

    void show(const rect& r) const
    {
        if (!gl_handle)
            return;

        glBindTexture(GL_TEXTURE_2D, gl_handle);
        glEnable(GL_TEXTURE_2D);
        glBegin(GL_QUAD_STRIP);
        glTexCoord2f(0.f, 1.f); glVertex2f(r.x, r.y + r.h);
        glTexCoord2f(0.f, 0.f); glVertex2f(r.x, r.y);
        glTexCoord2f(1.f, 1.f); glVertex2f(r.x + r.w, r.y + r.h);
        glTexCoord2f(1.f, 0.f); glVertex2f(r.x + r.w, r.y);
        glEnd();
        glDisable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, 0);

        draw_text((int)r.x + 15, (int)r.y + 20, rs2_stream_to_string(stream));
    }

private:
    GLuint gl_handle = 0;
    int width = 0;
    int height = 0;
    rs2_stream stream = RS2_STREAM_ANY;

    bool can_render(const rs2::frame& f) const
    {
        auto format = f.get_profile().format();
        switch (format)
        {
        case RS2_FORMAT_RGB8:
        case RS2_FORMAT_RGBA8:
        case RS2_FORMAT_Y8:
            return true;
        default:
            return false;
        }
    }

    rect calc_grid(float2 window, int streams)
    {
        float ratio = window.x / window.y;
        auto x = sqrt(ratio * (float)streams);
        auto y = (float)streams / x;
        auto w = round(x);
        auto h = round(y);
        while (w*h > streams)
            h > w ? h-- : w--;
        while (w*h < streams)
            h > w ? w++ : h++;
        auto new_w = round(window.x / w);
        auto new_h = round(window.y / h);
        return rect{ w, h, new_w, new_h}; //column count, line count, cell width cell height
    }

    std::vector<rect> calc_grid(float2 window, std::vector<rs2::video_frame>& frames)
    {
        auto grid = calc_grid(window, frames.size());

        int index = 0;
        std::vector<rect> rv;
        int curr_line = -1;
        for (auto f  : frames)
        {
            auto mod = index % (int)grid.x;
            auto fw = (float)frames[index].get_width();
            auto fh = (float)frames[index].get_height();

            float cell_x_postion = (float)(mod * grid.w);
            if (mod == 0) curr_line++;
            float cell_y_position = curr_line * grid.h;

            auto r = rect{ cell_x_postion, cell_y_position, grid.w, grid.h };
            rv.push_back(r.adjust_ratio(float2{ fw, fh }));
            index++;
        }

        return rv;
    }
};

class window
{
public:
    std::function<void(bool)>           on_left_mouse = [](bool) {};
    std::function<void(double, double)> on_mouse_scroll = [](double, double) {};
    std::function<void(double, double)> on_mouse_move = [](double, double) {};
    std::function<void(int)>            on_key_release = [](int) {};

    window(int width, int height, const char* title)
        : _width(width), _height(height)
    {
        glfwInit();
        win = glfwCreateWindow(width, height, title, nullptr, nullptr);
        if (!win)
            throw std::runtime_error("Could not open OpenGL window, please check your graphic drivers or use the textual SDK tools");
        glfwMakeContextCurrent(win);

        glfwSetWindowUserPointer(win, this);
        glfwSetMouseButtonCallback(win, [](GLFWwindow * win, int button, int action, int mods)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            if (button == 0) s->on_left_mouse(action == GLFW_PRESS);
        });

        glfwSetScrollCallback(win, [](GLFWwindow * win, double xoffset, double yoffset)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            s->on_mouse_scroll(xoffset, yoffset);
        });

        glfwSetCursorPosCallback(win, [](GLFWwindow * win, double x, double y)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            s->on_mouse_move(x, y);
        });

        glfwSetKeyCallback(win, [](GLFWwindow * win, int key, int scancode, int action, int mods)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            if (0 == action) // on key release
            {
                s->on_key_release(key);
            }
        });
    }

    float width() const { return float(_width); }
    float height() const { return float(_height); }

    operator bool()
    {
        glPopMatrix();
        glfwSwapBuffers(win);

        auto res = !glfwWindowShouldClose(win);

        glfwPollEvents();
        glfwGetFramebufferSize(win, &_width, &_height);

        // Clear the framebuffer
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(0, 0, _width, _height);

        // Draw the images
        glPushMatrix();
        glfwGetWindowSize(win, &_width, &_height);
        glOrtho(0, _width, _height, 0, -1, +1);

        return res;
    }

    ~window()
    {
        glfwDestroyWindow(win);
        glfwTerminate();
    }

    operator GLFWwindow*() { return win; }

private:
    GLFWwindow * win;
    int _width, _height;
};

// Struct for managing rotation of pointcloud view
struct glfw_state {
    glfw_state() : yaw(15.0), pitch(15.0), last_x(0.0), last_y(0.0),
        ml(false), offset_x(2.f), offset_y(2.f), tex() {}
    double yaw;
    double pitch;
    double last_x;
    double last_y;
    bool ml;
    float offset_x;
    float offset_y;
    texture tex;
};

// Handles all the OpenGL calls needed to display the point cloud
void draw_pointcloud(float width, float height, glfw_state& app_state, rs2::points& points)
{
    if (!points)
        return;

    // OpenGL commands that prep screen for the pointcloud
    glPopMatrix();
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    glClearColor(153.f / 255, 153.f / 255, 153.f / 255, 1);
    glClear(GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    gluPerspective(60, width / height, 0.01f, 10.0f);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    gluLookAt(0, 0, 0, 0, 0, 1, 0, -1, 0);

    glTranslatef(0, 0, +0.5f + app_state.offset_y*0.05f);
    glRotated(app_state.pitch, 1, 0, 0);
    glRotated(app_state.yaw, 0, 1, 0);
    glTranslatef(0, 0, -0.5f);

    glPointSize(width / 640);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, app_state.tex.get_gl_handle());
    float tex_border_color[] = { 0.8f, 0.8f, 0.8f, 0.8f };
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, tex_border_color);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F); // GL_CLAMP_TO_EDGE
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F); // GL_CLAMP_TO_EDGE
    glBegin(GL_POINTS);

    /* this segment actually prints the pointcloud */
    auto vertices = points.get_vertices();              // get vertices
    auto tex_coords = points.get_texture_coordinates(); // and texture coordinates
    for (int i = 0; i < points.size(); i++)
    {
        if (vertices[i].z)
        {
            // upload the point and texture coordinates only for points we have depth data for
            glVertex3fv(vertices[i]);
            glTexCoord2fv(tex_coords[i]);
        }
    }

    // OpenGL cleanup
    glEnd();
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glPopAttrib();
    glPushMatrix();
}

// Registers the state variable and callbacks to allow mouse control of the pointcloud
void register_glfw_callbacks(window& app, glfw_state& app_state)
{
    app.on_left_mouse = [&](bool pressed)
    {
        app_state.ml = pressed;
    };

    app.on_mouse_scroll = [&](double xoffset, double yoffset)
    {
        app_state.offset_x -= static_cast<float>(xoffset);
        app_state.offset_y -= static_cast<float>(yoffset);
    };

    app.on_mouse_move = [&](double x, double y)
    {
        if (app_state.ml)
        {
            app_state.yaw -= (x - app_state.last_x);
            app_state.yaw = std::max(app_state.yaw, -120.0);
            app_state.yaw = std::min(app_state.yaw, +120.0);
            app_state.pitch += (y - app_state.last_y);
            app_state.pitch = std::max(app_state.pitch, -80.0);
            app_state.pitch = std::min(app_state.pitch, +80.0);
        }
        app_state.last_x = x;
        app_state.last_y = y;
    };

    app.on_key_release = [&](int key)
    {
        if (key == 32) // Escape
        {
            app_state.yaw = app_state.pitch = 0; app_state.offset_x = app_state.offset_y = 0.0;
        }
    };
}<commit_msg>resolve linux compilation issues<commit_after>// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2015 Intel Corporation. All Rights Reserved.

#pragma once

#define GLFW_INCLUDE_GLU
#include <GLFW/glfw3.h>

#include <string>
#include <sstream>
#include <iostream>
#include <algorithm>

//////////////////////////////
// Basic Data Types         //
//////////////////////////////

struct float3 { float x, y, z; };
struct float2 { float x, y; };

struct rect
{
    float x, y;
    float w, h;

    // Create new rect within original boundaries with give aspect ration
    rect adjust_ratio(float2 size) const
    {
        auto H = static_cast<float>(h), W = static_cast<float>(h) * size.x / size.y;
        if (W > w)
        {
            auto scale = w / W;
            W *= scale;
            H *= scale;
        }

        return{ x + (w - W) / 2, y + (h - H) / 2, W, H };
    }
};

//////////////////////////////
// Simple font loading code //
//////////////////////////////

#include "../third-party/stb_easy_font.h"

inline void draw_text(int x, int y, const char * text)
{
    char buffer[60000]; // ~300 chars
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, GL_FLOAT, 16, buffer);
    glDrawArrays(GL_QUADS, 0, 4 * stb_easy_font_print((float)x, (float)(y - 7), (char *)text, nullptr, buffer, sizeof(buffer)));
    glDisableClientState(GL_VERTEX_ARRAY);
}

////////////////////////
// Image display code //
////////////////////////
class texture
{
public:
    void render(const rs2::frameset& frames, int window_width, int window_height)
    {
        std::vector<rs2::video_frame> supported_frames;
        for (auto f : frames)
        {
            if (can_render(f))
                supported_frames.push_back(f);
        }
        if (supported_frames.empty())
            return;

        std::sort(supported_frames.begin(), supported_frames.end(), [](rs2::frame first, rs2::frame second)
            { return first.get_profile().stream_type() < second.get_profile().stream_type();  });

        auto image_grid = calc_grid(float2{ (float)window_width, (float)window_height }, supported_frames);

        int image_index = 0;
        for (auto f : supported_frames)
        {
            upload(f);
            show(image_grid.at(image_index));
            image_index++;
        }
    }

    void render(const rs2::video_frame& frame, const rect& r)
    {
        upload(frame);
        show(r.adjust_ratio({ float(width), float(height) }));
    }

    void upload(const rs2::video_frame& frame)
    {
        if (!frame) return;

        if (!gl_handle)
            glGenTextures(1, &gl_handle);
        GLenum err = glGetError();

        auto format = frame.get_profile().format();
        width = frame.get_width();
        height = frame.get_height();
        stream = frame.get_profile().stream_type();

        glBindTexture(GL_TEXTURE_2D, gl_handle);

        switch (format)
        {
        case RS2_FORMAT_RGB8:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, frame.get_data());
            break;
        case RS2_FORMAT_RGBA8:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame.get_data());
            break;
        case RS2_FORMAT_Y8:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame.get_data());
            break;
        default:
            throw std::runtime_error("The requested format is not supported by this demo!");
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
    }

    GLuint get_gl_handle() { return gl_handle; }

    void show(const rect& r) const
    {
        if (!gl_handle)
            return;

        glBindTexture(GL_TEXTURE_2D, gl_handle);
        glEnable(GL_TEXTURE_2D);
        glBegin(GL_QUAD_STRIP);
        glTexCoord2f(0.f, 1.f); glVertex2f(r.x, r.y + r.h);
        glTexCoord2f(0.f, 0.f); glVertex2f(r.x, r.y);
        glTexCoord2f(1.f, 1.f); glVertex2f(r.x + r.w, r.y + r.h);
        glTexCoord2f(1.f, 0.f); glVertex2f(r.x + r.w, r.y);
        glEnd();
        glDisable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, 0);

        draw_text((int)r.x + 15, (int)r.y + 20, rs2_stream_to_string(stream));
    }

private:
    GLuint gl_handle = 0;
    int width = 0;
    int height = 0;
    rs2_stream stream = RS2_STREAM_ANY;

    bool can_render(const rs2::frame& f) const
    {
        auto format = f.get_profile().format();
        switch (format)
        {
        case RS2_FORMAT_RGB8:
        case RS2_FORMAT_RGBA8:
        case RS2_FORMAT_Y8:
            return true;
        default:
            return false;
        }
    }

    rect calc_grid(float2 window, int streams)
    {
        float ratio = window.x / window.y;
        auto x = sqrt(ratio * (float)streams);
        auto y = (float)streams / x;
        auto w = round(x);
        auto h = round(y);
        while (w*h > streams)
            h > w ? h-- : w--;
        while (w*h < streams)
            h > w ? w++ : h++;
        auto new_w = round(window.x / w);
        auto new_h = round(window.y / h);
        return rect{ w, h, new_w, new_h}; //column count, line count, cell width cell height
    }

    std::vector<rect> calc_grid(float2 window, std::vector<rs2::video_frame>& frames)
    {
        auto grid = calc_grid(window, frames.size());

        int index = 0;
        std::vector<rect> rv;
        int curr_line = -1;
        for (auto f  : frames)
        {
            auto mod = index % (int)grid.x;
            auto fw = (float)frames[index].get_width();
            auto fh = (float)frames[index].get_height();

            float cell_x_postion = (float)(mod * grid.w);
            if (mod == 0) curr_line++;
            float cell_y_position = curr_line * grid.h;

            auto r = rect{ cell_x_postion, cell_y_position, grid.w, grid.h };
            rv.push_back(r.adjust_ratio(float2{ fw, fh }));
            index++;
        }

        return rv;
    }
};

class window
{
public:
    std::function<void(bool)>           on_left_mouse = [](bool) {};
    std::function<void(double, double)> on_mouse_scroll = [](double, double) {};
    std::function<void(double, double)> on_mouse_move = [](double, double) {};
    std::function<void(int)>            on_key_release = [](int) {};

    window(int width, int height, const char* title)
        : _width(width), _height(height)
    {
        glfwInit();
        win = glfwCreateWindow(width, height, title, nullptr, nullptr);
        if (!win)
            throw std::runtime_error("Could not open OpenGL window, please check your graphic drivers or use the textual SDK tools");
        glfwMakeContextCurrent(win);

        glfwSetWindowUserPointer(win, this);
        glfwSetMouseButtonCallback(win, [](GLFWwindow * win, int button, int action, int mods)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            if (button == 0) s->on_left_mouse(action == GLFW_PRESS);
        });

        glfwSetScrollCallback(win, [](GLFWwindow * win, double xoffset, double yoffset)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            s->on_mouse_scroll(xoffset, yoffset);
        });

        glfwSetCursorPosCallback(win, [](GLFWwindow * win, double x, double y)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            s->on_mouse_move(x, y);
        });

        glfwSetKeyCallback(win, [](GLFWwindow * win, int key, int scancode, int action, int mods)
        {
            auto s = (window*)glfwGetWindowUserPointer(win);
            if (0 == action) // on key release
            {
                s->on_key_release(key);
            }
        });
    }

    float width() const { return float(_width); }
    float height() const { return float(_height); }

    operator bool()
    {
        glPopMatrix();
        glfwSwapBuffers(win);

        auto res = !glfwWindowShouldClose(win);

        glfwPollEvents();
        glfwGetFramebufferSize(win, &_width, &_height);

        // Clear the framebuffer
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(0, 0, _width, _height);

        // Draw the images
        glPushMatrix();
        glfwGetWindowSize(win, &_width, &_height);
        glOrtho(0, _width, _height, 0, -1, +1);

        return res;
    }

    ~window()
    {
        glfwDestroyWindow(win);
        glfwTerminate();
    }

    operator GLFWwindow*() { return win; }

private:
    GLFWwindow * win;
    int _width, _height;
};

// Struct for managing rotation of pointcloud view
struct glfw_state {
    glfw_state() : yaw(15.0), pitch(15.0), last_x(0.0), last_y(0.0),
        ml(false), offset_x(2.f), offset_y(2.f), tex() {}
    double yaw;
    double pitch;
    double last_x;
    double last_y;
    bool ml;
    float offset_x;
    float offset_y;
    texture tex;
};

// Handles all the OpenGL calls needed to display the point cloud
void draw_pointcloud(float width, float height, glfw_state& app_state, rs2::points& points)
{
    if (!points)
        return;

    // OpenGL commands that prep screen for the pointcloud
    glPopMatrix();
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    glClearColor(153.f / 255, 153.f / 255, 153.f / 255, 1);
    glClear(GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    gluPerspective(60, width / height, 0.01f, 10.0f);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    gluLookAt(0, 0, 0, 0, 0, 1, 0, -1, 0);

    glTranslatef(0, 0, +0.5f + app_state.offset_y*0.05f);
    glRotated(app_state.pitch, 1, 0, 0);
    glRotated(app_state.yaw, 0, 1, 0);
    glTranslatef(0, 0, -0.5f);

    glPointSize(width / 640);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, app_state.tex.get_gl_handle());
    float tex_border_color[] = { 0.8f, 0.8f, 0.8f, 0.8f };
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, tex_border_color);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F); // GL_CLAMP_TO_EDGE
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F); // GL_CLAMP_TO_EDGE
    glBegin(GL_POINTS);

    /* this segment actually prints the pointcloud */
    auto vertices = points.get_vertices();              // get vertices
    auto tex_coords = points.get_texture_coordinates(); // and texture coordinates
    for (int i = 0; i < points.size(); i++)
    {
        if (vertices[i].z)
        {
            // upload the point and texture coordinates only for points we have depth data for
            glVertex3fv(vertices[i]);
            glTexCoord2fv(tex_coords[i]);
        }
    }

    // OpenGL cleanup
    glEnd();
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glPopAttrib();
    glPushMatrix();
}

// Registers the state variable and callbacks to allow mouse control of the pointcloud
void register_glfw_callbacks(window& app, glfw_state& app_state)
{
    app.on_left_mouse = [&](bool pressed)
    {
        app_state.ml = pressed;
    };

    app.on_mouse_scroll = [&](double xoffset, double yoffset)
    {
        app_state.offset_x -= static_cast<float>(xoffset);
        app_state.offset_y -= static_cast<float>(yoffset);
    };

    app.on_mouse_move = [&](double x, double y)
    {
        if (app_state.ml)
        {
            app_state.yaw -= (x - app_state.last_x);
            app_state.yaw = std::max(app_state.yaw, -120.0);
            app_state.yaw = std::min(app_state.yaw, +120.0);
            app_state.pitch += (y - app_state.last_y);
            app_state.pitch = std::max(app_state.pitch, -80.0);
            app_state.pitch = std::min(app_state.pitch, +80.0);
        }
        app_state.last_x = x;
        app_state.last_y = y;
    };

    app.on_key_release = [&](int key)
    {
        if (key == 32) // Escape
        {
            app_state.yaw = app_state.pitch = 0; app_state.offset_x = app_state.offset_y = 0.0;
        }
    };
}<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "gdbengine.h"
#include "gdbmi.h"
#include "abstractgdbadapter.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerstringutils.h"
#include "debuggertooltipmanager.h"

#include "breakhandler.h"
#include "watchhandler.h"
#include "stackhandler.h"

#include <utils/qtcassert.h>

#define PRECONDITION QTC_ASSERT(hasPython(), /**/)
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

namespace Debugger {
namespace Internal {

void GdbEngine::updateLocalsPython(bool tryPartial, const QByteArray &varList)
{
    PRECONDITION;
    m_processedNames.clear();
    watchHandler()->beginCycle(!tryPartial);
    //m_toolTipExpression.clear();
    WatchHandler *handler = watchHandler();

    QByteArray expanded = "expanded:" + handler->expansionRequests() + ' ';
    expanded += "typeformats:" + handler->typeFormatRequests() + ' ';
    expanded += "formats:" + handler->individualFormatRequests();

    QByteArray watchers;
    const QString fileName = stackHandler()->currentFrame().file;
    const QString function = stackHandler()->currentFrame().function;
    if (!fileName.isEmpty()) {
        QStringList expressions =
            DebuggerToolTipManager::instance()->treeWidgetExpressions(fileName, objectName(), function);
        const QString currentExpression = tooltipExpression();
        if (!currentExpression.isEmpty() && !expressions.contains(currentExpression))
            expressions.push_back(currentExpression);
        foreach (const QString &te, expressions) {
            if (!watchers.isEmpty())
                watchers += "##";
            watchers += te.toLatin1();
            watchers += '#';
            watchers += tooltipIName(te);
        }
    }

    QHash<QByteArray, int> watcherNames = handler->watcherNames();
    QHashIterator<QByteArray, int> it(watcherNames);
    while (it.hasNext()) {
        it.next();
        if (!watchers.isEmpty())
            watchers += "##";
        watchers += it.key() + "#watch." + QByteArray::number(it.value());
    }

    QByteArray options;
    if (debuggerCore()->boolSetting(UseDebuggingHelpers))
        options += "fancy,";
    if (debuggerCore()->boolSetting(AutoDerefPointers))
        options += "autoderef,";
    if (!qgetenv("QTC_DEBUGGER_PYTHON_VERBOSE").isEmpty())
        options += "pe,";
    if (options.isEmpty())
        options += "defaults,";
    if (tryPartial)
        options += "partial,";
    options.chop(1);

    QByteArray resultVar;
    if (!m_resultVarName.isEmpty())
        resultVar = "resultvarname:" + m_resultVarName + ' ';

    postCommand("bb options:" + options + " vars:" + varList + ' '
            + resultVar + expanded + " watchers:" + watchers.toHex(),
        WatchUpdate, CB(handleStackFramePython), QVariant(tryPartial));
}

void GdbEngine::handleStackListLocalsPython(const GdbResponse &response)
{
    PRECONDITION;
    if (response.resultClass == GdbResultDone) {
        // 44^done,data={locals=[name="model",name="backString",...]}
        QByteArray varList = "vars"; // Dummy entry, will be stripped by dumper.
        foreach (const GdbMi &child, response.data.findChild("locals").children()) {
            varList.append(',');
            varList.append(child.data());
        }
        updateLocalsPython(false, varList);
    }
}

void GdbEngine::handleStackFramePython(const GdbResponse &response)
{
    PRECONDITION;
    if (response.resultClass == GdbResultDone) {
        const bool partial = response.cookie.toBool();
        //qDebug() << "READING " << (partial ? "PARTIAL" : "FULL");
        QByteArray out = response.data.findChild("consolestreamoutput").data();
        while (out.endsWith(' ') || out.endsWith('\n'))
            out.chop(1);
        //qDebug() << "SECOND CHUNK: " << out;
        int pos = out.indexOf("data=");
        if (pos != 0) {
            showMessage(_("DISCARDING JUNK AT BEGIN OF RESPONSE: "
                + out.left(pos)));
            out = out.mid(pos);
        }
        GdbMi all;
        all.fromStringMultiple(out);
        //qDebug() << "ALL: " << all.toString();

        GdbMi data = all.findChild("data");
        QList<WatchData> list;
        foreach (const GdbMi &child, data.children()) {
            WatchData dummy;
            dummy.iname = child.findChild("iname").data();
            GdbMi wname = child.findChild("wname");
            if (wname.isValid()) {
                // Happens (only) for watched expressions. They are encoded as.
                // base64 encoded 8 bit data, without quotes
                dummy.name = decodeData(wname.data(), 5);
                dummy.exp = dummy.name.toUtf8();
            } else {
                dummy.name = _(child.findChild("name").data());
            }
            //qDebug() << "CHILD: " << child.toString();
            parseWatchData(watchHandler()->expandedINames(), dummy, child, &list);
        }
        watchHandler()->insertBulkData(list);
        //for (int i = 0; i != list.size(); ++i)
        //    qDebug() << "LOCAL: " << list.at(i).toString();

#if 0
        data = all.findChild("bkpts");
        if (data.isValid()) {
            BreakHandler *handler = breakHandler();
            foreach (const GdbMi &child, data.children()) {
                int bpNumber = child.findChild("number").data().toInt();
                int found = handler->findBreakpoint(bpNumber);
                if (found != -1) {
                    BreakpointData *bp = handler->at(found);
                    GdbMi addr = child.findChild("addr");
                    if (addr.isValid()) {
                        bp->bpAddress = child.findChild("addr").data();
                        bp->pending = false;
                    } else {
                        bp->bpAddress = "<PENDING>";
                        bp->pending = true;
                    }
                    bp->bpFuncName = child.findChild("func").data();
                    bp->bpLineNumber = child.findChild("line").data();
                    bp->bpFileName = child.findChild("file").data();
                    bp->markerLineNumber = bp->bpLineNumber.toInt();
                    bp->markerFileName = bp->bpFileName;
                    // Happens with moved/symlinked sources.
                    if (!bp->fileName.isEmpty()
                            && !bp->bpFileName.isEmpty()
                            && bp->fileName !=  bp->bpFileName)
                        bp->markerFileName = bp->fileName;
                } else {
                    QTC_ASSERT(false, qDebug() << child.toString() << bpNumber);
                    //bp->bpNumber = "<unavailable>";
                }
            }
            handler->updateMarkers();
        }
#endif

        //PENDING_DEBUG("AFTER handleStackFrame()");
        // FIXME: This should only be used when updateLocals() was
        // triggered by expanding an item in the view.
        if (m_pendingWatchRequests <= 0) {
            //PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildWatchModel();
        }
        if (!partial)
            emit stackFrameCompleted();
    } else {
        showMessage(_("DUMPER FAILED: " + response.toString()));
    }
}

// Called from CoreAdapter and AttachAdapter
void GdbEngine::updateAllPython()
{
    PRECONDITION;
    //PENDING_DEBUG("UPDATING ALL\n");
    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopOk, /**/);
    reloadModulesInternal();
    postCommand("-stack-list-frames", CB(handleStackListFrames),
        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
    stackHandler()->setCurrentIndex(0);
    if (m_gdbAdapter->isTrkAdapter())
        m_gdbAdapter->trkReloadThreads();
    else
        postCommand("-thread-list-ids", CB(handleThreadListIds), 0);
    reloadRegisters();
    updateLocals();
}

} // namespace Internal
} // namespace Debugger
<commit_msg>debugger: use the new enum<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "gdbengine.h"
#include "gdbmi.h"
#include "abstractgdbadapter.h"
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerstringutils.h"
#include "debuggertooltipmanager.h"

#include "breakhandler.h"
#include "watchhandler.h"
#include "stackhandler.h"

#include <utils/qtcassert.h>

#define PRECONDITION QTC_ASSERT(hasPython(), /**/)
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

namespace Debugger {
namespace Internal {

void GdbEngine::updateLocalsPython(bool tryPartial, const QByteArray &varList)
{
    PRECONDITION;
    m_processedNames.clear();
    watchHandler()->beginCycle(!tryPartial);
    //m_toolTipExpression.clear();
    WatchHandler *handler = watchHandler();

    QByteArray expanded = "expanded:" + handler->expansionRequests() + ' ';
    expanded += "typeformats:" + handler->typeFormatRequests() + ' ';
    expanded += "formats:" + handler->individualFormatRequests();

    QByteArray watchers;
    const QString fileName = stackHandler()->currentFrame().file;
    const QString function = stackHandler()->currentFrame().function;
    if (!fileName.isEmpty()) {
        QStringList expressions =
            DebuggerToolTipManager::instance()->treeWidgetExpressions(fileName, objectName(), function);
        const QString currentExpression = tooltipExpression();
        if (!currentExpression.isEmpty() && !expressions.contains(currentExpression))
            expressions.push_back(currentExpression);
        foreach (const QString &te, expressions) {
            if (!watchers.isEmpty())
                watchers += "##";
            watchers += te.toLatin1();
            watchers += '#';
            watchers += tooltipIName(te);
        }
    }

    QHash<QByteArray, int> watcherNames = handler->watcherNames();
    QHashIterator<QByteArray, int> it(watcherNames);
    while (it.hasNext()) {
        it.next();
        if (!watchers.isEmpty())
            watchers += "##";
        watchers += it.key() + "#watch." + QByteArray::number(it.value());
    }

    QByteArray options;
    if (debuggerCore()->boolSetting(UseDebuggingHelpers))
        options += "fancy,";
    if (debuggerCore()->boolSetting(AutoDerefPointers))
        options += "autoderef,";
    if (!qgetenv("QTC_DEBUGGER_PYTHON_VERBOSE").isEmpty())
        options += "pe,";
    if (options.isEmpty())
        options += "defaults,";
    if (tryPartial)
        options += "partial,";
    options.chop(1);

    QByteArray resultVar;
    if (!m_resultVarName.isEmpty())
        resultVar = "resultvarname:" + m_resultVarName + ' ';

    postCommand("bb options:" + options + " vars:" + varList + ' '
            + resultVar + expanded + " watchers:" + watchers.toHex(),
        WatchUpdate, CB(handleStackFramePython), QVariant(tryPartial));
}

void GdbEngine::handleStackListLocalsPython(const GdbResponse &response)
{
    PRECONDITION;
    if (response.resultClass == GdbResultDone) {
        // 44^done,data={locals=[name="model",name="backString",...]}
        QByteArray varList = "vars"; // Dummy entry, will be stripped by dumper.
        foreach (const GdbMi &child, response.data.findChild("locals").children()) {
            varList.append(',');
            varList.append(child.data());
        }
        updateLocalsPython(false, varList);
    }
}

void GdbEngine::handleStackFramePython(const GdbResponse &response)
{
    PRECONDITION;
    if (response.resultClass == GdbResultDone) {
        const bool partial = response.cookie.toBool();
        //qDebug() << "READING " << (partial ? "PARTIAL" : "FULL");
        QByteArray out = response.data.findChild("consolestreamoutput").data();
        while (out.endsWith(' ') || out.endsWith('\n'))
            out.chop(1);
        //qDebug() << "SECOND CHUNK: " << out;
        int pos = out.indexOf("data=");
        if (pos != 0) {
            showMessage(_("DISCARDING JUNK AT BEGIN OF RESPONSE: "
                + out.left(pos)));
            out = out.mid(pos);
        }
        GdbMi all;
        all.fromStringMultiple(out);
        //qDebug() << "ALL: " << all.toString();

        GdbMi data = all.findChild("data");
        QList<WatchData> list;
        foreach (const GdbMi &child, data.children()) {
            WatchData dummy;
            dummy.iname = child.findChild("iname").data();
            GdbMi wname = child.findChild("wname");
            if (wname.isValid()) {
                // Happens (only) for watched expressions.
                dummy.name = decodeData(wname.data(), Base64Encoded8Bit);
                dummy.exp = dummy.name.toUtf8();
            } else {
                dummy.name = _(child.findChild("name").data());
            }
            //qDebug() << "CHILD: " << child.toString();
            parseWatchData(watchHandler()->expandedINames(), dummy, child, &list);
        }
        watchHandler()->insertBulkData(list);
        //for (int i = 0; i != list.size(); ++i)
        //    qDebug() << "LOCAL: " << list.at(i).toString();

#if 0
        data = all.findChild("bkpts");
        if (data.isValid()) {
            BreakHandler *handler = breakHandler();
            foreach (const GdbMi &child, data.children()) {
                int bpNumber = child.findChild("number").data().toInt();
                int found = handler->findBreakpoint(bpNumber);
                if (found != -1) {
                    BreakpointData *bp = handler->at(found);
                    GdbMi addr = child.findChild("addr");
                    if (addr.isValid()) {
                        bp->bpAddress = child.findChild("addr").data();
                        bp->pending = false;
                    } else {
                        bp->bpAddress = "<PENDING>";
                        bp->pending = true;
                    }
                    bp->bpFuncName = child.findChild("func").data();
                    bp->bpLineNumber = child.findChild("line").data();
                    bp->bpFileName = child.findChild("file").data();
                    bp->markerLineNumber = bp->bpLineNumber.toInt();
                    bp->markerFileName = bp->bpFileName;
                    // Happens with moved/symlinked sources.
                    if (!bp->fileName.isEmpty()
                            && !bp->bpFileName.isEmpty()
                            && bp->fileName !=  bp->bpFileName)
                        bp->markerFileName = bp->fileName;
                } else {
                    QTC_ASSERT(false, qDebug() << child.toString() << bpNumber);
                    //bp->bpNumber = "<unavailable>";
                }
            }
            handler->updateMarkers();
        }
#endif

        //PENDING_DEBUG("AFTER handleStackFrame()");
        // FIXME: This should only be used when updateLocals() was
        // triggered by expanding an item in the view.
        if (m_pendingWatchRequests <= 0) {
            //PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildWatchModel();
        }
        if (!partial)
            emit stackFrameCompleted();
    } else {
        showMessage(_("DUMPER FAILED: " + response.toString()));
    }
}

// Called from CoreAdapter and AttachAdapter
void GdbEngine::updateAllPython()
{
    PRECONDITION;
    //PENDING_DEBUG("UPDATING ALL\n");
    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopOk, /**/);
    reloadModulesInternal();
    postCommand("-stack-list-frames", CB(handleStackListFrames),
        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
    stackHandler()->setCurrentIndex(0);
    if (m_gdbAdapter->isTrkAdapter())
        m_gdbAdapter->trkReloadThreads();
    else
        postCommand("-thread-list-ids", CB(handleThreadListIds), 0);
    reloadRegisters();
    updateLocals();
}

} // namespace Internal
} // namespace Debugger
<|endoftext|>


<commit_before>/*******************************************************************************

Copyright The University of Auckland

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

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

//==============================================================================
// Qwt plugin
//==============================================================================

#include "qwtplugin.h"

//==============================================================================

namespace OpenCOR {
namespace Qwt {

//==============================================================================

PLUGININFO_FUNC QwtPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", QString::fromUtf8("a plugin to access Qwt 6.1.2+."));
    descriptions.insert("fr", QString::fromUtf8("une extension pour accéder Qwt 6.1.2+."));

    return new PluginInfo("Third-party", false, false,
                          QStringList(),
                          descriptions);
}

//==============================================================================

}   // namespace Qwt
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Qwt library: some work on upgrading to version 6.1.3 (#1002).<commit_after>/*******************************************************************************

Copyright The University of Auckland

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

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

//==============================================================================
// Qwt plugin
//==============================================================================

#include "qwtplugin.h"

//==============================================================================

namespace OpenCOR {
namespace Qwt {

//==============================================================================

PLUGININFO_FUNC QwtPluginInfo()
{
    Descriptions descriptions;

    descriptions.insert("en", QString::fromUtf8("a plugin to access Qwt 6.1.3."));
    descriptions.insert("fr", QString::fromUtf8("une extension pour accéder Qwt 6.1.3."));

    return new PluginInfo("Third-party", false, false,
                          QStringList(),
                          descriptions);
}

//==============================================================================

}   // namespace Qwt
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


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

#include "src/profiling/memory/bookkeeping.h"

#include "test/gtest_and_gmock.h"

namespace perfetto {
namespace profiling {
namespace {

using ::testing::AnyOf;
using ::testing::Eq;

std::vector<FrameData> stack() {
  std::vector<FrameData> res;

  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun2";
  data.map_name = "map2";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

std::vector<FrameData> stack2() {
  std::vector<FrameData> res;
  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun3";
  data.map_name = "map3";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

TEST(BookkeepingTest, Basic) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 0u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, Max) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, true);

  hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.max_timestamp(), 200u);
  ASSERT_EQ(hd.GetMaxForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetMaxForTesting(stack2()), 2u);
}

TEST(BookkeepingTest, TwoHeapTrackers) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);
  {
    HeapTracker hd2(&c, false);

    hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
    hd2.RecordMalloc(stack(), 2, 2, 2, sequence_number, 100 * sequence_number);
    sequence_number++;
    ASSERT_EQ(hd2.GetSizeForTesting(stack()), 2u);
    ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
    ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  }
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
}

TEST(BookkeepingTest, ReplaceAlloc) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 1, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 0u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, OutOfOrder) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 1, 5, 5, 2, 2);
  hd.RecordMalloc(stack2(), 1, 2, 2, 1, 1);
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 5u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 0u);
}

TEST(BookkeepingTest, ManyAllocations) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  std::vector<std::pair<uint64_t, uint64_t>> batch_frees;

  for (uint64_t sequence_number = 1; sequence_number < 1000;) {
    if (batch_frees.size() > 10) {
      for (const auto& p : batch_frees)
        hd.RecordFree(p.first, p.second, 100 * p.second);
      batch_frees.clear();
    }

    uint64_t addr = sequence_number;
    hd.RecordMalloc(stack(), addr, 5, 5, sequence_number, sequence_number);
    sequence_number++;
    batch_frees.emplace_back(addr, sequence_number++);
    ASSERT_THAT(hd.GetSizeForTesting(stack()), AnyOf(Eq(0u), Eq(5u)));
  }
}

TEST(BookkeepingTest, ArbitraryOrder) {
  std::vector<FrameData> s = stack();
  std::vector<FrameData> s2 = stack2();

  enum OperationType { kAlloc, kFree, kDump };

  struct Operation {
    uint64_t sequence_number;
    OperationType type;
    uint64_t address;
    uint64_t bytes;                       // 0 for free
    const std::vector<FrameData>* stack;  // nullptr for free

    // For std::next_permutation.
    bool operator<(const Operation& other) const {
      return sequence_number < other.sequence_number;
    }
  } operations[] = {
      {1, kAlloc, 1, 5, &s},      //
      {2, kAlloc, 1, 10, &s2},    //
      {3, kFree, 1, 0, nullptr},  //
      {4, kFree, 2, 0, nullptr},  //
      {5, kFree, 3, 0, nullptr},  //
      {6, kAlloc, 3, 2, &s},      //
      {7, kAlloc, 4, 3, &s2},     //
      {0, kDump, 0, 0, nullptr},  //
  };

  uint64_t s_size = 2;
  uint64_t s2_size = 3;

  do {
    GlobalCallstackTrie c;
    HeapTracker hd(&c, false);

    for (auto it = std::begin(operations); it != std::end(operations); ++it) {
      const Operation& operation = *it;

      if (operation.type == OperationType::kFree) {
        hd.RecordFree(operation.address, operation.sequence_number,
                      100 * operation.sequence_number);
      } else if (operation.type == OperationType::kAlloc) {
        hd.RecordMalloc(*operation.stack, operation.address, operation.bytes,
                        operation.bytes, operation.sequence_number,
                        100 * operation.sequence_number);
      } else {
        hd.GetCallstackAllocations(
            [](const HeapTracker::CallstackAllocations&) {});
      }
    }
    ASSERT_EQ(hd.GetSizeForTesting(s), s_size);
    ASSERT_EQ(hd.GetSizeForTesting(s2), s2_size);
  } while (std::next_permutation(std::begin(operations), std::end(operations)));
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto
<commit_msg>Add bookkeeping test for empty callstack.<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/profiling/memory/bookkeeping.h"

#include "test/gtest_and_gmock.h"

namespace perfetto {
namespace profiling {
namespace {

using ::testing::AnyOf;
using ::testing::Eq;

std::vector<FrameData> stack() {
  std::vector<FrameData> res;

  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun2";
  data.map_name = "map2";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

std::vector<FrameData> stack2() {
  std::vector<FrameData> res;
  unwindstack::FrameData data{};
  data.function_name = "fun1";
  data.map_name = "map1";
  res.emplace_back(std::move(data), "dummy_buildid");
  data = {};
  data.function_name = "fun3";
  data.map_name = "map3";
  res.emplace_back(std::move(data), "dummy_buildid");
  return res;
}

TEST(BookkeepingTest, EmptyStack) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc({}, 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(0x1, sequence_number, 100 * sequence_number);
  hd.GetCallstackAllocations([](const HeapTracker::CallstackAllocations&) {});
}

TEST(BookkeepingTest, Basic) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 0x2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(0x2, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  hd.RecordFree(0x1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 0u);
  ASSERT_EQ(hd.GetSizeForTesting(stack2()), 0u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, Max) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, true);

  hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 0x2, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(0x2, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordFree(0x1, sequence_number, 100 * sequence_number);
  sequence_number++;
  ASSERT_EQ(hd.max_timestamp(), 200u);
  ASSERT_EQ(hd.GetMaxForTesting(stack()), 5u);
  ASSERT_EQ(hd.GetMaxForTesting(stack2()), 2u);
}

TEST(BookkeepingTest, TwoHeapTrackers) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);
  {
    HeapTracker hd2(&c, false);

    hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
    hd2.RecordMalloc(stack(), 0x2, 2, 2, sequence_number,
                     100 * sequence_number);
    sequence_number++;
    ASSERT_EQ(hd2.GetSizeForTesting(stack()), 2u);
    ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
    ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
  }
  ASSERT_EQ(hd.GetSizeForTesting(stack()), 5u);
}

TEST(BookkeepingTest, ReplaceAlloc) {
  uint64_t sequence_number = 1;
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 0x1, 5, 5, sequence_number, 100 * sequence_number);
  sequence_number++;
  hd.RecordMalloc(stack2(), 0x1, 2, 2, sequence_number, 100 * sequence_number);
  sequence_number++;
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 0u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 2u);
  ASSERT_EQ(hd.GetTimestampForTesting(), 100 * (sequence_number - 1));
}

TEST(BookkeepingTest, OutOfOrder) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  hd.RecordMalloc(stack(), 0x1, 5, 5, 2, 2);
  hd.RecordMalloc(stack2(), 0x1, 2, 2, 1, 1);
  EXPECT_EQ(hd.GetSizeForTesting(stack()), 5u);
  EXPECT_EQ(hd.GetSizeForTesting(stack2()), 0u);
}

TEST(BookkeepingTest, ManyAllocations) {
  GlobalCallstackTrie c;
  HeapTracker hd(&c, false);

  std::vector<std::pair<uint64_t, uint64_t>> batch_frees;

  for (uint64_t sequence_number = 1; sequence_number < 1000;) {
    if (batch_frees.size() > 10) {
      for (const auto& p : batch_frees)
        hd.RecordFree(p.first, p.second, 100 * p.second);
      batch_frees.clear();
    }

    uint64_t addr = sequence_number;
    hd.RecordMalloc(stack(), addr, 5, 5, sequence_number, sequence_number);
    sequence_number++;
    batch_frees.emplace_back(addr, sequence_number++);
    ASSERT_THAT(hd.GetSizeForTesting(stack()), AnyOf(Eq(0u), Eq(5u)));
  }
}

TEST(BookkeepingTest, ArbitraryOrder) {
  std::vector<FrameData> s = stack();
  std::vector<FrameData> s2 = stack2();

  enum OperationType { kAlloc, kFree, kDump };

  struct Operation {
    uint64_t sequence_number;
    OperationType type;
    uint64_t address;
    uint64_t bytes;                       // 0 for free
    const std::vector<FrameData>* stack;  // nullptr for free

    // For std::next_permutation.
    bool operator<(const Operation& other) const {
      return sequence_number < other.sequence_number;
    }
  } operations[] = {
      {1, kAlloc, 0x1, 5, &s},       //
      {2, kAlloc, 0x1, 10, &s2},     //
      {3, kFree, 0x01, 0, nullptr},  //
      {4, kFree, 0x02, 0, nullptr},  //
      {5, kFree, 0x03, 0, nullptr},  //
      {6, kAlloc, 0x3, 2, &s},       //
      {7, kAlloc, 0x4, 3, &s2},      //
      {0, kDump, 0x00, 0, nullptr},  //
  };

  uint64_t s_size = 2;
  uint64_t s2_size = 3;

  do {
    GlobalCallstackTrie c;
    HeapTracker hd(&c, false);

    for (auto it = std::begin(operations); it != std::end(operations); ++it) {
      const Operation& operation = *it;

      if (operation.type == OperationType::kFree) {
        hd.RecordFree(operation.address, operation.sequence_number,
                      100 * operation.sequence_number);
      } else if (operation.type == OperationType::kAlloc) {
        hd.RecordMalloc(*operation.stack, operation.address, operation.bytes,
                        operation.bytes, operation.sequence_number,
                        100 * operation.sequence_number);
      } else {
        hd.GetCallstackAllocations(
            [](const HeapTracker::CallstackAllocations&) {});
      }
    }
    ASSERT_EQ(hd.GetSizeForTesting(s), s_size);
    ASSERT_EQ(hd.GetSizeForTesting(s2), s2_size);
  } while (std::next_permutation(std::begin(operations), std::end(operations)));
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto
<|endoftext|>


<commit_before>/*
 Copyright (c) 2015 ANON authors, see AUTHORS file.

 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 "sproc_mgr.h"
#include "log.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

char  base_path[4096];
char  cmd_path[4096];

// hard-coded for now
const char* exe_name;

static void validate_command_file()
{
  struct stat st;
  if (stat(cmd_path, &st) != 0) {
    if (errno == ENOENT) {
      if (mkfifo(cmd_path, 0666) != 0)
        do_error("mkfifo(\"" << &cmd_path[0] << "\", 0666");
      else
        return;
    } else
      do_error("stat(\"" << &cmd_path[0] << "\", &st)");
  }

  if (S_ISFIFO(st.st_mode))
    return;

  if (S_ISREG(st.st_mode)) {
    if (unlink(cmd_path) != 0)
      do_error("unlink(\"" << &cmd_path[0] << "\")");
    validate_command_file();
  } else if (S_ISDIR(st.st_mode)) {
    anon_log("\"" << &cmd_path[0] << "\" is a directory and must be manually deleted for this program to run");
    exit(1);
  }

  anon_log("\"" << &cmd_path[0] << "\" is an unknown file type and must be manually deleted for this program to run");
  exit(1);
}

static void write_all(int fd, const char* data)
{
  size_t  len = strlen(data);
  size_t  written = 0;
  while (written < len)
    written += ::write(fd,&data[written],len-written);
}

static bool process_command(const std::string& cmd)
{
  std::ostringstream  reply;
  bool                ret = true;
  bool                show_help = false;

  try {
    if (cmd == "help") {

      show_help = true;

    } else if (cmd == "quit") {

      reply << "\nquitting, bye\n\n";
      ret = false;

    } else if (cmd == "list_exes") {

      list_exes(base_path, exe_name, reply);

    } else if (cmd.find("start") == 0) {

      const char* p = &cmd.c_str()[5];
      while (*p && ((*p == ' ') || (*p == '\t')))
        ++p;
      std::string full_path = std::string(base_path) + p;

      // hard-coded for now...
      std::vector<std::string> args;
      args.push_back("-cert_verify_dir");
      args.push_back("/etc/ssl/certs");
      args.push_back("-server_cert");
      args.push_back("./secrets/srv_cert.pem");
      args.push_back("-server_key");
      args.push_back("./secrets/srv_key.pem");

      start_server(full_path.c_str(), args);
      reply << "\n" << p << " now running in process " << current_server_pid() << "\n\n";

    } else if (cmd == "current_exe") {

      if (current_server_pid())
        reply << "\ncurrent executable: " << current_exe_name() << ", in process id: " << current_server_pid() << "\n\n";
      else
        reply << "\nno executable currently running\n\n";

    } else {

      reply << "ignoring unknown command, you sent:\n" << cmd << "\n\n";
      show_help = true;

    }
  } catch (const std::exception& err) {
    reply << "\n\nerror: " << err.what() << "\n\n";
  } catch (...) {
    reply << "\n\nunknown error\n\n";
  }

  if (show_help) {
    reply << "available commands:\n\n";
    reply << "help\n";
    reply << "  shows this menu\n\n";
    reply << "quit\n";
    reply << "  quits the server application and all of its child processes\n\n";
    reply << "list_exes\n";
    reply << "  list the set of available executable images to run, along with their\n";
    reply << "  sha1 checksum values\n\n";
    reply << "start <executable name>\n";
    reply << "  starts the specified process running.  If there is already a process\n";
    reply << "  running it will perform a \"hot-swap\" of the process, stopping the\n";
    reply << "  older one and replacing it with the newer one\n\n";
    reply << "current_exe\n";
    reply << "  returns the file name and process id of the currently running executable\n";
    reply << "  if there is one, otherwise tells you that no process is currently running\n\n";
  }

  validate_command_file();
  int fd = open(cmd_path, O_WRONLY | O_CLOEXEC);
  if (fd != -1) {
    write_all(fd, reply.str().c_str());
    close(fd);
  }

  return ret;
}

extern "C" int main(int argc, char** argv)
{
  if (argc != 3) {
    fprintf(stderr,"usage: epoxy <port> <exe_name>\n");
    return 1;
  }

  size_t sz = strlen(argv[0]);
  if (sz > sizeof(cmd_path) - 20) {
    fprintf(stderr,"path to epoxy executable too long\n");
    return 1;
  }

  memcpy(base_path, argv[0], sz+1);
  char* p = &base_path[sz+1];
  while (p > &base_path[0] && *(p-1) != '/')
    --p;
  *p = 0;

  const char* cmd_file_name = ".epoxy_cmd";

  strcpy(cmd_path, base_path);
  strcat(cmd_path, cmd_file_name);

  int port = atoi(argv[1]);
  exe_name = argv[2];

  try {
    sproc_mgr_init(port);
  } catch (const std::exception& err) {
    anon_log("unable to initialize: " << err.what());
    return 1;
  } catch (...) {
    anon_log("unable to initialize");
    return 1;
  }

  anon_log("epoxy bound to network port " << port);
  anon_log("listening for commands on file " << &cmd_path[0]);

  int   exitcode = 0;
  char  cmd_buf[4096];

  validate_command_file();

  while (true) {

    int fd = open(cmd_path, O_RDONLY | O_CLOEXEC);
    if (fd < 0)
      do_error("open(\"" << &cmd_path[0] << "\", O_RDONLY | O_CLOEXEC)");
    auto bytes = ::read(fd, &cmd_buf[0], sizeof(cmd_buf));
    close(fd);
    if (bytes == sizeof(cmd_buf)) {
      cmd_buf[20] = 0;
      anon_log("command too big, ignoring - starts with: \"" << &cmd_buf[0] << "...\"");
    }
    else {
      while (bytes > 0 && cmd_buf[bytes-1] == '\n')
        --bytes;
      if (bytes)  {
        bool keep_going = true;
        try {
          keep_going = process_command(std::string(&cmd_buf[0], 0, bytes));
        }
        catch(...)
        {}
        if (!keep_going)
          break;
      }
    }

  }

  unlink(cmd_path);
  sproc_mgr_term();

  anon_log("epoxy process exiting");
  return exitcode;

}

<commit_msg>minor bug fix<commit_after>/*
 Copyright (c) 2015 ANON authors, see AUTHORS file.

 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 "sproc_mgr.h"
#include "log.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

char  base_path[4096];
char  cmd_path[4096];

// hard-coded for now
const char* exe_name;

static void validate_command_file()
{
  struct stat st;
  if (stat(cmd_path, &st) != 0) {
    if (errno == ENOENT) {
      if (mkfifo(cmd_path, 0666) != 0)
        do_error("mkfifo(\"" << &cmd_path[0] << "\", 0666");
      else
        return;
    } else
      do_error("stat(\"" << &cmd_path[0] << "\", &st)");
  }

  if (S_ISFIFO(st.st_mode))
    return;

  if (S_ISREG(st.st_mode)) {
    if (unlink(cmd_path) != 0)
      do_error("unlink(\"" << &cmd_path[0] << "\")");
    validate_command_file();
    return;
  } else if (S_ISDIR(st.st_mode)) {
    anon_log("\"" << &cmd_path[0] << "\" is a directory and must be manually deleted for this program to run");
    exit(1);
  }

  anon_log("\"" << &cmd_path[0] << "\" is an unknown file type and must be manually deleted for this program to run");
  exit(1);
}

static void write_all(int fd, const char* data)
{
  size_t  len = strlen(data);
  size_t  written = 0;
  while (written < len)
    written += ::write(fd,&data[written],len-written);
}

static bool process_command(const std::string& cmd)
{
  std::ostringstream  reply;
  bool                ret = true;
  bool                show_help = false;

  try {
    if (cmd == "help") {

      show_help = true;

    } else if (cmd == "quit") {

      reply << "\nquitting, bye\n\n";
      ret = false;

    } else if (cmd == "list_exes") {

      list_exes(base_path, exe_name, reply);

    } else if (cmd.find("start") == 0) {

      const char* p = &cmd.c_str()[5];
      while (*p && ((*p == ' ') || (*p == '\t')))
        ++p;
      std::string full_path = std::string(base_path) + p;

      // hard-coded for now...
      std::vector<std::string> args;
      args.push_back("-cert_verify_dir");
      args.push_back("/etc/ssl/certs");
      args.push_back("-server_cert");
      args.push_back("./secrets/srv_cert.pem");
      args.push_back("-server_key");
      args.push_back("./secrets/srv_key.pem");

      start_server(full_path.c_str(), args);
      reply << "\n" << p << " now running in process " << current_server_pid() << "\n\n";

    } else if (cmd == "current_exe") {

      if (current_server_pid())
        reply << "\ncurrent executable: " << current_exe_name() << ", in process id: " << current_server_pid() << "\n\n";
      else
        reply << "\nno executable currently running\n\n";

    } else {

      reply << "ignoring unknown command, you sent:\n" << cmd << "\n\n";
      show_help = true;

    }
  } catch (const std::exception& err) {
    reply << "\n\nerror: " << err.what() << "\n\n";
  } catch (...) {
    reply << "\n\nunknown error\n\n";
  }

  if (show_help) {
    reply << "available commands:\n\n";
    reply << "help\n";
    reply << "  shows this menu\n\n";
    reply << "quit\n";
    reply << "  quits the server application and all of its child processes\n\n";
    reply << "list_exes\n";
    reply << "  list the set of available executable images to run, along with their\n";
    reply << "  sha1 checksum values\n\n";
    reply << "start <executable name>\n";
    reply << "  starts the specified process running.  If there is already a process\n";
    reply << "  running it will perform a \"hot-swap\" of the process, stopping the\n";
    reply << "  older one and replacing it with the newer one\n\n";
    reply << "current_exe\n";
    reply << "  returns the file name and process id of the currently running executable\n";
    reply << "  if there is one, otherwise tells you that no process is currently running\n\n";
  }

  validate_command_file();
  int fd = open(cmd_path, O_WRONLY | O_CLOEXEC);
  if (fd != -1) {
    write_all(fd, reply.str().c_str());
    close(fd);
  }

  return ret;
}

extern "C" int main(int argc, char** argv)
{
  if (argc != 3) {
    fprintf(stderr,"usage: epoxy <port> <exe_name>\n");
    return 1;
  }

  size_t sz = strlen(argv[0]);
  if (sz > sizeof(cmd_path) - 20) {
    fprintf(stderr,"path to epoxy executable too long\n");
    return 1;
  }

  memcpy(base_path, argv[0], sz+1);
  char* p = &base_path[sz+1];
  while (p > &base_path[0] && *(p-1) != '/')
    --p;
  *p = 0;

  const char* cmd_file_name = ".epoxy_cmd";

  strcpy(cmd_path, base_path);
  strcat(cmd_path, cmd_file_name);

  int port = atoi(argv[1]);
  exe_name = argv[2];

  try {
    sproc_mgr_init(port);
  } catch (const std::exception& err) {
    anon_log("unable to initialize: " << err.what());
    return 1;
  } catch (...) {
    anon_log("unable to initialize");
    return 1;
  }

  anon_log("epoxy bound to network port " << port);
  anon_log("listening for commands on file " << &cmd_path[0]);

  int   exitcode = 0;
  char  cmd_buf[4096];

  validate_command_file();

  while (true) {

    int fd = open(cmd_path, O_RDONLY | O_CLOEXEC);
    if (fd < 0)
      do_error("open(\"" << &cmd_path[0] << "\", O_RDONLY | O_CLOEXEC)");
    auto bytes = ::read(fd, &cmd_buf[0], sizeof(cmd_buf));
    close(fd);
    if (bytes == sizeof(cmd_buf)) {
      cmd_buf[20] = 0;
      anon_log("command too big, ignoring - starts with: \"" << &cmd_buf[0] << "...\"");
    }
    else {
      while (bytes > 0 && cmd_buf[bytes-1] == '\n')
        --bytes;
      if (bytes)  {
        bool keep_going = true;
        try {
          keep_going = process_command(std::string(&cmd_buf[0], 0, bytes));
        }
        catch(...)
        {}
        if (!keep_going)
          break;
      }
    }

  }

  unlink(cmd_path);
  sproc_mgr_term();

  anon_log("epoxy process exiting");
  return exitcode;

}

<|endoftext|>