	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hint();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()
{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

void
Dock_Canvases::new_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

	etl::loose_handle<studio::Instance> loose_instance(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this,&Dock_Canvases::refresh_instances));
	instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}


/* === S Y N F I G ========================================================= */
/*!	\file dock_canvases.cpp
**	\brief Template File
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "docks/dock_canvases.h"

#include <gui/app.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Dock_Canvases::Dock_Canvases():
	Dockable("canvases",_("Canvas Browser"),Gtk::StockID("synfig-canvas"))
{

	App::signal_instance_created().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::new_instance));
	App::signal_instance_deleted().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::delete_instance));
	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hint();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()
{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

void
Dock_Canvases::new_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

	etl::loose_handle<studio::Instance> loose_instance(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this,&Dock_Canvases::refresh_instances));
	instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}


/* === S Y N F I G ========================================================= */
/*!	\file dock_canvases.cpp
**	\brief Template File
**
**	\legal
**
**	This file is part of Synfig.
**
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "docks/dock_canvases.h"

#include <gui/app.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

Dock_Canvases::Dock_Canvases():
	Dockable("canvases",_("Canvas Browser"),Gtk::StockID("synfig-canvas"))
{

	App::signal_instance_created().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::new_instance));
	App::signal_instance_deleted().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::delete_instance));
	App::signal_instance_selected().connect(sigc::mem_fun(*this,&studio::Dock_Canvases::set_selected_instance_signal));

	add(*create_canvas_tree());

/*  // \todo Implement canvas management in canvas browser
	add_button(
		Gtk::StockID("synfig-canvas_new"),
		_("Insert a new canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_new_canvas
		)
	);

	add_button(
		Gtk::StockID("gtk-delete"),
		_("Remove selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_delete
		)
	);

	add_button(
		Gtk::StockID("synfig-rename"),
		_("Rename selected canvas")
	)->signal_clicked().connect(
		sigc::mem_fun(
			*this,
			&Dock_Canvases::menu_rename
		)
	);
*/
}

Dock_Canvases::~Dock_Canvases()
{
}

Gtk::Widget*
Dock_Canvases::create_canvas_tree()
{
	studio::Instance::CanvasTreeModel canvas_tree_model;
	canvas_tree=manage(new class Gtk::TreeView());
	{
		Gtk::TreeView::Column* column = Gtk::manage( new Gtk::TreeView::Column(_("ID")) );

		column->pack_start(canvas_tree_model.icon, false); //false = don't expand.
		column->pack_start(canvas_tree_model.label);

		canvas_tree->append_column(*column);
	}
	canvas_tree->set_rules_hi

nt();
	canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this,&Dock_Canvases::on_row_activate));
	canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
	canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
	canvas_tree->show();
	canvas_tree->set_headers_visible(false);

	Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
	scrolledwindow->set_can_focus(true);
	scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindow->add(*canvas_tree);
	scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
	scrolledwindow->show_all();

	return scrolledwindow;
}

etl::loose_handle<studio::CanvasView>
Dock_Canvases::get_selected_canvas_view()
{
	return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

etl::loose_handle<synfig::Canvas>
Dock_Canvases::get_selected_canvas()
{
	Glib::RefPtr<Gtk::TreeSelection> selection=canvas_tree->get_selection();

	if(!selection || !selection->get_selected())
		return 0;

	studio::Instance::CanvasTreeModel canvas_tree_model;

	return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
Dock_Canvases::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
	set_selected_instance(x);
}

void
Dock_Canvases::set_selected_instance_(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	selected_instance=instance;
	if(instance)
	{
		canvas_tree->set_model(instance->canvas_tree_store());
		canvas_tree->show();
	}
	else
	{
		canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
		canvas_tree->hide();
	}
}

void
Dock_Canvases::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
	if(studio::App::shutdown_in_progress)
		return;

	// if it's already selected, don't select it again
	if (x==selected_instance)
		return;

	set_selected_instance_(x);
}

