<commit_before>/*
 *   Copyright (C) 2014 Pelagicore AB
 *   All rights reserved.
 */
#include "debug.h"
#include "generators.h"
#include "jansson.h"

#include "devicenodegateway.h"

DeviceNodeGateway::DeviceNodeGateway(ControllerAbstractInterface *controllerInterface):
    Gateway(controllerInterface),
    m_controllerIface(controllerInterface)
{
}

std::string DeviceNodeGateway::id()
{
    return "devicenode";
}

std::string DeviceNodeGateway::environment()
{
    return "";
}

bool DeviceNodeGateway::setConfig(const std::string &config)
{
    json_error_t  error;
    json_t       *root = NULL, *devices = NULL;
    bool success = true;
    std::vector<struct Device> newDevList;

    log_debug("DeviceNodeGateway::setConfig called\n");

    /* Get root JSON object */
    root = json_loads(config.c_str(), 0, &error);

    if (!root) {
        log_error("Error on line %d: %s\n", error.line, error.text);
        success = false;
        goto cleanup_setConfig;
    }

    // Get string
    devices = json_object_get(root, "devices");

    if (!json_is_array(devices)) {
        log_error("Value is not an array.");
        log_error("error: on line %d: %s\n", error.line, error.text);
        success = false;
        goto cleanup_setConfig;
    }

    newDevList = parseDeviceList(devices, success);
    if (success) {
        m_devList = newDevList;
    }

cleanup_setConfig:

    // Also frees 'devices'
    if (root)
        json_decref (root);

    return success;
}

std::vector<struct DeviceNodeGateway::Device>
DeviceNodeGateway::parseDeviceList(json_t *list, bool &ok) {
    ok = true;
    std::vector<struct DeviceNodeGateway::Device> dev_list;
    for (uint i = 0; i < json_array_size(list); i++) {
        struct DeviceNodeGateway::Device dev;
        json_t *device = 0;
        std::string* fields[] = {&dev.name, &dev.major, &dev.minor, &dev.mode};
        std::string  fieldsStr[] = {"name", "major", "minor", "mode"};
        uint numFields = 4;

        device = json_array_get(list, i);
        if (ok && !json_is_object(device)) {
            log_error ("Expected JSON device object, found something else");
            ok = false;
        }

        for (uint j = 0; ok && j < numFields; j++) {
            json_t *value = 0;
            value = json_object_get (device, fieldsStr[j].c_str());
            if (ok && !json_is_string(value)) {
                log_error (std::string("Key '" + fieldsStr[j] +
                           "' is not a member of device object!").c_str());
                ok = false;
            }
            if (json_string_value(value)) {
                *fields[j] = std::string(json_string_value(value));
                ok = true;
            } else {
                ok = false;
                break;
            }
        }

        if (ok) {
            dev_list.push_back(dev);
        } else {
            break;
        }
    }
    return dev_list;
}

bool DeviceNodeGateway::activate()
{
    bool success = true;

    for (uint i = 0; i < m_devList.size(); i++)
    {
        struct DeviceNodeGateway::Device dev;
        dev = m_devList.at(i);
        success &= m_controllerIface->systemCall("mknod " + dev.name + " " +
                                       dev.major + " " + dev.minor);
        if (success) {
            success &= m_controllerIface->systemCall("chmod " + dev.name +
            " " + dev.mode);
        } else {
            log_debug (std::string("Failed to create device " + dev.name)
                            .c_str());
        }
    }

    return success;
}
<commit_msg>Fixed so mkdir and chmod commands are valid<commit_after>/*
 *   Copyright (C) 2014 Pelagicore AB
 *   All rights reserved.
 */
#include "debug.h"
#include "generators.h"
#include "jansson.h"

#include "devicenodegateway.h"

DeviceNodeGateway::DeviceNodeGateway(ControllerAbstractInterface *controllerInterface):
    Gateway(controllerInterface),
    m_controllerIface(controllerInterface)
{
}

std::string DeviceNodeGateway::id()
{
    return "devicenode";
}

std::string DeviceNodeGateway::environment()
{
    return "";
}

bool DeviceNodeGateway::setConfig(const std::string &config)
{
    json_error_t  error;
    json_t       *root = NULL, *devices = NULL;
    bool success = true;
    std::vector<struct Device> newDevList;

    log_debug("DeviceNodeGateway::setConfig called\n");

    /* Get root JSON object */
    root = json_loads(config.c_str(), 0, &error);

    if (!root) {
        log_error("Error on line %d: %s\n", error.line, error.text);
        success = false;
        goto cleanup_setConfig;
    }

    // Get string
    devices = json_object_get(root, "devices");

    if (!json_is_array(devices)) {
        log_error("Value is not an array.");
        log_error("error: on line %d: %s\n", error.line, error.text);
        success = false;
        goto cleanup_setConfig;
    }

    newDevList = parseDeviceList(devices, success);
    if (success) {
        m_devList = newDevList;
    }

cleanup_setConfig:

    // Also frees 'devices'
    if (root)
        json_decref (root);

    return success;
}

std::vector<struct DeviceNodeGateway::Device>
DeviceNodeGateway::parseDeviceList(json_t *list, bool &ok) {
    ok = true;
    std::vector<struct DeviceNodeGateway::Device> dev_list;
    for (uint i = 0; i < json_array_size(list); i++) {
        struct DeviceNodeGateway::Device dev;
        json_t *device = 0;
        std::string* fields[] = {&dev.name, &dev.major, &dev.minor, &dev.mode};
        std::string  fieldsStr[] = {"name", "major", "minor", "mode"};
        uint numFields = 4;

        device = json_array_get(list, i);
        if (ok && !json_is_object(device)) {
            log_error ("Expected JSON device object, found something else");
            ok = false;
        }

        for (uint j = 0; ok && j < numFields; j++) {
            json_t *value = 0;
            value = json_object_get (device, fieldsStr[j].c_str());
            if (ok && !json_is_string(value)) {
                log_error (std::string("Key '" + fieldsStr[j] +
                           "' is not a member of device object!").c_str());
                ok = false;
            }
            if (json_string_value(value)) {
                *fields[j] = std::string(json_string_value(value));
                ok = true;
            } else {
                ok = false;
                break;
            }
        }

        if (ok) {
            dev_list.push_back(dev);
        } else {
            break;
        }
    }
    return dev_list;
}

bool DeviceNodeGateway::activate()
{
    bool success = true;

    for (uint i = 0; i < m_devList.size(); i++)
    {
        struct DeviceNodeGateway::Device dev;
        dev = m_devList.at(i);
        success &= m_controllerIface->systemCall("mknod " + dev.name + " c " +
                                       dev.major + " " + dev.minor);
        if (success) {
            success &= m_controllerIface->systemCall("chmod " +
            dev.mode + " " + dev.name );
        } else {
            log_debug (std::string("Failed to create device " + dev.name)
                            .c_str());
        }
    }

    return success;
}
<|endoftext|>


<commit_before>#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/signals2.hpp>

#include "config/config.h"
#include "logging/logging.h"
#include "primary/aktualizr.h"
#include "primary/aktualizr_helpers.h"
#include "secondary.h"
#include "utilities/aktualizr_version.h"
#include "utilities/sig_handler.h"
#include "utilities/utils.h"

namespace bpo = boost::program_options;

void checkInfoOptions(const bpo::options_description &description, const bpo::variables_map &vm) {
  if (vm.count("help") != 0) {
    std::cout << description << '\n';
    exit(EXIT_SUCCESS);
  }
  if (vm.count("version") != 0) {
    std::cout << "Current aktualizr version is: " << aktualizr_version() << "\n";
    exit(EXIT_SUCCESS);
  }
}

bpo::variables_map parseOptions(int argc, char *argv[]) {
  bpo::options_description description("aktualizr command line options");
  // clang-format off
  // Try to keep these options in the same order as Config::updateFromCommandLine().
  // The first three are commandline only.
  description.add_options()
      ("help,h", "print usage")
      ("version,v", "Current aktualizr version")
      ("config,c", bpo::value<std::vector<boost::filesystem::path> >()->composing(), "configuration file or directory")
      ("loglevel", bpo::value<int>(), "set log level 0-5 (trace, debug, info, warning, error, fatal)")
      ("run-mode", bpo::value<std::string>(), "run mode of aktualizr: full, once, campaign_check, campaign_accept, campaign_decline, campaign_postpone, check, download, or install")
      ("tls-server", bpo::value<std::string>(), "url of device gateway")
      ("repo-server", bpo::value<std::string>(), "url of the uptane repo repository")
      ("director-server", bpo::value<std::string>(), "url of the uptane director repository")
      ("ostree-server", bpo::value<std::string>(), "url of the ostree repository")
      ("primary-ecu-serial", bpo::value<std::string>(), "serial number of primary ecu")
      ("primary-ecu-hardware-id", bpo::value<std::string>(), "hardware ID of primary ecu")
      ("secondary-configs-dir", bpo::value<boost::filesystem::path>(), "directory containing secondary ECU configuration files")
      ("secondary-config-file", bpo::value<boost::filesystem::path>(), "secondary ECUs configuration file")
      ("campaign-id", bpo::value<std::string>(), "id of the campaign to act on");
  // clang-format on

  // consider the first positional argument as the aktualizr run mode
  bpo::positional_options_description pos;
  pos.add("run-mode", 1);

  bpo::variables_map vm;
  std::vector<std::string> unregistered_options;
  try {
    bpo::basic_parsed_options<char> parsed_options =
        bpo::command_line_parser(argc, argv).options(description).positional(pos).allow_unregistered().run();
    bpo::store(parsed_options, vm);
    checkInfoOptions(description, vm);
    bpo::notify(vm);
    unregistered_options = bpo::collect_unrecognized(parsed_options.options, bpo::exclude_positional);
    if (vm.count("help") == 0 && !unregistered_options.empty()) {
      std::cout << description << "\n";
      exit(EXIT_FAILURE);
    }
  } catch (const bpo::required_option &ex) {
    // print the error and append the default commandline option description
    std::cout << ex.what() << std::endl << description;
    exit(EXIT_FAILURE);
  } catch (const bpo::error &ex) {
    checkInfoOptions(description, vm);

    // log boost error
    LOG_ERROR << "boost command line option error: " << ex.what();

    // print the error message to the standard output too, as the user provided
    // a non-supported commandline option
    std::cout << ex.what() << '\n';

    // set the returnValue, thereby ctest will recognize
    // that something went wrong
    exit(EXIT_FAILURE);
  }

  return vm;
}

void processEvent(const std::shared_ptr<event::BaseEvent> &event) {
  if (event->isTypeOf<event::DownloadProgressReport>()) {
    // Do nothing; libaktualizr already logs it.
  } else if (event->variant == "UpdateCheckComplete") {
    // Do nothing; libaktualizr already logs it.
  } else {
    LOG_INFO << "got " << event->variant << " event";
  }
}