void
Dock_Canvases::new_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	assert(instance);

	etl::loose_handle<studio::Instance> loose_instance(instance);

	instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this,&Dock_Canvases::refresh_instances));
	instance->synfigapp::Instance::signal_filename_changed().connect(
		sigc::bind<etl::loose_handle<studio::Instance> >(
			sigc::mem_fun(*this,&Dock_Canvases::set_selected_instance),
			loose_instance
		)
	);
}

void
Dock_Canvases::delete_instance(etl::handle<studio::Instance> instance)
{
	if(studio::App::shutdown_in_progress)
		return;

	refresh_instances();

	if(selected_instance==instance)
	{
		set_selected_instance(0);
	}
}

void
Dock_Canvases::refresh_instances()
{
	if(studio::App::shutdown_in_progress)
		return;
}

void
Dock_Canvases::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
	assert(get_selected_instance());
	studio::Instance::CanvasTreeModel canvas_tree_model;
	const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));
	if(row[canvas_tree_model.is_canvas])
		get_selected_instance()->focus(row[canvas_tree_model.canvas]);
	else
		studio::App::dialog_not_implemented();
}


static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

{ return "GDI"; }
  auto ready() -> bool override { return _ready; }

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowClass.lpszMenuName = 0;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void {
    _ready = false;
    if(_buffer) { delete[] _buffer; _buffer = nullptr; }
    if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
    if(_dc) { DeleteDC(_dc); _dc = nullptr; }
    if(_window) { DestroyWindow(_window); _window = nullptr; }
    _context = nullptr;
  }

  bool _ready = false;

  int _monitorX = 0;
  int _monitorY = 0;
  int _monitorWidth = 0;
  int _monitorHeight = 0;

  uint32_t* _buffer = nullptr;
  uint _width = 0;
  uint _height = 0;

  HWND _window = nullptr;
  HWND _context = nullptr;
  HBITMAP _bitmap = nullptr;
  HDC _dc = nullptr;
  BITMAPINFO _info = {};
};


static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

  auto driver() -> string override { return "GDI"; }
  auto ready() -> bool override

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowClass.lpszMenuName = 0;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void {
    _ready = false;
    if(_buffer) { delete[] _buffer; _buffer = nullptr; }
    if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
    if(_dc) { DeleteDC(_dc); _dc = nullptr; }
    if(_window) { DestroyWindow(_window); _window = nullptr; }
    _context = nullptr;
  }

  bool _ready = false;

  int _monitorX = 0;
  int _monitorY = 0;
  int _monitorWidth = 0;
  int _monitorHeight = 0;

  uint32_t* _buffer = nullptr;
  uint _width = 0;
  uint _height = 0;

  HWND _window = nullptr;
  HWND _context = nullptr;
  HBITMAP _bitmap = nullptr;
  HDC _dc = nullptr;
  BITMAPINFO _info = {};
};


{
    _ready = false;
    if(_buffer) { delete[] _buffer; _buffer = nullptr; }

static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

  auto driver() -> string override { return "GDI"; }
  auto ready() -> bool override { return _ready; }

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowClass.lpszMenuName = 0;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void

    if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
    if(_dc) { DeleteDC(_dc); _dc = nullptr; }
    if(_window) { DestroyWindow(_window); _window = nullptr; }
    _context = nullptr;
  }

  bool _ready = false;

  int _monitorX = 0;
  int _monitorY = 0;
  int _monitorWidth = 0;
  int _monitorHeight = 0;

  uint32_t* _buffer = nullptr;
  uint _width = 0;
  uint _height = 0;

  HWND _window = nullptr;
  HWND _context = nullptr;
  HBITMAP _bitmap = nullptr;
  HDC _dc = nullptr;
  BITMAPINFO _info = {};
};


if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
    if(_dc) { DeleteDC(_dc); _dc = nullptr; }

static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

  auto driver() -> string override { return "GDI"; }
  auto ready() -> bool override { return _ready; }

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowClass.lpszMenuName = 0;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void {
    _ready = false;
    if(_buffer) { delete[] _buffer; _buffer = nullptr; }

    if(_window) { DestroyWindow(_window); _window = nullptr; }
    _context = nullptr;
  }

  bool _ready = false;

  int _monitorX = 0;
  int _monitorY = 0;
  int _monitorWidth = 0;
  int _monitorHeight = 0;

  uint32_t* _buffer = nullptr;
  uint _width = 0;
  uint _height = 0;

  HWND _window = nullptr;
  HWND _context = nullptr;
  HBITMAP _bitmap = nullptr;
  HDC _dc = nullptr;
  BITMAPINFO _info = {};
};


if(_window) { DestroyWindow(_window); _window = nullptr; }
    _context = nullptr;
  }

static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

  auto driver() -> string override { return "GDI"; }
  auto ready() -> bool override { return _ready; }

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowClass.lpszMenuName = 0;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void {
    _ready = false;
    if(_buffer) { delete[] _buffer; _buffer = nullptr; }
    if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
    if(_dc) { DeleteDC(_dc); _dc = nullptr; }

  bool _ready = false;

  int _monitorX = 0;
  int _monitorY = 0;
  int _monitorWidth = 0;
  int _monitorHeight = 0;

  uint32_t* _buffer = nullptr;
  uint _width = 0;
  uint _height = 0;

  HWND _window = nullptr;
  HWND _context = nullptr;
  HBITMAP _bitmap = nullptr;
  HDC _dc = nullptr;
  BITMAPINFO _info = {};
};


static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

  auto driver() -> string override { return "GDI"; }
  auto ready() -> bool override { return _ready; }

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowClass.lpsz

    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void {
    _ready = false;
    if(_buffer) { delete[] _buffer; _buffer = nullptr; }
    if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
    if(_dc) { DeleteDC(_dc); _dc = nullptr; }
    if(_window) { DestroyWindow(_window); _window = nullptr; }
    _context = nullptr;
  }

  bool _ready = false;

  int _monitorX = 0;
  int _monitorY = 0;
  int _monitorWidth = 0;
  int _monitorHeight = 0;

  uint32_t* _buffer = nullptr;
  uint _width = 0;
  uint _height = 0;

  HWND _window = nullptr;
  HWND _context = nullptr;
  HBITMAP _bitmap = nullptr;
  HDC _dc = nullptr;
  BITMAPINFO _info = {};
};


if(_buffer) { delete[] _buffer; _buffer = nullptr; }