int main(int argc, char *argv[]) {
  logger_init();
  logger_set_threshold(boost::log::trivial::info);

  bpo::variables_map commandline_map = parseOptions(argc, argv);

  LOG_INFO << "Aktualizr version " << aktualizr_version() << " starting";

  int r = EXIT_FAILURE;

  try {
    if (geteuid() != 0) {
      LOG_WARNING << "\033[31mAktualizr is currently running as non-root and may not work as expected! Aktualizr "
                     "should be run as root for proper functionality.\033[0m\n";
    }
    Config config(commandline_map);
    LOG_DEBUG << "Current directory: " << boost::filesystem::current_path().string();

    Aktualizr aktualizr(config);
    std::function<void(std::shared_ptr<event::BaseEvent> event)> f_cb = processEvent;
    boost::signals2::scoped_connection conn;

    conn = aktualizr.SetSignalHandler(f_cb);

    if (!config.uptane.secondary_config_file.empty()) {
      if (boost::filesystem::exists(config.uptane.secondary_config_file)) {
        Primary::initSecondaries(aktualizr, config.uptane.secondary_config_file);
      } else {
        LOG_WARNING << "The specified secondary config file does not exist: " << config.uptane.secondary_config_file
                    << "\nProceed further without secondary(ies)";
      }
    }

    aktualizr.Initialize();

    // handle unix signals
    SigHandler::get().start([&aktualizr]() {
      aktualizr.Abort();
      aktualizr.Shutdown();
    });
    SigHandler::signal(SIGHUP);
    SigHandler::signal(SIGINT);
    SigHandler::signal(SIGTERM);

    std::string run_mode;
    if (commandline_map.count("run-mode") != 0) {
      run_mode = commandline_map["run-mode"].as<std::string>();
    }
    // launch the first event
    if (run_mode == "campaign_check") {
      aktualizr.CampaignCheck().get();
    } else if (run_mode == "campaign_accept" || run_mode == "campaign_decline" || run_mode == "campaign_postpone") {
      if (commandline_map.count("campaign-id") == 0) {
        throw std::runtime_error(run_mode + " requires a campaign ID");
      }
      aktualizr.CampaignControl(commandline_map["campaign-id"].as<std::string>(), campaign::cmdFromName(run_mode))
          .get();
    } else if (run_mode == "check") {
      aktualizr.SendDeviceData().get();
      aktualizr.CheckUpdates().get();
    } else if (run_mode == "download") {
      result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
      aktualizr.Download(update_result.updates).get();
    } else if (run_mode == "install") {
      result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
      aktualizr.Install(update_result.updates).get();
    } else if (run_mode == "once") {
      aktualizr.SendDeviceData().get();
      aktualizr.UptaneCycle();
    } else {
      boost::signals2::connection ac_conn =
          aktualizr.SetSignalHandler(std::bind(targets_autoclean_cb, std::ref(aktualizr), std::placeholders::_1));

      try {
        aktualizr.RunForever().get();
      } catch (const std::exception &ex) {
        LOG_ERROR << ex.what();
      }

      LOG_DEBUG << "Aktualizr daemon exiting...";
    }
    r = EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    LOG_ERROR << ex.what();
  }

  return r;
}
<commit_msg>Remove obsolete secondary-configs-dir input parameter.<commit_after>#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/signals2.hpp>

#include "config/config.h"
#include "logging/logging.h"
#include "primary/aktualizr.h"
#include "primary/aktualizr_helpers.h"
#include "secondary.h"
#include "utilities/aktualizr_version.h"
#include "utilities/sig_handler.h"
#include "utilities/utils.h"

namespace bpo = boost::program_options;

void checkInfoOptions(const bpo::options_description &description, const bpo::variables_map &vm) {
  if (vm.count("help") != 0) {
    std::cout << description << '\n';
    exit(EXIT_SUCCESS);
  }
  if (vm.count("version") != 0) {
    std::cout << "Current aktualizr version is: " << aktualizr_version() << "\n";
    exit(EXIT_SUCCESS);
  }
}

bpo::variables_map parseOptions(int argc, char *argv[]) {
  bpo::options_description description("aktualizr command line options");
  // clang-format off
  // Try to keep these options in the same order as Config::updateFromCommandLine().
  // The first three are commandline only.
  description.add_options()
      ("help,h", "print usage")
      ("version,v", "Current aktualizr version")
      ("config,c", bpo::value<std::vector<boost::filesystem::path> >()->composing(), "configuration file or directory")
      ("loglevel", bpo::value<int>(), "set log level 0-5 (trace, debug, info, warning, error, fatal)")
      ("run-mode", bpo::value<std::string>(), "run mode of aktualizr: full, once, campaign_check, campaign_accept, campaign_decline, campaign_postpone, check, download, or install")
      ("tls-server", bpo::value<std::string>(), "url of device gateway")
      ("repo-server", bpo::value<std::string>(), "url of the uptane repo repository")
      ("director-server", bpo::value<std::string>(), "url of the uptane director repository")
      ("ostree-server", bpo::value<std::string>(), "url of the ostree repository")
      ("primary-ecu-serial", bpo::value<std::string>(), "serial number of primary ecu")
      ("primary-ecu-hardware-id", bpo::value<std::string>(), "hardware ID of primary ecu")
      ("secondary-config-file", bpo::value<boost::filesystem::path>(), "secondary ECUs configuration file")
      ("campaign-id", bpo::value<std::string>(), "id of the campaign to act on");
  // clang-format on

  // consider the first positional argument as the aktualizr run mode
  bpo::positional_options_description pos;
  pos.add("run-mode", 1);

  bpo::variables_map vm;
  std::vector<std::string> unregistered_options;
  try {
    bpo::basic_parsed_options<char> parsed_options =
        bpo::command_line_parser(argc, argv).options(description).positional(pos).allow_unregistered().run();
    bpo::store(parsed_options, vm);
    checkInfoOptions(description, vm);
    bpo::notify(vm);
    unregistered_options = bpo::collect_unrecognized(parsed_options.options, bpo::exclude_positional);
    if (vm.count("help") == 0 && !unregistered_options.empty()) {
      std::cout << description << "\n";
      exit(EXIT_FAILURE);
    }
  } catch (const bpo::required_option &ex) {
    // print the error and append the default commandline option description
    std::cout << ex.what() << std::endl << description;
    exit(EXIT_FAILURE);
  } catch (const bpo::error &ex) {
    checkInfoOptions(description, vm);

    // log boost error
    LOG_ERROR << "boost command line option error: " << ex.what();

    // print the error message to the standard output too, as the user provided
    // a non-supported commandline option
    std::cout << ex.what() << '\n';

    // set the returnValue, thereby ctest will recognize
    // that something went wrong
    exit(EXIT_FAILURE);
  }

  return vm;
}

void processEvent(const std::shared_ptr<event::BaseEvent> &event) {
  if (event->isTypeOf<event::DownloadProgressReport>()) {
    // Do nothing; libaktualizr already logs it.
  } else if (event->variant == "UpdateCheckComplete") {
    // Do nothing; libaktualizr already logs it.
  } else {
    LOG_INFO << "got " << event->variant << " event";
  }
}

int main(int argc, char *argv[]) {
  logger_init();
  logger_set_threshold(boost::log::trivial::info);

  bpo::variables_map commandline_map = parseOptions(argc, argv);

  LOG_INFO << "Aktualizr version " << aktualizr_version() << " starting";

  int r = EXIT_FAILURE;

  try {
    if (geteuid() != 0) {
      LOG_WARNING << "\033[31mAktualizr is currently running as non-root and may not work as expected! Aktualizr "
                     "should be run as root for proper functionality.\033[0m\n";
    }
    Config config(commandline_map);
    LOG_DEBUG << "Current directory: " << boost::filesystem::current_path().string();

    Aktualizr aktualizr(config);
    std::function<void(std::shared_ptr<event::BaseEvent> event)> f_cb = processEvent;
    boost::signals2::scoped_connection conn;

    conn = aktualizr.SetSignalHandler(f_cb);

    if (!config.uptane.secondary_config_file.empty()) {
      if (boost::filesystem::exists(config.uptane.secondary_config_file)) {
        Primary::initSecondaries(aktualizr, config.uptane.secondary_config_file);
      } else {
        LOG_WARNING << "The specified secondary config file does not exist: " << config.uptane.secondary_config_file
                    << "\nProceed further without secondary(ies)";
      }
    }

    aktualizr.Initialize();

    // handle unix signals
    SigHandler::get().start([&aktualizr]() {
      aktualizr.Abort();
      aktualizr.Shutdown();
    });
    SigHandler::signal(SIGHUP);
    SigHandler::signal(SIGINT);
    SigHandler::signal(SIGTERM);

    std::string run_mode;
    if (commandline_map.count("run-mode") != 0) {
      run_mode = commandline_map["run-mode"].as<std::string>();
    }
    // launch the first event
    if (run_mode == "campaign_check") {
      aktualizr.CampaignCheck().get();
    } else if (run_mode == "campaign_accept" || run_mode == "campaign_decline" || run_mode == "campaign_postpone") {
      if (commandline_map.count("campaign-id") == 0) {
        throw std::runtime_error(run_mode + " requires a campaign ID");
      }
      aktualizr.CampaignControl(commandline_map["campaign-id"].as<std::string>(), campaign::cmdFromName(run_mode))
          .get();
    } else if (run_mode == "check") {
      aktualizr.SendDeviceData().get();
      aktualizr.CheckUpdates().get();
    } else if (run_mode == "download") {
      result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
      aktualizr.Download(update_result.updates).get();
    } else if (run_mode == "install") {
      result::UpdateCheck update_result = aktualizr.CheckUpdates().get();
      aktualizr.Install(update_result.updates).get();
    } else if (run_mode == "once") {
      aktualizr.SendDeviceData().get();
      aktualizr.UptaneCycle();
    } else {
      boost::signals2::connection ac_conn =
          aktualizr.SetSignalHandler(std::bind(targets_autoclean_cb, std::ref(aktualizr), std::placeholders::_1));

      try {
        aktualizr.RunForever().get();
      } catch (const std::exception &ex) {
        LOG_ERROR << ex.what();
      }

      LOG_DEBUG << "Aktualizr daemon exiting...";
    }
    r = EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    LOG_ERROR << ex.what();
  }

  return r;
}
<|endoftext|>


<commit_before>#include "stack.hpp"
#include <stdlib.h>
#include <string>
#include <chrono>
#include <thread>

template <typename T>
void producer(stack<T> &Stack)
{
	for(;;)
	{
		Stack.push(std::rand() % 100);
		std::this_thread::sleep_for(std::chrono::seconds(std::rand() % (3) + 1));
	}
}

void consumer(stack<T> &Stack)
{
	for(;;)
	{
		Stack.pop();
		std::this_thread::sleep_for(std::chrono::seconds(std::rand() % (3) + 2));
	}
}

int main()
{
	stack<int> Stack;

	std::thread prod(producer, std::ref(Stack));
	std::thread cons(consumer, std::ref(Stack));

	prod.join();
	cons.join();

	system("pause");
	return 0;
}
<commit_msg>Update stack.cpp<commit_after>#include "stack.hpp"
#include <string>
#include <chrono>
#include <thread>

template <typename T>
void producer(stack<T> &Stack)
{
	for(;;)
	{
		Stack.push(std::rand() % 100);
		std::this_thread::sleep_for(std::chrono::seconds(std::rand() % (3) + 1));
	}
}
template <typename T>
void consumer(stack<T> &Stack)
{
	for(;;)
	{
		try
		{
			Stack.pop();
		}
		catch(std::logic_error)
		{
			std::this_thread::sleep_for(std::chrono::seconds(std::rand() % (3) + 2));
		}
		std::this_thread::sleep_for(std::chrono::seconds(std::rand() % (3) + 2));
	}
}

int main()
{
	stack<int> Stack;

	std::thread prod(producer, std::ref(Stack));
	std::thread cons(consumer, std::ref(Stack));

	prod.join();
	cons.join();

	return 0;
}
<|endoftext|>


<commit_before>#include "x_client_thumbnail_gl.hpp"

#include <xcb/composite.h>
#include <xcb/xcb_renderutil.h>

x_client_thumbnail::x_client_thumbnail(x_connection & c,
                                       const rectangle & rect,
                                       const xcb_window_t & window,
                                       std::shared_ptr<x_client> xclient)
      : _c(c)
{
  if (window == XCB_NONE && xclient == NULL) {
    throw std::invalid_argument(
        "x_client_thumbnail requires either window or xclient parameter");
  } else if (xclient == NULL) {
    _x_client = x_client_ptr(new x_client(_c, window));
  } else {
    _x_client = xclient;
  }

  update_rectangle(rect);

  _c.register_handler(_c.damage_event_id(), this);

  _damage = xcb_generate_id(_c());

  _parent_pixmap = xcb_generate_id(_c());
  xcb_composite_name_window_pixmap(_c(), _x_client->window(), _parent_pixmap);

  _thumbnail_window = xcb_generate_id(_c());
  xcb_colormap_t colormap = xcb_generate_id(_c());

  GLint gl_vi_attr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
  XVisualInfo * vi =
    glXChooseVisual(_c.dpy(), DefaultScreen(_c.dpy()), gl_vi_attr);

  xcb_create_colormap(_c(), XCB_COLORMAP_ALLOC_NONE, colormap,
                      _c.root_window(), vi->visualid);

  uint32_t valuemask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP;
  uint32_t valuelist[] = { 1, colormap, 0 };
  xcb_create_window(_c(), XCB_COPY_FROM_PARENT, _thumbnail_window,
                    _c.root_window(), 0, 0,
                    _x_client->rect().width(), _x_client->rect().height(),
                    0, XCB_WINDOW_CLASS_INPUT_OUTPUT, vi->visualid,
                    valuemask, valuelist);

  xcb_free_colormap(_c(), colormap);

  configure_gl(vi);
  delete vi;

  update();
}

x_client_thumbnail::~x_client_thumbnail(void)
{
  _c.deregister_handler(_c.damage_event_id(), this);
  xcb_destroy_window(_c(), _thumbnail_window);
}

void
x_client_thumbnail::show(void)
{
  xcb_damage_create(_c(), _damage, _x_client->window(),
                    XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
  configure_thumbnail_window();
  update();
}

void
x_client_thumbnail::hide(void)
{
  xcb_damage_destroy(_c(), _damage);
  xcb_unmap_window(_c(), _thumbnail_window);
}

void
x_client_thumbnail::select(void)
{
  _c.request_change_current_desktop(_x_client->net_wm_desktop());
  _c.request_change_active_window(_x_client->window());
}

void
x_client_thumbnail::update(void)
{
  update(0, 0, _rectangle.width(), _rectangle.height());
}

void
x_client_thumbnail::update(int x, int y, unsigned int width, unsigned int height)
{
  glXMakeCurrent(_c.dpy(), _thumbnail_window, _gl_ctx);

  glViewport(0, 0, _rectangle.width(), _rectangle.height());
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glBegin(GL_QUADS);
  glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0, 0.0);
  glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0, 0.0);
  glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, -1.0, 0.0);
  glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0, 0.0);
  glEnd();

  glXSwapBuffers(_c.dpy(), _thumbnail_window);
}

void
x_client_thumbnail::highlight(bool want_highlight)
{
  update();
}

bool
x_client_thumbnail::handle(xcb_generic_event_t * ge)
{
  if (_c.damage_event_id() == (ge->response_type & ~0x80)) {
    xcb_damage_notify_event_t * e = (xcb_damage_notify_event_t *)ge;
    xcb_damage_subtract(_c(), e->damage, XCB_NONE, XCB_NONE);
    update(e->area.x * _scale, e->area.y * _scale,
           e->area.width * _scale, e->area.height * _scale);
    return true;
  }

  return false;
}

void
x_client_thumbnail::update_rectangle(const rectangle & rect)
{
  _scale = std::min((double)rect.width() / _x_client->rect().width(),
                    (double)rect.height() / _x_client->rect().height());

  _rectangle.x() = rect.x();
  _rectangle.y() = rect.y();
  _rectangle.width() = _x_client->rect().width() * _scale;
  _rectangle.height() = _x_client->rect().height() * _scale;
}

void
x_client_thumbnail::configure_thumbnail_window(void)
{
  uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
                | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT
                | XCB_CONFIG_WINDOW_STACK_MODE;

  uint32_t values[] = { (uint32_t)_rectangle.x(),
                        (uint32_t)_rectangle.y(),
                        (uint32_t)_rectangle.width(),
                        (uint32_t)_rectangle.height(),
                        XCB_STACK_MODE_ABOVE };

  xcb_configure_window(_c(), _thumbnail_window, mask, values);
  xcb_map_window(_c(), _thumbnail_window);
}

void
x_client_thumbnail::configure_gl(XVisualInfo * vi)
{
  const int pixmap_config[] = {
    GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
    GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
    GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
    GLX_DOUBLEBUFFER, False,
    GLX_Y_INVERTED_EXT,
    None
  };

  const int pixmap_attr[] = {
    GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
    GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
    None
  };

  if (vi == NULL) {
    GLint gl_vi_attr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
    vi = glXChooseVisual(_c.dpy(), DefaultScreen(_c.dpy()), gl_vi_attr);
    delete vi;
  }
  _gl_ctx = glXCreateContext(_c.dpy(), vi, NULL, GL_TRUE);

  glXMakeCurrent(_c.dpy(), _thumbnail_window, _gl_ctx);

  int config = 0;
  GLXFBConfig * _gl_configs =
    glXChooseFBConfig(_c.dpy(), 0, pixmap_config, &config);
  _thumbnail_gl_pixmap =
      glXCreatePixmap(_c.dpy(), _gl_configs[0], _parent_pixmap, pixmap_attr);
  delete _gl_configs;

  GLuint _thumbnail_gl_texture_id;
  glEnable(GL_TEXTURE_2D);
  glGenTextures(1, &_thumbnail_gl_texture_id);
  glBindTexture(GL_TEXTURE_2D, _thumbnail_gl_texture_id);
  _c.glXBindTexImageEXT(_c.dpy(), _thumbnail_gl_pixmap, GLX_FRONT_EXT, NULL);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

bool operator==(const x_client_thumbnail & thumbnail, const xcb_window_t & window)
{
  return *(thumbnail._x_client) == window;
}

bool operator==(const xcb_window_t & window, const x_client_thumbnail & thumbnail)
{
  return thumbnail == window;
}
<commit_msg>Do not make thumbnails larger than the original window<commit_after>#include "x_client_thumbnail_gl.hpp"

#include <xcb/composite.h>
#include <xcb/xcb_renderutil.h>

x_client_thumbnail::x_client_thumbnail(x_connection & c,
                                       const rectangle & rect,
                                       const xcb_window_t & window,
                                       std::shared_ptr<x_client> xclient)
      : _c(c)
{
  if (window == XCB_NONE && xclient == NULL) {
    throw std::invalid_argument(
        "x_client_thumbnail requires either window or xclient parameter");
  } else if (xclient == NULL) {
    _x_client = x_client_ptr(new x_client(_c, window));
  } else {
    _x_client = xclient;
  }

  update_rectangle(rect);

  _c.register_handler(_c.damage_event_id(), this);

  _damage = xcb_generate_id(_c());

  _parent_pixmap = xcb_generate_id(_c());
  xcb_composite_name_window_pixmap(_c(), _x_client->window(), _parent_pixmap);

  _thumbnail_window = xcb_generate_id(_c());
  xcb_colormap_t colormap = xcb_generate_id(_c());

  GLint gl_vi_attr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
  XVisualInfo * vi =
    glXChooseVisual(_c.dpy(), DefaultScreen(_c.dpy()), gl_vi_attr);

  xcb_create_colormap(_c(), XCB_COLORMAP_ALLOC_NONE, colormap,
                      _c.root_window(), vi->visualid);

  uint32_t valuemask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP;
  uint32_t valuelist[] = { 1, colormap, 0 };
  xcb_create_window(_c(), XCB_COPY_FROM_PARENT, _thumbnail_window,
                    _c.root_window(), 0, 0,
                    _x_client->rect().width(), _x_client->rect().height(),
                    0, XCB_WINDOW_CLASS_INPUT_OUTPUT, vi->visualid,
                    valuemask, valuelist);

  xcb_free_colormap(_c(), colormap);

  configure_gl(vi);
  delete vi;

  update();
}

x_client_thumbnail::~x_client_thumbnail(void)
{
  _c.deregister_handler(_c.damage_event_id(), this);
  xcb_destroy_window(_c(), _thumbnail_window);
}

void
x_client_thumbnail::show(void)
{
  xcb_damage_create(_c(), _damage, _x_client->window(),
                    XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
  configure_thumbnail_window();
  update();
}

void
x_client_thumbnail::hide(void)
{
  xcb_damage_destroy(_c(), _damage);
  xcb_unmap_window(_c(), _thumbnail_window);
}

void
x_client_thumbnail::select(void)
{
  _c.request_change_current_desktop(_x_client->net_wm_desktop());
  _c.request_change_active_window(_x_client->window());
}

void
x_client_thumbnail::update(void)
{
  update(0, 0, _rectangle.width(), _rectangle.height());
}

void
x_client_thumbnail::update(int x, int y, unsigned int width, unsigned int height)
{
  glXMakeCurrent(_c.dpy(), _thumbnail_window, _gl_ctx);

  glViewport(0, 0, _rectangle.width(), _rectangle.height());
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glBegin(GL_QUADS);
  glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0, 0.0);
  glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0, 0.0);
  glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, -1.0, 0.0);
  glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0, 0.0);
  glEnd();

  glXSwapBuffers(_c.dpy(), _thumbnail_window);
}

void
x_client_thumbnail::highlight(bool want_highlight)
{
  update();
}

bool
x_client_thumbnail::handle(xcb_generic_event_t * ge)
{
  if (_c.damage_event_id() == (ge->response_type & ~0x80)) {
    xcb_damage_notify_event_t * e = (xcb_damage_notify_event_t *)ge;
    xcb_damage_subtract(_c(), e->damage, XCB_NONE, XCB_NONE);
    update(e->area.x * _scale, e->area.y * _scale,
           e->area.width * _scale, e->area.height * _scale);
    return true;
  }

  return false;
}

void
x_client_thumbnail::update_rectangle(const rectangle & rect)
{
  _scale = std::min((double)rect.width() / _x_client->rect().width(),
                    (double)rect.height() / _x_client->rect().height());
  _scale = std::min(1.0, _scale);

  _rectangle.x() = rect.x();
  _rectangle.y() = rect.y();
  _rectangle.width() = _x_client->rect().width() * _scale;
  _rectangle.height() = _x_client->rect().height() * _scale;
}

void
x_client_thumbnail::configure_thumbnail_window(void)
{
  uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
                | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT
                | XCB_CONFIG_WINDOW_STACK_MODE;

  uint32_t values[] = { (uint32_t)_rectangle.x(),
                        (uint32_t)_rectangle.y(),
                        (uint32_t)_rectangle.width(),
                        (uint32_t)_rectangle.height(),
                        XCB_STACK_MODE_ABOVE };

  xcb_configure_window(_c(), _thumbnail_window, mask, values);
  xcb_map_window(_c(), _thumbnail_window);
}