static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

  auto driver() -> string override { return "GDI"; }
  auto ready() -> bool override { return _ready; }

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowClass.lpszMenuName = 0;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void {
    _ready = false;


static LRESULT CALLBACK VideoGDI_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

struct VideoGDI : VideoDriver {
  VideoGDI& self = *this;
  VideoGDI(Video& super) : VideoDriver(super) { construct(); }
  ~VideoGDI() { destruct(); }

  auto create() -> bool override {
    VideoDriver::shader = "None";
    return initialize();
  }

  auto driver() -> string override { return "GDI"; }
  auto ready() -> bool override { return _ready; }

  auto hasFullScreen() -> bool override { return true; }
  auto hasMonitor() -> bool override { return true; }
  auto hasContext() -> bool override { return true; }

  auto setFullScreen(bool fullScreen) -> bool override { return initialize(); }
  auto setMonitor(string monitor) -> bool override { return initialize(); }
  auto setContext(uintptr context) -> bool override { return initialize(); }

  auto focused() -> bool override {
    if(self.fullScreen && self.exclusive) return true;
    auto focused = GetFocus();
    return _context == focused || IsChild(_context, focused);
  }

  auto size(uint& width, uint& height) -> void override {
    RECT rectangle;
    GetClientRect(_context, &rectangle);
    width = rectangle.right - rectangle.left;
    height = rectangle.bottom - rectangle.top;
  }

  auto acquire(uint32_t*& data, uint& pitch, uint width, uint height) -> bool override {
    if(!_buffer || _width != width || _height != height) {
      if(_buffer) delete[] _buffer;
      if(_bitmap) DeleteObject(_bitmap);
      if(_dc) DeleteObject(_dc);

      _buffer = new uint32_t[width * height]();
      _width = width;
      _height = height;

      HDC hdc = GetDC(_context);
      _dc = CreateCompatibleDC(hdc);
      _bitmap = CreateCompatibleBitmap(hdc, width, height);
      SelectObject(_dc, _bitmap);
      ReleaseDC(_context, hdc);

      memory::fill(&_info, sizeof(BITMAPINFO));
      _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      _info.bmiHeader.biWidth = width;
      _info.bmiHeader.biHeight = -height;
      _info.bmiHeader.biPlanes = 1;
      _info.bmiHeader.biBitCount = 32;
      _info.bmiHeader.biCompression = BI_RGB;
      _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
    }

    pitch = _width * sizeof(uint32_t);
    return data = _buffer;
  }

  auto release() -> void override {
  }

  auto output(uint width, uint height) -> void override {
    uint windowWidth, windowHeight;
    size(windowWidth, windowHeight);

    SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
    HDC hdc = GetDC(_context);
    StretchBlt(hdc,
      ((int)windowWidth - (int)width) / 2, ((int)windowHeight - (int)height) / 2, width, height, _dc,
      0, 0, _width, _height, SRCCOPY
    );
    ReleaseDC(_context, hdc);
  }

private:
  auto construct() -> void {
    WNDCLASS windowClass{};
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    windowClass.hCursor = LoadCursor(0, IDC_ARROW);
    windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    windowClass.hInstance = GetModuleHandle(0);
    windowClass.lpfnWndProc = VideoGDI_WindowProcedure;
    windowClass.lpszClassName = L"VideoGDI_Window";
    windowCla

ss.lpszMenuName = 0;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&windowClass);
  }

  auto destruct() -> void {
    terminate();
  }

  auto initialize() -> bool {
    terminate();
    if(!self.fullScreen && !self.context) return false;

    auto monitor = Video::monitor(self.monitor);
    _monitorX = monitor.x;
    _monitorY = monitor.y;
    _monitorWidth = monitor.width;
    _monitorHeight = monitor.height;

    if(self.fullScreen) {
      _context = _window = CreateWindowEx(WS_EX_TOPMOST, L"VideoGDI_Window", L"", WS_VISIBLE | WS_POPUP,
        _monitorX, _monitorY, _monitorWidth, _monitorHeight,
        nullptr, nullptr, GetModuleHandle(0), nullptr);
    } else {
      _context = (HWND)self.context;
    }

    _width = 0;
    _height = 0;
    return _ready = true;
  }

  auto terminate() -> void {
    _ready = false;
    if(_buffer) { delete[] _buffer; _buffer = nullptr; }
    if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
    if(_dc) { DeleteDC(_dc); _dc = nullptr; }
    if(_window) { DestroyWindow(_window); _window = nullptr; }
    _context = nullptr;
  }

  bool _ready = false;

  int _monitorX = 0;
  int _monitorY = 0;
  int _monitorWidth = 0;
  int _monitorHeight = 0;

  uint32_t* _buffer = nullptr;
  uint _width = 0;
  uint _height = 0;

  HWND _window = nullptr;
  HWND _context = nullptr;
  HBITMAP _bitmap = nullptr;
  HDC _dc = nullptr;
  BITMAPINFO _info = {};
};


/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;
    itval.it_interval.tv_sec = 0;
    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

    volatile FN doThenInstrumentation = DoThenInstrumentation;
    while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