void
x_client_thumbnail::configure_gl(XVisualInfo * vi)
{
  const int pixmap_config[] = {
    GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
    GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
    GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
    GLX_DOUBLEBUFFER, False,
    GLX_Y_INVERTED_EXT,
    None
  };

  const int pixmap_attr[] = {
    GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
    GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
    None
  };

  if (vi == NULL) {
    GLint gl_vi_attr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
    vi = glXChooseVisual(_c.dpy(), DefaultScreen(_c.dpy()), gl_vi_attr);
    delete vi;
  }
  _gl_ctx = glXCreateContext(_c.dpy(), vi, NULL, GL_TRUE);

  glXMakeCurrent(_c.dpy(), _thumbnail_window, _gl_ctx);

  int config = 0;
  GLXFBConfig * _gl_configs =
    glXChooseFBConfig(_c.dpy(), 0, pixmap_config, &config);
  _thumbnail_gl_pixmap =
      glXCreatePixmap(_c.dpy(), _gl_configs[0], _parent_pixmap, pixmap_attr);
  delete _gl_configs;

  GLuint _thumbnail_gl_texture_id;
  glEnable(GL_TEXTURE_2D);
  glGenTextures(1, &_thumbnail_gl_texture_id);
  glBindTexture(GL_TEXTURE_2D, _thumbnail_gl_texture_id);
  _c.glXBindTexImageEXT(_c.dpy(), _thumbnail_gl_pixmap, GLX_FRONT_EXT, NULL);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

bool operator==(const x_client_thumbnail & thumbnail, const xcb_window_t & window)
{
  return *(thumbnail._x_client) == window;
}

bool operator==(const xcb_window_t & window, const x_client_thumbnail & thumbnail)
{
  return thumbnail == window;
}
<|endoftext|>


<commit_before>#include "xchainer/backprop.h"

#include <vector>

#ifdef XCHAINER_ENABLE_CUDA
#include <cuda_runtime.h>
#endif  // XCHAINER_ENABLE_CUDA
#include <gtest/gtest.h>

#include "xchainer/array.h"
#include "xchainer/backprop.h"
#ifdef XCHAINER_ENABLE_CUDA
#include "xchainer/cuda/cuda_runtime.h"
#endif  // XCHAINER_ENABLE_CUDA
#include "xchainer/device.h"
#include "xchainer/dtype.h"
#include "xchainer/shape.h"

namespace xchainer {
namespace {

std::vector<Array> MakeFullArrays(const Shape& shape, const std::vector<float>& values, bool requires_grad) {
    std::vector<Array> ret;
    for (float value : values) {
        ret.push_back(Array::Full(shape, value));
        ret.back().set_requires_grad(requires_grad);
    }
    return ret;
}

class BackpropTest : public ::testing::TestWithParam<::testing::tuple<std::string>> {
protected:
    virtual void SetUp() {
        std::string device_name = ::testing::get<0>(GetParam());
        device_scope_ = std::make_unique<DeviceScope>(device_name);
    }

    virtual void TearDown() { device_scope_.reset(); }

public:
    template <typename T>
    void ExpectEqual(const Array& expected, const Array& actual) const {
        EXPECT_EQ(expected.dtype(), actual.dtype());
        EXPECT_EQ(expected.shape(), actual.shape());
        ExpectDataEqual<T>(expected, actual);
    }

    template <typename T>
    void ExpectDataEqual(const Array& expected, const Array& actual) const {
#ifdef XCHAINER_ENABLE_CUDA
        std::string device_name = ::testing::get<0>(GetParam());
        if (device_name == "cuda") {
            cuda::CheckError(cudaDeviceSynchronize());
        }
#endif  // XCHAINER_ENABLE_CUDA
        auto total_size = expected.shape().total_size();
        const T* expected_data = static_cast<const T*>(expected.data().get());
        const T* actual_data = static_cast<const T*>(actual.data().get());
        for (decltype(total_size) i = 0; i < total_size; i++) {
            EXPECT_EQ(expected_data[i], actual_data[i]);
        }
    }

    // Checks the correctness of Backward() applied to the output of a given function.
    // Gradients are only computed w.r.t. target_inputs, and are compared to expected_grads.
    template <typename Fprop>
    void CheckBackprop(std::vector<Array>& target_inputs, std::vector<Array>& other_inputs, std::vector<Array>& expected_grads,
                       Fprop&& fprop) const {
        EXPECT_EQ(expected_grads.size(), target_inputs.size());
        auto y = fprop(target_inputs, other_inputs);
        Backward(y);
        for (size_t i = 0; i < expected_grads.size(); ++i) {
            ExpectEqual<float>(expected_grads[i], *target_inputs[i].grad());
        }
    }

    // Simple version. It makes and uses an array with one element for each input.
    template <typename Fprop>
    void CheckBackprop(std::vector<float> target_inputs, std::vector<float> other_inputs, std::vector<float> expected_grads,
                       Fprop&& fprop) const {
        auto xs = MakeFullArrays({1}, target_inputs, true);
        auto other_xs = MakeFullArrays({1}, other_inputs, false);
        auto expected_gxs = MakeFullArrays({1}, expected_grads, false);
        CheckBackprop(xs, other_xs, expected_gxs, std::forward<Fprop>(fprop));
    }

private:
    std::unique_ptr<DeviceScope> device_scope_;
};

TEST_P(BackpropTest, Backward) {
    CheckBackprop({2.0f}, {3.0f}, {7.0f}, [](auto& xs, auto& ys) { return xs[0] * (xs[0] + ys[0]); });
}

TEST_P(BackpropTest, BackwardNoGrad) {
    auto x = Array::Full({1}, 2.0f);
    auto y = Array::Full({1}, 3.0f);
    auto z = x * y;
    Backward(z);
    EXPECT_FALSE(x.grad());
    EXPECT_FALSE(y.grad());
}

TEST_P(BackpropTest, DoubleBackprop) {
    auto fprop = [](auto& xs, auto& ys) {
        auto z = xs[0] * (xs[0] + ys[0]);
        Backward(z);
        auto gx = *xs[0].grad();
        xs[0].ClearGrad();
        return gx;
    };
    CheckBackprop({2.0f}, {3.0f}, {2.0f}, fprop);
}

TEST_P(BackpropTest, BackwardIdenticalInputs) {
    CheckBackprop({2.0f}, {}, {2.0f}, [](auto& xs, __attribute__((unused)) auto& ys) { return xs[0] + xs[0]; });
}

TEST_P(BackpropTest, BackwardGivenInputGrad) {
    auto fprop = [](auto& xs, __attribute__((unused)) auto& ys) {
        xs[0].set_grad(Array::OnesLike(xs[0]));
        return xs[0];
    };
    CheckBackprop({1.0f}, {}, {2.0f}, fprop);
}

TEST_P(BackpropTest, BackwardGivenOutputGrad) {
    auto x = Array::Ones({1}, TypeToDtype<float>);
    x.set_requires_grad(true);
    auto y = x;
    y.set_grad(Array::FullLike(y, 2.0f));
    Backward(y);
    auto e = Array::FullLike(x, 2.0f);
    ExpectEqual<float>(e, *x.grad());
}

INSTANTIATE_TEST_CASE_P(ForEachDevice, BackpropTest, ::testing::Values(
#ifdef XCHAINER_ENABLE_CUDA
                                                         std::string{"cuda"},
#endif  // XCHAINER_ENABLE_CUDA
                                                         std::string{"cpu"}));

}  // namespace
}  // namespace xchainer
<commit_msg>Split tests<commit_after>#include "xchainer/backprop.h"

#include <vector>

#ifdef XCHAINER_ENABLE_CUDA
#include <cuda_runtime.h>
#endif  // XCHAINER_ENABLE_CUDA
#include <gtest/gtest.h>

#include "xchainer/array.h"
#include "xchainer/backprop.h"
#ifdef XCHAINER_ENABLE_CUDA
#include "xchainer/cuda/cuda_runtime.h"
#endif  // XCHAINER_ENABLE_CUDA
#include "xchainer/device.h"
#include "xchainer/dtype.h"
#include "xchainer/shape.h"

namespace xchainer {
namespace {

std::vector<Array> MakeFullArrays(const Shape& shape, const std::vector<float>& values, bool requires_grad) {
    std::vector<Array> ret;
    for (float value : values) {
        ret.push_back(Array::Full(shape, value));
        ret.back().set_requires_grad(requires_grad);
    }
    return ret;
}

class BackpropTest : public ::testing::TestWithParam<::testing::tuple<std::string>> {
protected:
    virtual void SetUp() {
        std::string device_name = ::testing::get<0>(GetParam());
        device_scope_ = std::make_unique<DeviceScope>(device_name);
    }

    virtual void TearDown() { device_scope_.reset(); }

public:
    template <typename T>
    void ExpectEqual(const Array& expected, const Array& actual) const {
        EXPECT_EQ(expected.dtype(), actual.dtype());
        EXPECT_EQ(expected.shape(), actual.shape());
        ExpectDataEqual<T>(expected, actual);
    }

    template <typename T>
    void ExpectDataEqual(const Array& expected, const Array& actual) const {
#ifdef XCHAINER_ENABLE_CUDA
        std::string device_name = ::testing::get<0>(GetParam());
        if (device_name == "cuda") {
            cuda::CheckError(cudaDeviceSynchronize());
        }
#endif  // XCHAINER_ENABLE_CUDA
        auto total_size = expected.shape().total_size();
        const T* expected_data = static_cast<const T*>(expected.data().get());
        const T* actual_data = static_cast<const T*>(actual.data().get());
        for (decltype(total_size) i = 0; i < total_size; i++) {
            EXPECT_EQ(expected_data[i], actual_data[i]);
        }
    }

    // Checks the correctness of Backward() applied to the output of a given function.
    // Gradients are only computed w.r.t. target_inputs, and are compared to expected_grads.
    template <typename Fprop>
    void CheckBackprop(std::vector<Array>& target_inputs, std::vector<Array>& other_inputs, std::vector<Array>& expected_grads,
                       Fprop&& fprop) const {
        EXPECT_EQ(expected_grads.size(), target_inputs.size());
        auto y = fprop(target_inputs, other_inputs);
        Backward(y);
        for (size_t i = 0; i < expected_grads.size(); ++i) {
            ExpectEqual<float>(expected_grads[i], *target_inputs[i].grad());
        }
    }

    // Simple version. It makes and uses an array with one element for each input.
    template <typename Fprop>
    void CheckBackprop(std::vector<float> target_inputs, std::vector<float> other_inputs, std::vector<float> expected_grads,
                       Fprop&& fprop) const {
        auto xs = MakeFullArrays({1}, target_inputs, true);
        auto other_xs = MakeFullArrays({1}, other_inputs, false);
        auto expected_gxs = MakeFullArrays({1}, expected_grads, false);
        CheckBackprop(xs, other_xs, expected_gxs, std::forward<Fprop>(fprop));
    }

private:
    std::unique_ptr<DeviceScope> device_scope_;
};

TEST_P(BackpropTest, Backward) {
    CheckBackprop({2.0f, 3.0f}, {4.0f}, {3.0f, 6.0f}, [](auto& xs, auto& ys) { return xs[1] * (xs[0] + ys[0]); });
}

TEST_P(BackpropTest, BackwardNoGrad) {
    auto x = Array::Full({1}, 2.0f);
    auto y = Array::Full({1}, 3.0f);
    auto z = x * y;
    Backward(z);
    EXPECT_FALSE(x.grad());
    EXPECT_FALSE(y.grad());
}

TEST_P(BackpropTest, DoubleBackprop) {
    auto fprop = [](auto& xs, auto& ys) {
        auto z = xs[0] * (xs[0] + ys[0]);
        Backward(z);
        auto gx = *xs[0].grad();
        xs[0].ClearGrad();
        return gx;
    };
    CheckBackprop({2.0f}, {3.0f}, {2.0f}, fprop);
}

TEST_P(BackpropTest, BackwardInputToMultipleOps) {
    CheckBackprop({2.0f}, {3.0f}, {7.0f}, [](auto& xs, auto& ys) { return xs[0] * (xs[0] + ys[0]); });
}

TEST_P(BackpropTest, BackwardIdenticalInputs) {
    CheckBackprop({2.0f}, {}, {2.0f}, [](auto& xs, __attribute__((unused)) auto& ys) { return xs[0] + xs[0]; });
}

TEST_P(BackpropTest, BackwardGivenInputGrad) {
    auto fprop = [](auto& xs, __attribute__((unused)) auto& ys) {
        xs[0].set_grad(Array::OnesLike(xs[0]));
        return xs[0];
    };
    CheckBackprop({1.0f}, {}, {2.0f}, fprop);
}

TEST_P(BackpropTest, BackwardGivenOutputGrad) {
    auto x = Array::Ones({1}, TypeToDtype<float>);
    x.set_requires_grad(true);
    auto y = x;
    y.set_grad(Array::FullLike(y, 2.0f));
    Backward(y);
    auto e = Array::FullLike(x, 2.0f);
    ExpectEqual<float>(e, *x.grad());
}

INSTANTIATE_TEST_CASE_P(ForEachDevice, BackpropTest, ::testing::Values(
#ifdef XCHAINER_ENABLE_CUDA
                                                         std::string{"cuda"},
#endif  // XCHAINER_ENABLE_CUDA
                                                         std::string{"cpu"}));

}  // namespace
}  // namespace xchainer
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Build Suite.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include <logging/consolelogger.h>

#include <QCoreApplication>
#include <QtDebug>
#include <QStringList>

#include <iostream>

#include "setupqt.h"

using namespace qbs;

static void printWrongQMakePath(const QString &qmakePath)
{
    qbsError() << QCoreApplication::translate("SetupQt", "Invalid path to qmake: %1").arg(qmakePath);
}

static void printHelp()
{
    std::cout << QCoreApplication::translate("SetupQt", "usage: qbs-setup-qt [qmake path [profile name]]").toStdString() << std::endl;
    std::cout << QCoreApplication::translate("SetupQt", "With --detect all qmakes in the PATH will be searched.").toStdString() << std::endl;
}

int main(int argc, char *argv[])
{
    QCoreApplication application(argc, argv);
    ConsoleLogger cl;

    try {
        if (argc == 1) {
            printHelp();
            return 0;
        } else if (argc == 2) {
            if (application.arguments()[1] == QLatin1String("--help") || application.arguments()[1] == QLatin1String("-h")) {
                printHelp();
            } else if (application.arguments()[1] == QLatin1String("--detect")) {
                // search all Qt's in path and dump their settings
                QList<QtEnviroment> qtEnvironments = SetupQt::fetchEnviroments();

                foreach (const QtEnviroment &qtEnvironment, qtEnvironments) {
                    QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion;
                    if (SetupQt::checkIfMoreThanOneQtWithTheSameVersion(qtEnvironment.qtVersion, qtEnvironments))
                        profileName += QLatin1String("-") + qtEnvironment.installPrefixPath.split("/").last();
                    SetupQt::saveToQbsSettings(profileName, qtEnvironment);
                }
            } else {
                QString qmakePath = application.arguments()[1];
                if (!SetupQt::isQMakePathValid(qmakePath)) {
                    printWrongQMakePath(qmakePath);
                    return 1;
                }
                QtEnviroment qtEnvironment = SetupQt::fetchEnviroment(qmakePath);
                QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion;
                profileName.replace(".", "-");
                SetupQt::saveToQbsSettings(profileName , qtEnvironment);
            }
        } else if (argc == 3) {
            QString qmakePath = application.arguments()[1];
            if (!SetupQt::isQMakePathValid(qmakePath)) {
                printWrongQMakePath(qmakePath);
                return 1;
            }
            QtEnviroment qtEnvironment = SetupQt::fetchEnviroment(qmakePath);
            QString profileName = application.arguments()[2];
            profileName.replace(".", "-");
            SetupQt::saveToQbsSettings(profileName , qtEnvironment);
        } else {
            printHelp();
        }
    } catch (const Error &e) {
        qbsError() << "qbs-setup-qt: " << qPrintable(e.toString());
        return EXIT_FAILURE;
    }

    return 0;
}
<commit_msg>Only add a suffix to the Qt profile name if the suffix is not empty.<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Build Suite.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include <logging/consolelogger.h>

#include <QCoreApplication>
#include <QtDebug>
#include <QStringList>

#include <iostream>

#include "setupqt.h"

using namespace qbs;

static void printWrongQMakePath(const QString &qmakePath)
{
    qbsError() << QCoreApplication::translate("SetupQt", "Invalid path to qmake: %1").arg(qmakePath);
}

static void printHelp()
{
    std::cout << QCoreApplication::translate("SetupQt", "usage: qbs-setup-qt [qmake path [profile name]]").toStdString() << std::endl;
    std::cout << QCoreApplication::translate("SetupQt", "With --detect all qmakes in the PATH will be searched.").toStdString() << std::endl;
}

int main(int argc, char *argv[])
{
    QCoreApplication application(argc, argv);
    ConsoleLogger cl;

    try {
        if (argc == 1) {
            printHelp();
            return 0;
        } else if (argc == 2) {
            if (application.arguments()[1] == QLatin1String("--help") || application.arguments()[1] == QLatin1String("-h")) {
                printHelp();
            } else if (application.arguments()[1] == QLatin1String("--detect")) {
                // search all Qt's in path and dump their settings
                QList<QtEnviroment> qtEnvironments = SetupQt::fetchEnviroments();

                foreach (const QtEnviroment &qtEnvironment, qtEnvironments) {
                    QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion;
                    if (SetupQt::checkIfMoreThanOneQtWithTheSameVersion(qtEnvironment.qtVersion, qtEnvironments)) {
                        QStringList prefixPathParts = qtEnvironment.installPrefixPath.split("/", QString::SkipEmptyParts);
                        if (!prefixPathParts.isEmpty())
                            profileName += QLatin1String("-") + prefixPathParts.last();
                    }

                    SetupQt::saveToQbsSettings(profileName, qtEnvironment);
                }
            } else {
                QString qmakePath = application.arguments()[1];
                if (!SetupQt::isQMakePathValid(qmakePath)) {
                    printWrongQMakePath(qmakePath);
                    return 1;
                }
                QtEnviroment qtEnvironment = SetupQt::fetchEnviroment(qmakePath);
                QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion;
                profileName.replace(".", "-");
                SetupQt::saveToQbsSettings(profileName , qtEnvironment);
            }
        } else if (argc == 3) {
            QString qmakePath = application.arguments()[1];
            if (!SetupQt::isQMakePathValid(qmakePath)) {
                printWrongQMakePath(qmakePath);
                return 1;
            }
            QtEnviroment qtEnvironment = SetupQt::fetchEnviroment(qmakePath);
            QString profileName = application.arguments()[2];
            profileName.replace(".", "-");
            SetupQt::saveToQbsSettings(profileName , qtEnvironment);
        } else {
            printHelp();
        }
    } catch (const Error &e) {
        qbsError() << "qbs-setup-qt: " << qPrintable(e.toString());
        return EXIT_FAILURE;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include "settings/Settings.hpp"
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSaveFile>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QStringBuilder>
#include <QApplication>

namespace Kryvo {
  const QStringList kDefaultPaths =
    QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
  const QString kDefaultPath = QString{kDefaultPaths.first() %
                                       QDir::separator()};
}

class Kryvo::SettingsPrivate {
  Q_DISABLE_COPY(SettingsPrivate)

 public:
  SettingsPrivate();

  /*!
   * \brief importSettings Imports settings from the local settings file
   */
  void importSettings();

  /*!
   * \brief exportSettings Exports settings to the local settings file
   */
  void exportSettings() const;

  QPoint position;
  bool maximized;
  QSize size;
  QString cipher;
  std::size_t keySize;
  QString modeOfOperation;
  bool compressionMode;
  bool containerMode;
  QString outputPath;
  QString lastOpenPath;
  QString styleSheetPath;
};

Kryvo::Settings::Settings()
  : d_ptr{std::make_unique<SettingsPrivate>()} {
  Q_D(Settings);

  d->importSettings();
}

Kryvo::Settings::~Settings() {
  Q_D(Settings);

  d->exportSettings();
}

void Kryvo::Settings::position(const QPoint& position) {
  Q_D(Settings);

  d->position = position;
}

QPoint Kryvo::Settings::position() const {
  Q_D(const Settings);

  return d->position;
}

void Kryvo::Settings::maximized(const bool maximized) {
  Q_D(Settings);

  d->maximized = maximized;
}

bool Kryvo::Settings::maximized() const {
  Q_D(const Settings);

  return d->maximized;
}

void Kryvo::Settings::size(const QSize& size) {
  Q_D(Settings);

  d->size = size;
}

QSize Kryvo::Settings::size() const {
  Q_D(const Settings);

  return d->size;
}

void Kryvo::Settings::cipher(const QString& cipherName) {
  Q_D(Settings);

  d->cipher = cipherName;
}

QString Kryvo::Settings::cipher() const {
  Q_D(const Settings);

  return d->cipher;
}

void Kryvo::Settings::keySize(const std::size_t keySize) {
  Q_D(Settings);

  d->keySize = keySize;
}

std::size_t Kryvo::Settings::keySize() const {
  Q_D(const Settings);

  return d->keySize;
}

void Kryvo::Settings::modeOfOperation(const QString& modeOfOperation) {
  Q_D(Settings);

  d->modeOfOperation = modeOfOperation;
}

QString Kryvo::Settings::modeOfOperation() const {
  Q_D(const Settings);

  return d->modeOfOperation;
}

void Kryvo::Settings::compressionMode(const bool compress) {
  Q_D(Settings);

  d->compressionMode = compress;
}

bool Kryvo::Settings::compressionMode() const {
  Q_D(const Settings);

  return d->compressionMode;
}

void Kryvo::Settings::containerMode(const bool container) {
  Q_D(Settings);

  d->containerMode = container;
}

bool Kryvo::Settings::containerMode() const {
  Q_D(const Settings);

  return d->containerMode;
}

void Kryvo::Settings::outputPath(const QString& path) {
  Q_D(Settings);

  d->outputPath = path;
}

QString Kryvo::Settings::outputPath() const {
  Q_D(const Settings);

  return d->outputPath;
}

void Kryvo::Settings::lastOpenPath(const QString& path) {
  Q_D(Settings);

  d->lastOpenPath = path;
}

QString Kryvo::Settings::lastOpenPath() const {
  Q_D(const Settings);

  return d->lastOpenPath;
}

QString Kryvo::Settings::styleSheetPath() const {
  Q_D(const Settings);

  return d->styleSheetPath;
}

Kryvo::SettingsPrivate::SettingsPrivate()
  : maximized{false}, keySize{std::size_t{128}}, compressionMode{true},
    containerMode{false}, outputPath{kDefaultPath},
    lastOpenPath{kDefaultPath} {
}

void Kryvo::SettingsPrivate::importSettings() {
#if defined(Q_OS_MACOS)
  const QString settingsPath = qApp->applicationDirPath() %
                               QDir::separator() %
                               QStringLiteral("settings.json");
#else
  const QString settingsPath = QStringLiteral("settings.json");
#endif

  QFile settingsFile{settingsPath};
  const bool fileOpen = settingsFile.open(QIODevice::ReadOnly);

  if (fileOpen) {
    const QByteArray settingsData = settingsFile.readAll();

    const QJsonDocument settingsDoc = QJsonDocument::fromJson(settingsData);
    const QJsonObject settings = settingsDoc.object();

    const QJsonObject positionObject =
      settings[QStringLiteral("position")].toObject();
    position = QPoint{positionObject[QStringLiteral("x")].toInt(100),
                      positionObject[QStringLiteral("y")].toInt(100)};

    maximized = settings[QStringLiteral("maximized")].toBool(false);

    if (!maximized) {
      const QJsonObject sizeObject = settings["size"].toObject();
      size = QSize{sizeObject[QStringLiteral("width")].toInt(800),
                   sizeObject[QStringLiteral("height")].toInt(600)};
    }

    cipher = settings[QStringLiteral("cipher")].toString(QStringLiteral("AES"));

    keySize =
      static_cast<std::size_t>(settings[QStringLiteral("keySize")].toInt(128));

    const QJsonValue modeOfOperationObject =
        settings[QStringLiteral("modeOfOperation")];
    modeOfOperation = modeOfOperationObject.toString(QStringLiteral("GCM"));

    compressionMode = settings[QStringLiteral("compressionMode")].toBool(true);

    containerMode = settings[QStringLiteral("containerMode")].toBool(true);

    outputPath = settings[QStringLiteral("outputPath")].toString(kDefaultPath);

    const QString lastOpen =
      settings[QStringLiteral("lastOpenPath")].toString(kDefaultPath);

    const QFileInfo lastOpenInfo{lastOpen};

    if (lastOpenInfo.exists() && lastOpenInfo.isDir()) {
      lastOpenPath = lastOpenInfo.absolutePath() % QDir::separator();
    }
    else {
      lastOpenPath = kDefaultPath;
    }

    const QJsonValue styleObject = settings[QStringLiteral("styleSheetPath")];
    styleSheetPath = styleObject.toString(QStringLiteral("kryvo.qss"));
  }
  else { // Settings file couldn't be opened, so use defaults
    position = QPoint{100, 100};
    maximized = false;
    size = QSize{800, 600};
    cipher = QStringLiteral("AES");
    keySize = std::size_t{128};
    modeOfOperation = QStringLiteral("GCM");
    compressionMode = true;
    containerMode = true;
    outputPath = kDefaultPath;
    lastOpenPath = kDefaultPath;
    styleSheetPath = QStringLiteral("kryvo.qss");
  }
}

void Kryvo::SettingsPrivate::exportSettings() const {
#if defined(Q_OS_MAC)
  const QString settingsPath = QCoreApplication::applicationDirPath() %
                               QDir::separator() %
                               QStringLiteral("settings.json");
#else
  const QString settingsPath = QStringLiteral("settings.json");
#endif

  QSaveFile settingsFile{settingsPath};
  settingsFile.setDirectWriteFallback(true);
  const bool fileOpen = settingsFile.open(QIODevice::WriteOnly);

  if (fileOpen) {
    QJsonObject settings;

    QJsonObject positionObject;
    positionObject[QStringLiteral("x")] = position.x();
    positionObject[QStringLiteral("y")] = position.y();

    settings[QStringLiteral("position")] = positionObject;
    settings[QStringLiteral("maximized")] = maximized;

    if (!maximized) {
      QJsonObject sizeObject;
      sizeObject[QStringLiteral("width")] = size.width();
      sizeObject[QStringLiteral("height")] = size.height();
      settings[QStringLiteral("size")] = sizeObject;
    }

    settings[QStringLiteral("cipher")] = cipher;
    settings[QStringLiteral("keySize")] = static_cast<int>(keySize);
    settings[QStringLiteral("modeOfOperation")] = modeOfOperation;
    settings[QStringLiteral("compressionMode")] = compressionMode;
    settings[QStringLiteral("containerMode")] = containerMode;

    auto addPathSeparator = [] (const QString& inPath) {
      const QString cleanedPath = QDir::cleanPath(inPath);

      const QFileInfo cleanedPathInfo{cleanedPath};

      QString outPath = cleanedPath;

      if (cleanedPathInfo.isDir() && !cleanedPathInfo.isRoot()) {
        outPath = cleanedPath % QDir::separator();
      }

      return outPath;
    };

    settings[QStringLiteral("outputPath")] =
      outputPath.isEmpty() ? kDefaultPath : addPathSeparator(outputPath);
    settings[QStringLiteral("lastOpenPath")] =
      lastOpenPath.isEmpty() ? kDefaultPath : addPathSeparator(lastOpenPath);
    settings[QStringLiteral("styleSheetPath")] =
      addPathSeparator(styleSheetPath);

    const QJsonDocument settingsDoc{settings};
    settingsFile.write(settingsDoc.toJson());
  }

  settingsFile.commit();
}
<commit_msg>Improve const correctness<commit_after>#include "settings/Settings.hpp"
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSaveFile>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QStringBuilder>
#include <QApplication>

namespace Kryvo {
  const QStringList kDefaultPaths =
    QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
  const QString kDefaultPath = QString{kDefaultPaths.first() %
                                       QDir::separator()};
}

class Kryvo::SettingsPrivate {
  Q_DISABLE_COPY(SettingsPrivate)

 public:
  SettingsPrivate();

  /*!
   * \brief importSettings Imports settings from the local settings file
   */
  void importSettings();

  /*!
   * \brief exportSettings Exports settings to the local settings file
   */
  void exportSettings() const;

  QPoint position;
  bool maximized;
  QSize size;
  QString cipher;
  std::size_t keySize;
  QString modeOfOperation;
  bool compressionMode;
  bool containerMode;
  QString outputPath;
  QString lastOpenPath;
  QString styleSheetPath;
};

Kryvo::Settings::Settings()
  : d_ptr{std::make_unique<SettingsPrivate>()} {
  Q_D(Settings);

  d->importSettings();
}

Kryvo::Settings::~Settings() {
  Q_D(Settings);

  d->exportSettings();
}

void Kryvo::Settings::position(const QPoint& position) {
  Q_D(Settings);

  d->position = position;
}

QPoint Kryvo::Settings::position() const {
  Q_D(const Settings);

  return d->position;
}

void Kryvo::Settings::maximized(const bool maximized) {
  Q_D(Settings);

  d->maximized = maximized;
}

bool Kryvo::Settings::maximized() const {
  Q_D(const Settings);

  return d->maximized;
}

void Kryvo::Settings::size(const QSize& size) {
  Q_D(Settings);

  d->size = size;
}

QSize Kryvo::Settings::size() const {
  Q_D(const Settings);

  return d->size;
}

void Kryvo::Settings::cipher(const QString& cipherName) {
  Q_D(Settings);

  d->cipher = cipherName;
}

QString Kryvo::Settings::cipher() const {
  Q_D(const Settings);

  return d->cipher;
}

void Kryvo::Settings::keySize(const std::size_t keySize) {
  Q_D(Settings);

  d->keySize = keySize;
}

std::size_t Kryvo::Settings::keySize() const {
  Q_D(const Settings);

  return d->keySize;
}

void Kryvo::Settings::modeOfOperation(const QString& modeOfOperation) {
  Q_D(Settings);

  d->modeOfOperation = modeOfOperation;
}

QString Kryvo::Settings::modeOfOperation() const {
  Q_D(const Settings);

  return d->modeOfOperation;
}

void Kryvo::Settings::compressionMode(const bool compress) {
  Q_D(Settings);

  d->compressionMode = compress;
}

bool Kryvo::Settings::compressionMode() const {
  Q_D(const Settings);

  return d->compressionMode;
}

void Kryvo::Settings::containerMode(const bool container) {
  Q_D(Settings);

  d->containerMode = container;
}

bool Kryvo::Settings::containerMode() const {
  Q_D(const Settings);

  return d->containerMode;
}

void Kryvo::Settings::outputPath(const QString& path) {
  Q_D(Settings);

  d->outputPath = path;
}

QString Kryvo::Settings::outputPath() const {
  Q_D(const Settings);

  return d->outputPath;
}

void Kryvo::Settings::lastOpenPath(const QString& path) {
  Q_D(Settings);

  d->lastOpenPath = path;
}

QString Kryvo::Settings::lastOpenPath() const {
  Q_D(const Settings);

  return d->lastOpenPath;
}

QString Kryvo::Settings::styleSheetPath() const {
  Q_D(const Settings);

  return d->styleSheetPath;
}

Kryvo::SettingsPrivate::SettingsPrivate()
  : maximized{false}, keySize{std::size_t{128}}, compressionMode{true},
    containerMode{false}, outputPath{kDefaultPath},
    lastOpenPath{kDefaultPath} {
}

void Kryvo::SettingsPrivate::importSettings() {
#if defined(Q_OS_MACOS)
  const QString settingsPath = qApp->applicationDirPath() %
                               QDir::separator() %
                               QStringLiteral("settings.json");
#else
  const QString settingsPath = QStringLiteral("settings.json");
#endif

  QFile settingsFile{settingsPath};
  const bool fileOpen = settingsFile.open(QIODevice::ReadOnly);

  if (fileOpen) {
    const QByteArray settingsData = settingsFile.readAll();

    const QJsonDocument settingsDoc = QJsonDocument::fromJson(settingsData);
    const QJsonObject settings = settingsDoc.object();

    const QJsonObject positionObject =
      settings[QStringLiteral("position")].toObject();
    position = QPoint{positionObject[QStringLiteral("x")].toInt(100),
                      positionObject[QStringLiteral("y")].toInt(100)};

    maximized = settings[QStringLiteral("maximized")].toBool(false);

    if (!maximized) {
      const QJsonObject sizeObject = settings["size"].toObject();
      size = QSize{sizeObject[QStringLiteral("width")].toInt(800),
                   sizeObject[QStringLiteral("height")].toInt(600)};
    }

    cipher = settings[QStringLiteral("cipher")].toString(QStringLiteral("AES"));

    keySize =
      static_cast<std::size_t>(settings[QStringLiteral("keySize")].toInt(128));

    const QJsonValue modeOfOperationObject =
        settings[QStringLiteral("modeOfOperation")];
    modeOfOperation = modeOfOperationObject.toString(QStringLiteral("GCM"));

    compressionMode = settings[QStringLiteral("compressionMode")].toBool(true);

    containerMode = settings[QStringLiteral("containerMode")].toBool(true);

    outputPath = settings[QStringLiteral("outputPath")].toString(kDefaultPath);

    const QString lastOpen =
      settings[QStringLiteral("lastOpenPath")].toString(kDefaultPath);

    const QFileInfo lastOpenInfo{lastOpen};

    if (lastOpenInfo.exists() && lastOpenInfo.isDir()) {
      lastOpenPath = lastOpenInfo.absolutePath() % QDir::separator();
    }
    else {
      lastOpenPath = kDefaultPath;
    }

    const QJsonValue styleObject = settings[QStringLiteral("styleSheetPath")];
    styleSheetPath = styleObject.toString(QStringLiteral("kryvo.qss"));
  }
  else { // Settings file couldn't be opened, so use defaults
    position = QPoint{100, 100};
    maximized = false;
    size = QSize{800, 600};
    cipher = QStringLiteral("AES");
    keySize = std::size_t{128};
    modeOfOperation = QStringLiteral("GCM");
    compressionMode = true;
    containerMode = true;
    outputPath = kDefaultPath;
    lastOpenPath = kDefaultPath;
    styleSheetPath = QStringLiteral("kryvo.qss");
  }
}

void Kryvo::SettingsPrivate::exportSettings() const {
#if defined(Q_OS_MAC)
  const QString settingsPath = QCoreApplication::applicationDirPath() %
                               QDir::separator() %
                               QStringLiteral("settings.json");
#else
  const QString settingsPath = QStringLiteral("settings.json");
#endif

  QSaveFile settingsFile{settingsPath};
  settingsFile.setDirectWriteFallback(true);
  const bool fileOpen = settingsFile.open(QIODevice::WriteOnly);

  if (fileOpen) {
    QJsonObject settings;

    QJsonObject positionObject;
    positionObject[QStringLiteral("x")] = position.x();
    positionObject[QStringLiteral("y")] = position.y();

    settings[QStringLiteral("position")] = positionObject;
    settings[QStringLiteral("maximized")] = maximized;

    if (!maximized) {
      QJsonObject sizeObject;
      sizeObject[QStringLiteral("width")] = size.width();
      sizeObject[QStringLiteral("height")] = size.height();
      settings[QStringLiteral("size")] = sizeObject;
    }

    settings[QStringLiteral("cipher")] = cipher;
    settings[QStringLiteral("keySize")] = static_cast<int>(keySize);
    settings[QStringLiteral("modeOfOperation")] = modeOfOperation;
    settings[QStringLiteral("compressionMode")] = compressionMode;
    settings[QStringLiteral("containerMode")] = containerMode;

    const auto addPathSeparator = [] (const QString& inPath) {
      const QString cleanedPath = QDir::cleanPath(inPath);

      const QFileInfo cleanedPathInfo{cleanedPath};

      QString outPath = cleanedPath;

      if (cleanedPathInfo.isDir() && !cleanedPathInfo.isRoot()) {
        outPath = cleanedPath % QDir::separator();
      }

      return outPath;
    };

    settings[QStringLiteral("outputPath")] =
      outputPath.isEmpty() ? kDefaultPath : addPathSeparator(outputPath);
    settings[QStringLiteral("lastOpenPath")] =
      lastOpenPath.isEmpty() ? kDefaultPath : addPathSeparator(lastOpenPath);
    settings[QStringLiteral("styleSheetPath")] =
      addPathSeparator(styleSheetPath);

    const QJsonDocument settingsDoc{settings};
    settingsFile.write(settingsDoc.toJson());
  }

  settingsFile.commit();
}
<|endoftext|>


<commit_before>/*
 *  calstep_dialog.cpp
 *  PHD Guiding
 *
 *  Created by Bruce Waddington
 *  Copyright (c) 2013 Bruce Waddington
 *  All rights reserved.
 *
 *  This source code is distributed under the following "BSD" license
 *  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 Bret McKee, Dad Dog Development,
 *     Craig Stark, Stark Labs nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "phd.h"
#include "calstep_dialog.h"
#include "wx/valnum.h"

#define MIN_PIXELSIZE 0.1
#define MAX_PIXELSIZE 25.0
#define MIN_GUIDESPEED 0.10
#define MAX_GUIDESPEED 2.0
#define MIN_STEPS 6.0
#define MAX_STEPS 60.0
#define MIN_DECLINATION -60.0
#define MAX_DECLINATION 60.0

const double CalstepDialog::DEFAULT_GUIDESPEED = 0.5;  // 50% sidereal rate

static wxSpinCtrlDouble *NewSpinner(wxWindow *parent, int width, double val, double minval, double maxval, double inc)
{
    wxSpinCtrlDouble *pNewCtrl = new wxSpinCtrlDouble(parent, wxID_ANY, _T("foo2"), wxPoint(-1, -1),
        wxSize(width, -1), wxSP_ARROW_KEYS, minval, maxval, val, inc);
    pNewCtrl->SetDigits(2);
    return pNewCtrl;
}

CalstepDialog::CalstepDialog(wxWindow *parent, int focalLength, double pixelSize) :
    wxDialog(parent, wxID_ANY, _("Calibration Step Calculator"), wxDefaultPosition, wxSize(400, 500), wxCAPTION | wxCLOSE_BOX)
{
    double dGuideRateDec = 0.0; // initialize to suppress compiler warning
    double dGuideRateRA = 0.0; // initialize to suppress compiler warning
    const double dSiderealSecondPerSec = 0.9973;

    // Get squared away with initial parameter values
    m_iNumSteps = pConfig->Profile.GetInt("/CalStepCalc/NumSteps", DEFAULT_STEPS);
    m_dDeclination = pConfig->Profile.GetDouble ("/CalStepCalc/CalDeclination", 0.0);
    m_iFocalLength = focalLength;
    m_fPixelSize = pixelSize;

    // Get the guide rate from the mount or from the config file
    m_fGuideSpeed = (float) pConfig->Profile.GetDouble ("/CalStepCalc/GuideSpeed", DEFAULT_GUIDESPEED);

    if (pPointingSource && pPointingSource->IsConnected() &&
        !pPointingSource->GetGuideRates(&dGuideRateRA, &dGuideRateDec))
    {
        if (dGuideRateRA >= dGuideRateDec)
            m_fGuideSpeed = dGuideRateRA * 3600.0 / (15.0 * dSiderealSecondPerSec);  // Degrees/sec to Degrees/hour, 15 degrees/hour is roughly sidereal rate
        else
            m_fGuideSpeed = dGuideRateDec / (15.0 * dSiderealSecondPerSec);

        if (m_fGuideSpeed < MIN_GUIDESPEED)
            m_fGuideSpeed = MIN_GUIDESPEED;
    }

    m_bValidResult = false;

    // Create the sizers we're going to need
    m_pVSizer = new wxBoxSizer(wxVERTICAL);
    m_pInputTableSizer = new wxFlexGridSizer (2, 2, 15, 15);
    m_pOutputTableSizer = new wxFlexGridSizer (2, 2, 15, 15);

    // Build the group of input fields
    m_pInputGroupBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Input Parameters"));

    // Note that "min" values in fp validators don't work right - so leave them out
    // Focal length - int <= 4000
    int width = StringWidth(this, "00000") + 10;
    wxIntegerValidator <int> valFocalLength (&m_iFocalLength, 0);
    valFocalLength.SetRange (0, 3500);
    m_pFocalLength = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(width, -1), 0, valFocalLength);
    m_pFocalLength->Enable(!pFrame->CaptureActive);
    m_pFocalLength->Bind(wxEVT_TEXT, &CalstepDialog::OnText, this);
    AddTableEntry (m_pInputTableSizer, _("Focal length, mm"), m_pFocalLength, _("Guide scope focal length"));

    // Pixel size
    m_pPixelSize = NewSpinner (this, 1.5*width, m_fPixelSize, MIN_PIXELSIZE, MAX_PIXELSIZE, 0.1);
    m_pPixelSize->Enable(!pFrame->CaptureActive);
    m_pPixelSize->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Pixel size, microns"), m_pPixelSize, _("Guide camera pixel size"));

    // Guide speed
    m_pGuideSpeed = NewSpinner (this, 1.5*width, m_fGuideSpeed, MIN_GUIDESPEED, MAX_GUIDESPEED, 0.25);
    m_pGuideSpeed->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Guide speed, n.nn x sidereal"), m_pGuideSpeed, _("Guide speed, multiple of sidereal rate; to guide at ") +
        _("50% sidereal rate, enter 0.5"));

    // Number of steps
    m_pNumSteps = NewSpinner (this, 1.5*width, m_iNumSteps, MIN_STEPS, MAX_STEPS, 1);
    m_pNumSteps->SetDigits (0);
    m_pNumSteps->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Calibration steps"), m_pNumSteps, _("Targeted # steps in each direction"));

    // Calibration declination
    m_pDeclination = NewSpinner(this, 1.5*width, m_dDeclination, MIN_DECLINATION, MAX_DECLINATION, 5.0);
    m_pDeclination->SetDigits(0);
    m_pDeclination->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Calibration declination, degrees"), m_pDeclination, _("Approximate declination where you will do calibration"));

    // Build the group of output fields
    m_pOutputGroupBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Computed Values"));

    m_status = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
    m_pImageScale = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(width, -1));
    m_pImageScale->Enable(false);
    AddTableEntry (m_pOutputTableSizer, _("Image scale, arc-sec/px"), m_pImageScale, "");
    m_pRslt = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(width, -1));
    AddTableEntry (m_pOutputTableSizer, _("Calibration step, ms"), m_pRslt, "");

    // Add the tables to the panel, centered
    m_pInputGroupBox->Add (m_pInputTableSizer, 0, wxALL, 10);
    m_pOutputGroupBox->Add (m_pOutputTableSizer, 0, wxALL, 10);
    m_pVSizer->Add(m_status, 1, wxALL, 5);
    m_pVSizer->Add (m_pInputGroupBox, wxSizerFlags().Center().Border(wxALL, 10));
    m_pVSizer->Add (m_pOutputGroupBox, wxSizerFlags().Center().Border(wxRIGHT|wxLEFT|wxBOTTOM, 10));

    // ok/cancel buttons
    m_pVSizer->Add(
        CreateButtonSizer(wxOK | wxCANCEL),
        wxSizerFlags(0).Expand().Border(wxALL, 10) );

    SetSizerAndFit (m_pVSizer);
}

// Utility function to add the <label, input> tuples to the grid including tool-tips
void CalstepDialog::AddTableEntry (wxFlexGridSizer *pTable, const wxString& label, wxWindow *pControl, const wxString& toolTip)
{
    wxStaticText *pLabel = new wxStaticText(this, wxID_ANY, label + _(": "),wxPoint(-1,-1),wxSize(-1,-1));
    pTable->Add (pLabel, 1, wxALL, 5);
    pTable->Add (pControl, 1, wxALL, 5);
    pControl->SetToolTip (toolTip);
}

// Based on computed image scale, compute an RA calibration pulse direction that will result in DesiredSteps
//  for a "travel" distance of MAX_CALIBRATION_DISTANCE in each direction, adjusted for declination.
//  Result will be rounded up to the nearest 50 ms and is constrained to insure Dec calibration is at least
//  MIN_STEPs
//
//  FocalLength = focal length in millimeters
//  PixelSize = pixel size in microns
//  GuideSpeed = guide rate as fraction of sidereal rate
//  DesiredSteps = desired number of calibration steps
//  Declination = declination in degrees
//  pImageScale = address of computed image scale (arc-sec/pixel)
//  pStepSize = address of computed calibration step size, milliseconds
//
void CalstepDialog::GetCalibrationStepSize(int FocalLength, double PixelSize, double GuideSpeed, int DesiredSteps,
    double Declination, double *pImageScale, int *pStepSize)
{
    enum { CALIBRATION_PIXELS = 25 };
    double ImageScale = MyFrame::GetPixelScale(PixelSize, FocalLength); // arc-sec per pixel
    double totalDistance = (double) CALIBRATION_PIXELS * ImageScale; // arc-seconds
    double totalDuration = totalDistance / (15.0 * GuideSpeed);      // 15 arc-sec/sec approx sidereal rate
    double Pulse = totalDuration / DesiredSteps * 1000.0;            // milliseconds at DEC=0
    double MaxPulse = totalDuration / MIN_STEPS * 1000.0;            // max pulse size to still get MIN steps
    Pulse = wxMin(MaxPulse, Pulse / cos(M_PI / 180.0 * Declination)); // UI forces abs(Dec) <= 60 degrees
    if (pImageScale)
        *pImageScale = ImageScale;
    *pStepSize = (int) ceil(Pulse / 50.0) * 50;                      // round up to nearest 50 ms
}

void CalstepDialog::OnText(wxCommandEvent& evt)
{
    DoRecalc();
    evt.Skip();
}

void CalstepDialog::OnSpinCtrlDouble(wxSpinDoubleEvent& evt)
{
    DoRecalc();
    evt.Skip();
}

void CalstepDialog::DoRecalc(void)
{
    m_bValidResult = false;

    if (this->Validate() && this->TransferDataFromWindow())
    {
        m_fPixelSize = m_pPixelSize->GetValue();
        m_pPixelSize->SetValue(m_fPixelSize);           // For European locales, '.' -> ',' on output
        m_fGuideSpeed = m_pGuideSpeed->GetValue();
        m_pGuideSpeed->SetValue(m_fGuideSpeed);
        m_iNumSteps = m_pNumSteps->GetValue();
        m_dDeclination = abs(m_pDeclination->GetValue());

        if (m_iFocalLength < 50 || m_iFocalLength > 4000)
        {
            m_status->SetLabel(_("Please enter a focal length between 50 and 4000."));
        }
        else if (m_fPixelSize <= 0.0)
        {
            m_status->SetLabel(_("Please enter a pixel size greater than zero."));
        }
        else
        {
            m_status->SetLabel(wxEmptyString);

            // Spin controls enforce numeric ranges
            GetCalibrationStepSize(m_iFocalLength, m_fPixelSize, m_fGuideSpeed, m_iNumSteps, m_dDeclination, &m_fImageScale, &m_iStepSize);
            m_bValidResult = true;
        }

        if (m_bValidResult)
        {
            m_pImageScale->SetValue (wxString::Format ("%.2f", m_fImageScale));
            m_pRslt->SetValue (wxString::Format ("%3d", m_iStepSize));
        }
        else
        {
            m_pImageScale->SetValue(wxEmptyString);
            m_pRslt->SetValue(wxEmptyString);
        }
    }
}
// Public function for client to get the computed step-size along with possibly modified values for focal length and pixel size
bool CalstepDialog::GetResults(int *focalLength, double *pixelSize, int *stepSize)
{
    if (m_bValidResult)
    {
        // Remember the guide speed chosen is just to help the user - purely a UI thing, no guiding implications
        pConfig->Profile.SetDouble("/CalStepCalc/GuideSpeed", m_fGuideSpeed);
        pConfig->Profile.SetDouble("/CalStepCalc/CalDeclination", m_dDeclination);
        pConfig->Profile.SetInt("/CalStepCalc/NumSteps", m_iNumSteps);

        *focalLength = m_iFocalLength;
        *pixelSize = m_fPixelSize;
        *stepSize = m_iStepSize;
        long lval;
        if (m_pRslt->GetValue().ToLong(&lval) && lval > 0)
            *stepSize = (int) lval;
        return true;
    }
    else
        return false;
}

CalstepDialog::~CalstepDialog(void)
{
}
<commit_msg>Implement issue 327, calibration step calculator will use current declination position if available<commit_after>/*
 *  calstep_dialog.cpp
 *  PHD Guiding
 *
 *  Created by Bruce Waddington
 *  Copyright (c) 2013 Bruce Waddington
 *  All rights reserved.
 *
 *  This source code is distributed under the following "BSD" license
 *  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 Bret McKee, Dad Dog Development,
 *     Craig Stark, Stark Labs nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "phd.h"
#include "calstep_dialog.h"
#include "wx/valnum.h"

#define MIN_PIXELSIZE 0.1
#define MAX_PIXELSIZE 25.0
#define MIN_GUIDESPEED 0.10
#define MAX_GUIDESPEED 2.0
#define MIN_STEPS 6.0
#define MAX_STEPS 60.0
#define MIN_DECLINATION -60.0
#define MAX_DECLINATION 60.0

const double CalstepDialog::DEFAULT_GUIDESPEED = 0.5;  // 50% sidereal rate

static wxSpinCtrlDouble *NewSpinner(wxWindow *parent, int width, double val, double minval, double maxval, double inc)
{
    wxSpinCtrlDouble *pNewCtrl = new wxSpinCtrlDouble(parent, wxID_ANY, _T("foo2"), wxPoint(-1, -1),
        wxSize(width, -1), wxSP_ARROW_KEYS, minval, maxval, val, inc);
    pNewCtrl->SetDigits(2);
    return pNewCtrl;
}

CalstepDialog::CalstepDialog(wxWindow *parent, int focalLength, double pixelSize) :
    wxDialog(parent, wxID_ANY, _("Calibration Step Calculator"), wxDefaultPosition, wxSize(400, 500), wxCAPTION | wxCLOSE_BOX)
{
    double dGuideRateDec = 0.0; // initialize to suppress compiler warning
    double dGuideRateRA = 0.0; // initialize to suppress compiler warning
    const double dSiderealSecondPerSec = 0.9973;

    // Get squared away with initial parameter values - start with values from the profile
    m_iNumSteps = pConfig->Profile.GetInt("/CalStepCalc/NumSteps", DEFAULT_STEPS);
    m_dDeclination = pConfig->Profile.GetDouble ("/CalStepCalc/CalDeclination", 0.0);
    m_iFocalLength = focalLength;
    m_fPixelSize = pixelSize;
    m_fGuideSpeed = (float) pConfig->Profile.GetDouble ("/CalStepCalc/GuideSpeed", DEFAULT_GUIDESPEED);
    // Now improve on Dec and guide speed if mount/pointing info is available
    if (pPointingSource && pPointingSource->IsConnected())
    {
        if (!pPointingSource->GetGuideRates(&dGuideRateRA, &dGuideRateDec))
        {
            if (dGuideRateRA >= dGuideRateDec)
                m_fGuideSpeed = dGuideRateRA * 3600.0 / (15.0 * dSiderealSecondPerSec);  // Degrees/sec to Degrees/hour, 15 degrees/hour is roughly sidereal rate
            else
                m_fGuideSpeed = dGuideRateDec / (15.0 * dSiderealSecondPerSec);

            if (m_fGuideSpeed < MIN_GUIDESPEED)
                m_fGuideSpeed = MIN_GUIDESPEED;
        }
        double ra_val, dec_val, st;
        if (!pPointingSource->GetCoordinates(&ra_val, &dec_val, &st))
            m_dDeclination = dec_val;
    }

    m_bValidResult = false;

    // Create the sizers we're going to need
    m_pVSizer = new wxBoxSizer(wxVERTICAL);
    m_pInputTableSizer = new wxFlexGridSizer (2, 2, 15, 15);
    m_pOutputTableSizer = new wxFlexGridSizer (2, 2, 15, 15);

    // Build the group of input fields
    m_pInputGroupBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Input Parameters"));

    // Note that "min" values in fp validators don't work right - so leave them out
    // Focal length - int <= 4000
    int width = StringWidth(this, "00000") + 10;
    wxIntegerValidator <int> valFocalLength (&m_iFocalLength, 0);
    valFocalLength.SetRange (0, 3500);
    m_pFocalLength = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(width, -1), 0, valFocalLength);
    m_pFocalLength->Enable(!pFrame->CaptureActive);
    m_pFocalLength->Bind(wxEVT_TEXT, &CalstepDialog::OnText, this);
    AddTableEntry (m_pInputTableSizer, _("Focal length, mm"), m_pFocalLength, _("Guide scope focal length"));

    // Pixel size
    m_pPixelSize = NewSpinner (this, 1.5*width, m_fPixelSize, MIN_PIXELSIZE, MAX_PIXELSIZE, 0.1);
    m_pPixelSize->Enable(!pFrame->CaptureActive);
    m_pPixelSize->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Pixel size, microns"), m_pPixelSize, _("Guide camera pixel size"));

    // Guide speed
    m_pGuideSpeed = NewSpinner (this, 1.5*width, m_fGuideSpeed, MIN_GUIDESPEED, MAX_GUIDESPEED, 0.25);
    m_pGuideSpeed->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Guide speed, n.nn x sidereal"), m_pGuideSpeed, _("Guide speed, multiple of sidereal rate; to guide at ") +
        _("50% sidereal rate, enter 0.5"));

    // Number of steps
    m_pNumSteps = NewSpinner (this, 1.5*width, m_iNumSteps, MIN_STEPS, MAX_STEPS, 1);
    m_pNumSteps->SetDigits (0);
    m_pNumSteps->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Calibration steps"), m_pNumSteps, _("Targeted # steps in each direction"));

    // Calibration declination
    m_pDeclination = NewSpinner(this, 1.5*width, m_dDeclination, MIN_DECLINATION, MAX_DECLINATION, 5.0);
    m_pDeclination->SetDigits(0);
    m_pDeclination->Bind(wxEVT_SPINCTRLDOUBLE, &CalstepDialog::OnSpinCtrlDouble, this);
    AddTableEntry (m_pInputTableSizer, _("Calibration declination, degrees"), m_pDeclination, _("Approximate declination where you will do calibration"));

    // Build the group of output fields
    m_pOutputGroupBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Computed Values"));

    m_status = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
    m_pImageScale = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(width, -1));
    m_pImageScale->Enable(false);
    AddTableEntry (m_pOutputTableSizer, _("Image scale, arc-sec/px"), m_pImageScale, "");
    m_pRslt = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(width, -1));
    AddTableEntry (m_pOutputTableSizer, _("Calibration step, ms"), m_pRslt, "");

    // Add the tables to the panel, centered
    m_pInputGroupBox->Add (m_pInputTableSizer, 0, wxALL, 10);
    m_pOutputGroupBox->Add (m_pOutputTableSizer, 0, wxALL, 10);
    m_pVSizer->Add(m_status, 1, wxALL, 5);
    m_pVSizer->Add (m_pInputGroupBox, wxSizerFlags().Center().Border(wxALL, 10));
    m_pVSizer->Add (m_pOutputGroupBox, wxSizerFlags().Center().Border(wxRIGHT|wxLEFT|wxBOTTOM, 10));

    // ok/cancel buttons
    m_pVSizer->Add(
        CreateButtonSizer(wxOK | wxCANCEL),
        wxSizerFlags(0).Expand().Border(wxALL, 10) );

    SetSizerAndFit (m_pVSizer);
}

// Utility function to add the <label, input> tuples to the grid including tool-tips
void CalstepDialog::AddTableEntry (wxFlexGridSizer *pTable, const wxString& label, wxWindow *pControl, const wxString& toolTip)
{
    wxStaticText *pLabel = new wxStaticText(this, wxID_ANY, label + _(": "),wxPoint(-1,-1),wxSize(-1,-1));
    pTable->Add (pLabel, 1, wxALL, 5);
    pTable->Add (pControl, 1, wxALL, 5);
    pControl->SetToolTip (toolTip);
}

// Based on computed image scale, compute an RA calibration pulse direction that will result in DesiredSteps
//  for a "travel" distance of MAX_CALIBRATION_DISTANCE in each direction, adjusted for declination.
//  Result will be rounded up to the nearest 50 ms and is constrained to insure Dec calibration is at least
//  MIN_STEPs
//
//  FocalLength = focal length in millimeters
//  PixelSize = pixel size in microns
//  GuideSpeed = guide rate as fraction of sidereal rate
//  DesiredSteps = desired number of calibration steps
//  Declination = declination in degrees
//  pImageScale = address of computed image scale (arc-sec/pixel)
//  pStepSize = address of computed calibration step size, milliseconds
//
void CalstepDialog::GetCalibrationStepSize(int FocalLength, double PixelSize, double GuideSpeed, int DesiredSteps,
    double Declination, double *pImageScale, int *pStepSize)
{
    enum { CALIBRATION_PIXELS = 25 };
    double ImageScale = MyFrame::GetPixelScale(PixelSize, FocalLength); // arc-sec per pixel
    double totalDistance = (double) CALIBRATION_PIXELS * ImageScale; // arc-seconds
    double totalDuration = totalDistance / (15.0 * GuideSpeed);      // 15 arc-sec/sec approx sidereal rate
    double Pulse = totalDuration / DesiredSteps * 1000.0;            // milliseconds at DEC=0
    double MaxPulse = totalDuration / MIN_STEPS * 1000.0;            // max pulse size to still get MIN steps
    Pulse = wxMin(MaxPulse, Pulse / cos(M_PI / 180.0 * Declination)); // UI forces abs(Dec) <= 60 degrees
    if (pImageScale)
        *pImageScale = ImageScale;
    *pStepSize = (int) ceil(Pulse / 50.0) * 50;                      // round up to nearest 50 ms
}

void CalstepDialog::OnText(wxCommandEvent& evt)
{
    DoRecalc();
    evt.Skip();
}

void CalstepDialog::OnSpinCtrlDouble(wxSpinDoubleEvent& evt)
{
    DoRecalc();
    evt.Skip();
}

void CalstepDialog::DoRecalc(void)
{
    m_bValidResult = false;

    if (this->Validate() && this->TransferDataFromWindow())
    {
        m_fPixelSize = m_pPixelSize->GetValue();
        m_pPixelSize->SetValue(m_fPixelSize);           // For European locales, '.' -> ',' on output
        m_fGuideSpeed = m_pGuideSpeed->GetValue();
        m_pGuideSpeed->SetValue(m_fGuideSpeed);
        m_iNumSteps = m_pNumSteps->GetValue();
        m_dDeclination = abs(m_pDeclination->GetValue());

        if (m_iFocalLength < 50 || m_iFocalLength > 4000)
        {
            m_status->SetLabel(_("Please enter a focal length between 50 and 4000."));
        }
        else if (m_fPixelSize <= 0.0)
        {
            m_status->SetLabel(_("Please enter a pixel size greater than zero."));
        }
        else
        {
            m_status->SetLabel(wxEmptyString);

            // Spin controls enforce numeric ranges
            GetCalibrationStepSize(m_iFocalLength, m_fPixelSize, m_fGuideSpeed, m_iNumSteps, m_dDeclination, &m_fImageScale, &m_iStepSize);
            m_bValidResult = true;
        }

        if (m_bValidResult)
        {
            m_pImageScale->SetValue (wxString::Format ("%.2f", m_fImageScale));
            m_pRslt->SetValue (wxString::Format ("%3d", m_iStepSize));
        }
        else
        {
            m_pImageScale->SetValue(wxEmptyString);
            m_pRslt->SetValue(wxEmptyString);
        }
    }
}
// Public function for client to get the computed step-size along with possibly modified values for focal length and pixel size
bool CalstepDialog::GetResults(int *focalLength, double *pixelSize, int *stepSize)
{
    if (m_bValidResult)
    {
        // Remember the guide speed chosen is just to help the user - purely a UI thing, no guiding implications
        pConfig->Profile.SetDouble("/CalStepCalc/GuideSpeed", m_fGuideSpeed);
        pConfig->Profile.SetDouble("/CalStepCalc/CalDeclination", m_dDeclination);
        pConfig->Profile.SetInt("/CalStepCalc/NumSteps", m_iNumSteps);

        *focalLength = m_iFocalLength;
        *pixelSize = m_fPixelSize;
        *stepSize = m_iStepSize;
        long lval;
        if (m_pRslt->GetValue().ToLong(&lval) && lval > 0)
            *stepSize = (int) lval;
        return true;
    }
    else
        return false;
}

CalstepDialog::~CalstepDialog(void)
{
}
<|endoftext|>