int main()
{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;
    itval.it_interval.tv_sec = 0;
    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

    volatile FN doThenInstrumentation = DoThenInstrumentation;
    while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;

/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

int main()

    itval.it_interval.tv_sec = 0;
    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

    volatile FN doThenInstrumentation = DoThenInstrumentation;
    while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


itval.it_interval.tv_sec = 0;
    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

    volatile FN doThenInstrumentation = DoThenInstrumentation;

/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

int main()
{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;

    while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

int main()
{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;
    itval.it_interval.tv_sec = 0;
    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

    volatile FN doThenInstrumentation = DoThenInstrumentation;

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

int main()
{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;
    itval.it

    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

    volatile FN doThenInstrumentation = DoThenInstrumentation;
    while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

int main()
{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;
    itval.it_interval.tv_sec = 0;
    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;

    volatile FN doThenInstrumentation = DoThenInstrumentation;
    while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


/*BEGIN_LEGAL
END_LEGAL */
/*
 * This is a test program that stresses Pin's ability to deliver signals while
 * executing in a tool's "then" analysis routine.  This application is meant to
 * run with the "then-stress-tool.cpp" tool.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static const unsigned SIGCOUNT = 100;

typedef void (*FN)();
volatile unsigned SigCount = 0;
volatile unsigned long LoopCount = 0;

static void Handle(int);
extern "C" void DoThenInstrumentation();

int main()
{
    struct sigaction sigact;
    sigact.sa_handler = Handle;
    s

igact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);
    if (sigaction(SIGALRM, &sigact, 0) == -1)
    {
        fprintf(stderr, "Unable to set up handler\n");
        return 1;
    }

    struct itimerval itval;
    itval.it_interval.tv_sec = 0;
    itval.it_interval.tv_usec = 100000;
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 100000;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to set up timer\n");
        return 1;
    }

    volatile FN doThenInstrumentation = DoThenInstrumentation;
    while (SigCount < SIGCOUNT)
    {
        doThenInstrumentation();
        LoopCount++;
    }

    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &itval, 0) == -1)
    {
        fprintf(stderr, "Unable to disable timer\n");
        return 1;
    }

    return 0;
}

static void Handle(int sig)
{
    SigCount++;
    printf("SigCount=%u, LoopCount=%lu\n", SigCount, LoopCount);
    fflush(stdout);
}

void DoThenInstrumentation()
{
    /* the Pin tool inserts if-then instrumentation here */
}


auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)

/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{

{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}
	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;
		int magic = 0xd34df00d;
		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}
}


{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}

/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{
		auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)

	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;
		int magic = 0xd34df00d;
		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}
}


{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{
		auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)
		{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}
	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;
		int magic = 0xd34df00d;
		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}
}


QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;

/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{
		auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)
		{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}
	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		int magic = 0xd34df00d;
		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}
}


int magic = 0xd34df00d;
		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}

/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{
		auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)
		{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}
	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;


/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{
		auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)
		{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}
	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;
		int

		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}
}


if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{
		auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)
		{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}
	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		QFile f { exportDialog.GetDestination () };

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;
		int magic = 0xd34df00d;
		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}
}


/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 *
 **********************************************************************/

#include "exportutils.h"
#include <algorithm>
#include <QFile>
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include "export.h"
#include "opmlwriter.h"
#include "storagebackendmanager.h"
#include "storagebackend.h"
#include "channelutils.h"

namespace LC::Aggregator::ExportUtils
{
	namespace
	{
		auto FilterChannels (channels_shorts_t channels, const QSet<IDType_t>& selected)
		{
			const auto removedPos = std::remove_if (channels.begin (), channels.end (),
					[&selected] (const ChannelShort& ch) { return !selected.contains (ch.ChannelID_); });
			channels.erase (removedPos, channels.end ());
			return channels;
		}
	}

	void RunExportOPML (const ITagsManager *itm, QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to OPML"),
				QObject::tr ("Select save file"),
				QObject::tr ("OPML files (*.opml);

;"
					"XML files (*.xml);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		OPMLWriter writer { itm };
		auto data = writer.Write (channels,
				exportDialog.GetTitle (), exportDialog.GetOwner (), exportDialog.GetOwnerEmail ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("OPML export error"),
					QObject::tr ("Could not open file %1 for write.")
							.arg (f.fileName ()));
			return;
		}
		f.write (data.toUtf8 ());
	}

	void RunExportBinary (QWidget *parent)
	{
		const auto& allChannels = ChannelUtils::GetAllChannels ();
		Export exportDialog (QObject::tr ("Export to binary file"),
				QObject::tr ("Select save file"),
				QObject::tr ("Aggregator exchange files (*.lcae);;"
					"All files (*.*)"),
				parent);
		exportDialog.SetFeeds (allChannels);
		if (exportDialog.exec () == QDialog::Rejected)
			return;

		auto channels = FilterChannels (allChannels, exportDialog.GetSelectedFeeds ());

		QFile f { exportDialog.GetDestination () };
		if (!f.open (QIODevice::WriteOnly))
		{
			QMessageBox::critical (parent,
					QObject::tr ("Binary export error"),
					QObject::tr ("Could not open file %1 for write.")
						.arg (f.fileName ()));
			return;
		}

		QByteArray buffer;
		QDataStream data (&buffer, QIODevice::WriteOnly);

		int version = 1;
		int magic = 0xd34df00d;
		data << magic
				<< version
				<< exportDialog.GetTitle ()
				<< exportDialog.GetOwner ()
				<< exportDialog.GetOwnerEmail ();

		const auto sb = StorageBackendManager::Instance ().MakeStorageBackendForThread ();
		for (const auto& cs : channels)
		{
			auto channel = sb->GetChannel (cs.ChannelID_);
			channel.Items_ = sb->GetFullItems (channel.ChannelID_);
			data << channel;
		}

		f.write (qCompress (buffer, 9));
	}
}


void SoftmaxWithLossLayer<Dtype>::Reshape(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top)

#include <algorithm>
#include <cfloat>
#include <vector>

#include "caffe/layers/softmax_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
void SoftmaxWithLossLayer<Dtype>::LayerSetUp(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  LossLayer<Dtype>::LayerSetUp(bottom, top);
  LayerParameter softmax_param(this->layer_param_);
  softmax_param.set_type("Softmax");
  softmax_layer_ = LayerRegistry<Dtype>::CreateLayer(softmax_param);
  softmax_bottom_vec_.clear();
  softmax_bottom_vec_.push_back(bottom[0]);
  softmax_top_vec_.clear();
  softmax_top_vec_.push_back(&prob_);
  softmax_layer_->SetUp(softmax_bottom_vec_, softmax_top_vec_);

  /*has_ignore_label_ =
    this->layer_param_.loss_param().has_ignore_label();
  if (has_ignore_label_) {
    ignore_label_ = this->layer_param_.loss_param().ignore_label();
  }*/

  if (!this->layer_param_.loss_param().has_normalization() &&
      this->layer_param_.loss_param().has_normalize()) {
    normalization_ = this->layer_param_.loss_param().normalize() ?
                     LossParameter_NormalizationMode_VALID :
                     LossParameter_NormalizationMode_BATCH_SIZE;
  } else {
    normalization_ = this->layer_param_.loss_param().normalization();
  }
}

template <typename Dtype>

{
  LossLayer<Dtype>::Reshape(bottom, top);
  softmax_layer_->Reshape(softmax_bottom_vec_, softmax_top_vec_);
  softmax_axis_ =
      bottom[0]->CanonicalAxisIndex(this->layer_param_.softmax_param().axis());
  outer_num_ = bottom[0]->count(0, softmax_axis_);
  inner_num_ = bottom[0]->count(softmax_axis_ + 1);
  CHECK_EQ(outer_num_ * inner_num_, bottom[1]->count())
      << "Number of labels must match number of predictions; "
      << "e.g., if softmax axis == 1 and prediction shape is (N, C, H, W), "
      << "label count (number of labels) must be N*H*W, "
      << "with integer values in {0, 1, ..., C-1}.";
  if (top.size() >= 2) {
    // softmax output
    top[1]->ReshapeLike(*bottom[0]);
  }

  if (this->layer_param_.loss_param().has_label_start())
      label_start_ = this->layer_param_.loss_param().label_start();
  else
      label_start_ = 0;

  if (this->layer_param_.loss_param().has_label_end())
      label_end_ = this->layer_param_.loss_param().label_end();
  else
      label_end_ = prob_.shape(softmax_axis_) - 1;

  static bool not_first_time = 0;
  if (!not_first_time) {
      LOG(INFO) << "valid label value range: [" << label_start_ << ',' << label_end_
          << "], number of classes = " << prob_.shape(softmax_axis_)
          << ", normalization mode = " << normalization_;
      not_first_time = true;
  }

  CHECK_EQ(label_end_ - label_start_ + 1, prob_.shape(softmax_axis_))
      << "valid label count must match the number of predicted classes";
}

template <typename Dtype>
Dtype SoftmaxWithLossLayer<Dtype>::get_normalizer(
    LossParameter_NormalizationMode normalization_mode, int valid_count) {
  Dtype normalizer;
  switch (normalization_mode) {
    case LossParameter_NormalizationMode_FULL:
      normalizer = Dtype(outer_num_ * inner_num_);
      break;
    case LossParameter_NormalizationMode_VALID:
      if (valid_count == -1) {
        normalizer = Dtype(outer_num_ * inner_num_);
      } else {
        normalizer = Dtype(valid_count);
      }
      break;
    case LossParameter_NormalizationMode_BATCH_SIZE:
      normalizer = Dtype(outer_num_);
      break;
    case LossParameter_NormalizationMode_NONE:
      normalizer = Dtype(1);
      break;
    default:
      LOG(FATAL) << "Unknown normalization mode: "
          << LossParameter_NormalizationMode_Name(normalization_mode);
  }
  // Some users will have no labels for some examples in order to 'turn off' a
  // particular loss in a multi-task setup. The max prevents NaNs in that case.
  return std::max(Dtype(1.0), normalizer);
}

template <typename Dtype>
void SoftmaxWithLossLayer<Dtype>::Forward_cpu(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  // The forward pass computes the softmax prob values.
  softmax_layer_->Forward(softmax_bottom_vec_, softmax_top_vec_);
  const Dtype* prob_data = prob_.cpu_data();
  const Dtype* label = bottom[1]->cpu_data();
  int dim = prob_.count() / outer_num_;
  int count = 0;
  Dtype loss = 0;
  for (int i = 0; i < outer_num_; ++i) {
    for (int j = 0; j < inner_num_; j++) {
      int label_value = static_cast<int>(label[i * inner_num_ + j]);
      /*if (has_ignore_label_ && label_value == ignore_label_) {
        continue;
      }*/
      if (label_value < label_start_ || label_value > label_end_)
          continue;
      label_value -= label_start_;
      DCHECK_GE(label_value, 0);
      DCHECK_LT(label_value, prob_.shape(softmax_axis_));
      loss -= log(std::max(prob_data[i * dim + label_value * inner_num_ + j],
                           Dtype(FLT_MIN)));
      ++count;
    }
  }
  top[0]->mutable_cpu_data()[0] = loss / get_normalizer(normalization_, count);
  if (top.size() == 2) {
    top[1]->ShareData(prob_);
  }
}

template <typename Dtype>
void SoftmaxWithLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  if (propagate_down[1]) {
    LOG(FATAL) << this->type()
               << " Layer cannot backpropagate to label inputs.";
  }
  if (propagate_down[0]) {
    Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
    const Dtype* prob_data = prob_.cpu_data();
    caffe_copy(prob_.count(), prob_data, bottom_diff);
    const Dtype* label = bottom[1]->cpu_data();
    int dim = prob_.count() / outer_num_;
    int count = 0;
    for (int i = 0; i < outer_num_; ++i) {
      for (int j = 0; j < inner_num_; ++j) {
        const int label_value = static_cast<int>(label[i * inner_num_ + j]);
        if (label_value < label_start_ || label_value > label_end_) {
          for (int c = 0; c < bottom[0]->shape(softmax_axis_); ++c) {
            bottom_diff[i * dim + c * inner_num_ + j] = 0;
          }
        } else {
          bottom_diff[i * dim + (label_value - label_start_) * inner_num_ + j] -= 1;
          ++count;
        }
      }
    }
    // Scale gradient
    Dtype loss_weight = top[0]->cpu_diff()[0] /
                        get_normalizer(normalization_, count);
    caffe_scal(prob_.count(), loss_weight, bottom_diff);
  }
}

#ifdef CPU_ONLY
STUB_GPU(SoftmaxWithLossLayer);
#endif

INSTANTIATE_CLASS(SoftmaxWithLossLayer);
REGISTER_LAYER_CLASS(SoftmaxWithLoss);

}  // namespace caffe