<commit_before>

#include <ctype.h>
#include <iostream>
#include <boost/regex.hpp>

#include "storage/Devices/MdImpl.h"
#include "storage/Holders/MdUser.h"
#include "storage/Devicegraph.h"
#include "storage/Action.h"
#include "storage/Storage.h"
#include "storage/Environment.h"
#include "storage/SystemInfo/SystemInfo.h"
#include "storage/Utils/Exception.h"
#include "storage/Utils/Enum.h"
#include "storage/Utils/StorageTmpl.h"
#include "storage/Utils/StorageTypes.h"
#include "storage/Utils/StorageDefines.h"
#include "storage/Utils/SystemCmd.h"
#include "storage/Utils/StorageTmpl.h"

namespace storage
{

    using namespace std;

    const char* DeviceTraits<Md>::classname = "Md";

    Md::Impl::Impl(const xmlNode* node)
	: Partitionable::Impl(node)
    {
	string tmp;

	if (getChildValue(node, "md-level", tmp))
	    md_level = toValueWithFallback(tmp, RAID0);

	if (getChildValue(node, "md-parity", tmp))
	    md_parity = toValueWithFallback(tmp, PAR_DEFAULT);

	getChildValue(node, "chunk-size-k", chunk_size_k);
    }

    void
    Md::Impl::set_md_level(MdType md_level)
    {
	// TODO calculate size_k

	Impl::md_level = md_level;
    }

    void
    Md::Impl::set_chunk_size_k(unsigned long chunk_size_k)
    {
	// TODO calculate size_k

	Impl::chunk_size_k = chunk_size_k;
    }

    bool
    Md::Impl::is_valid_name(const string& name)
    {
	static boost::regex name_regex(DEVDIR "/md[0-9]+", boost::regex_constants::extended);

	return boost::regex_match(name, name_regex);
    }

    vector<string>
    Md::Impl::probe_mds(SystemInfo& systeminfo)
    {
	vector<string> ret;

	for (const string& short_name : systeminfo.getDir(SYSFSDIR "/block"))
	{
	    string name = DEVDIR "/" + short_name;

	    if (!is_valid_name(name))
		continue;

	    ret.push_back(name);
	}

	return ret;
    }

    void
    Md::Impl::probe(Devicegraph* probed, SystemInfo& systeminfo)
    {
	Partitionable::Impl::probe(systeminfo);

	string tmp = get_name().substr(strlen(DEVDIR "/"));

	ProcMdstat::Entry entry;
	if (!systeminfo.getProcMdstat().getEntry(tmp, entry))
	{
	    // TODO
	    throw;
	}

	md_level = entry.md_level;
	md_parity = entry.md_parity;

	chunk_size_k = entry.chunk_size_k;

	for (const string& device : entry.devices)
	{
	    BlkDevice* blk_device = BlkDevice::find(probed, device);
	    MdUser* md_user = MdUser::create(probed, blk_device, get_device());
	    md_user->set_spare(false);
	}

	for (const string& device : entry.spares)
	{
	    BlkDevice* blk_device = BlkDevice::find(probed, device);
	    MdUser* md_user = MdUser::create(probed, blk_device, get_device());
	    md_user->set_spare(true);
	}
    }

    void
    Md::Impl::save(xmlNode* node) const
    {
	Partitionable::Impl::save(node);

	setChildValue(node, "md-level", toString(md_level));
	setChildValueIf(node, "md-parity", toString(md_parity), md_parity != PAR_DEFAULT);

	setChildValueIf(node, "chunk-size-k", chunk_size_k, chunk_size_k != 0);
    }

    MdUser*
    Md::Impl::add_device(BlkDevice* blk_device)
    {
	if (blk_device->num_children() != 0)
	    ST_THROW(WrongNumberOfChildren(blk_device->num_children(), 0));

	// TODO calculate size_k

	// TODO set partition id?

	return MdUser::create(get_devicegraph(), blk_device, get_device());
    }

    vector<BlkDevice*>
    Md::Impl::get_devices()
    {
	Devicegraph::Impl& devicegraph = get_devicegraph()->get_impl();
	Devicegraph::Impl::vertex_descriptor vertex = get_vertex();

	return devicegraph.filter_devices_of_type<BlkDevice>(devicegraph.parents(vertex));
    }

    vector<const BlkDevice*>
    Md::Impl::get_devices() const
    {
	const Devicegraph::Impl& devicegraph = get_devicegraph()->get_impl();
	Devicegraph::Impl::vertex_descriptor vertex = get_vertex();

	return devicegraph.filter_devices_of_type<const BlkDevice>(devicegraph.parents(vertex));
    }

    unsigned int
    Md::Impl::get_number() const
    {
	string::size_type pos = get_name().find_last_not_of("0123456789");
	if (pos == string::npos || pos == get_name().size() - 1)
	    ST_THROW(Exception("md name has no number"));

	return atoi(get_name().substr(pos + 1).c_str());
    }

    bool
    Md::Impl::equal(const Device::Impl& rhs_base) const
    {
	const Impl& rhs = dynamic_cast<const Impl&>(rhs_base);

	if (!Partitionable::Impl::equal(rhs))
	    return false;

	return md_level == rhs.md_level && md_parity == rhs.md_parity &&
	    chunk_size_k == rhs.chunk_size_k;
    }

    void
    Md::Impl::log_diff(std::ostream& log, const Device::Impl& rhs_base) const
    {
	const Impl& rhs = dynamic_cast<const Impl&>(rhs_base);

	Partitionable::Impl::log_diff(log, rhs);

	storage::log_diff_enum(log, "md-level", md_level, rhs.md_level);
	storage::log_diff_enum(log, "md-parity", md_parity, rhs.md_parity);

	storage::log_diff(log, "chunk-size-k", chunk_size_k, rhs.chunk_size_k);
    }

    void
    Md::Impl::print(std::ostream& out) const
    {
	Partitionable::Impl::print(out);

	out << " md-level:" << toString(get_md_level());
	out << " md-parity:" << toString(get_md_parity());

	out << " chunk-size-k:" << get_chunk_size_k();
    }

    void
    Md::Impl::process_udev_ids(vector<string>& udev_ids) const
    {
	partition(udev_ids.begin(), udev_ids.end(), string_starts_with("md-uuid-"));
    }

    Text
    Md::Impl::do_create_text(bool doing) const
    {
	return sformat(_("Create MD RAID %1$s (%2$s)"), get_displayname().c_str(),
		       get_size_string().c_str());
    }

    void
    Md::Impl::do_create() const
    {
	string cmd_line = MDADMBIN " --create " + quote(get_name()) + " --run --level=" +
	    boost::to_lower_copy(toString(md_level), locale::classic()) + " -e 1.0 --homehost=any";

	if (md_level == RAID1 || md_level == RAID5 || md_level == RAID6 || md_level == RAID10)
	    cmd_line += " -b internal";

	if (chunk_size_k > 0)
	    cmd_line += " --chunk=" + to_string(chunk_size_k);

	if (md_parity != PAR_DEFAULT)
	    cmd_line += " --parity=" + toString(md_parity);

	vector<string> devices;
	vector<string> spares;

	for (const BlkDevice* blk_device : get_devices())
	{
	    bool spare = false;

	    // TODO add get_out_holder that throws if num_children != 1, like get_single_child_of_type

	    for (const Holder* out_holder : blk_device->get_out_holders())
	    {
		if (to_md_user(out_holder)->is_spare())
		{
		    spare = true;
		    break;
		}
	    }

	    if (!spare)
		devices.push_back(blk_device->get_name());
	    else
		spares.push_back(blk_device->get_name());
	}

	cmd_line += " --raid-devices=" + to_string(devices.size());

	if (!spares.empty())
	    cmd_line += " --spare-devices=" + to_string(spares.size());

	cmd_line += " " + quote(devices) + " " + quote(spares);

	cout << cmd_line << endl;

	SystemCmd cmd(cmd_line);
	if (cmd.retcode() != 0)
	    ST_THROW(Exception("create md raid failed"));
    }

    Text
    Md::Impl::do_delete_text(bool doing) const
    {
	return sformat(_("Delete MD RAID %1$s (%2$s)"), get_displayname().c_str(),
		       get_size_string().c_str());
    }

    void
    Md::Impl::do_delete() const
    {
	// TODO split into deactivate and delete?

	string cmd_line = MDADMBIN " --stop " + quote(get_name());

	cout << cmd_line << endl;

	SystemCmd cmd(cmd_line);
	if (cmd.retcode() != 0)
	    ST_THROW(Exception("delete md raid failed"));

	for (const BlkDevice* blk_device : get_devices())
	{
	    blk_device->get_impl().wipe_device();
	}
    }

    bool
    compare_by_number(const Md* lhs, const Md* rhs)
    {
	return lhs->get_number() < rhs->get_number();
    };

}
<commit_msg>- added TODO note<commit_after>

#include <ctype.h>
#include <iostream>
#include <boost/regex.hpp>

#include "storage/Devices/MdImpl.h"
#include "storage/Holders/MdUser.h"
#include "storage/Devicegraph.h"
#include "storage/Action.h"
#include "storage/Storage.h"
#include "storage/Environment.h"
#include "storage/SystemInfo/SystemInfo.h"
#include "storage/Utils/Exception.h"
#include "storage/Utils/Enum.h"
#include "storage/Utils/StorageTmpl.h"
#include "storage/Utils/StorageTypes.h"
#include "storage/Utils/StorageDefines.h"
#include "storage/Utils/SystemCmd.h"
#include "storage/Utils/StorageTmpl.h"

namespace storage
{

    using namespace std;

    const char* DeviceTraits<Md>::classname = "Md";

    Md::Impl::Impl(const xmlNode* node)
	: Partitionable::Impl(node)
    {
	string tmp;

	if (getChildValue(node, "md-level", tmp))
	    md_level = toValueWithFallback(tmp, RAID0);

	if (getChildValue(node, "md-parity", tmp))
	    md_parity = toValueWithFallback(tmp, PAR_DEFAULT);

	getChildValue(node, "chunk-size-k", chunk_size_k);
    }

    void
    Md::Impl::set_md_level(MdType md_level)
    {
	// TODO calculate size_k

	Impl::md_level = md_level;
    }

    void
    Md::Impl::set_chunk_size_k(unsigned long chunk_size_k)
    {
	// TODO calculate size_k

	Impl::chunk_size_k = chunk_size_k;
    }

    bool
    Md::Impl::is_valid_name(const string& name)
    {
	static boost::regex name_regex(DEVDIR "/md[0-9]+", boost::regex_constants::extended);

	return boost::regex_match(name, name_regex);
    }

    vector<string>
    Md::Impl::probe_mds(SystemInfo& systeminfo)
    {
	vector<string> ret;

	for (const string& short_name : systeminfo.getDir(SYSFSDIR "/block"))
	{
	    string name = DEVDIR "/" + short_name;

	    if (!is_valid_name(name))
		continue;

	    ret.push_back(name);
	}

	return ret;
    }

    void
    Md::Impl::probe(Devicegraph* probed, SystemInfo& systeminfo)
    {
	Partitionable::Impl::probe(systeminfo);

	string tmp = get_name().substr(strlen(DEVDIR "/"));

	ProcMdstat::Entry entry;
	if (!systeminfo.getProcMdstat().getEntry(tmp, entry))
	{
	    // TODO
	    throw;
	}

	md_level = entry.md_level;
	md_parity = entry.md_parity;

	chunk_size_k = entry.chunk_size_k;

	for (const string& device : entry.devices)
	{
	    BlkDevice* blk_device = BlkDevice::find(probed, device);
	    MdUser* md_user = MdUser::create(probed, blk_device, get_device());
	    md_user->set_spare(false);
	}

	for (const string& device : entry.spares)
	{
	    BlkDevice* blk_device = BlkDevice::find(probed, device);
	    MdUser* md_user = MdUser::create(probed, blk_device, get_device());
	    md_user->set_spare(true);
	}
    }

    void
    Md::Impl::save(xmlNode* node) const
    {
	Partitionable::Impl::save(node);

	setChildValue(node, "md-level", toString(md_level));
	setChildValueIf(node, "md-parity", toString(md_parity), md_parity != PAR_DEFAULT);

	setChildValueIf(node, "chunk-size-k", chunk_size_k, chunk_size_k != 0);
    }

    MdUser*
    Md::Impl::add_device(BlkDevice* blk_device)
    {
	if (blk_device->num_children() != 0)
	    ST_THROW(WrongNumberOfChildren(blk_device->num_children(), 0));

	// TODO calculate size_k

	// TODO set partition id?

	return MdUser::create(get_devicegraph(), blk_device, get_device());
    }

    vector<BlkDevice*>
    Md::Impl::get_devices()
    {
	Devicegraph::Impl& devicegraph = get_devicegraph()->get_impl();
	Devicegraph::Impl::vertex_descriptor vertex = get_vertex();

	// TODO sorting

	return devicegraph.filter_devices_of_type<BlkDevice>(devicegraph.parents(vertex));
    }

    vector<const BlkDevice*>
    Md::Impl::get_devices() const
    {
	const Devicegraph::Impl& devicegraph = get_devicegraph()->get_impl();
	Devicegraph::Impl::vertex_descriptor vertex = get_vertex();

	// TODO sorting

	return devicegraph.filter_devices_of_type<const BlkDevice>(devicegraph.parents(vertex));
    }

    unsigned int
    Md::Impl::get_number() const
    {
	string::size_type pos = get_name().find_last_not_of("0123456789");
	if (pos == string::npos || pos == get_name().size() - 1)
	    ST_THROW(Exception("md name has no number"));

	return atoi(get_name().substr(pos + 1).c_str());
    }

    bool
    Md::Impl::equal(const Device::Impl& rhs_base) const
    {
	const Impl& rhs = dynamic_cast<const Impl&>(rhs_base);

	if (!Partitionable::Impl::equal(rhs))
	    return false;

	return md_level == rhs.md_level && md_parity == rhs.md_parity &&
	    chunk_size_k == rhs.chunk_size_k;
    }

    void
    Md::Impl::log_diff(std::ostream& log, const Device::Impl& rhs_base) const
    {
	const Impl& rhs = dynamic_cast<const Impl&>(rhs_base);

	Partitionable::Impl::log_diff(log, rhs);

	storage::log_diff_enum(log, "md-level", md_level, rhs.md_level);
	storage::log_diff_enum(log, "md-parity", md_parity, rhs.md_parity);

	storage::log_diff(log, "chunk-size-k", chunk_size_k, rhs.chunk_size_k);
    }

    void
    Md::Impl::print(std::ostream& out) const
    {
	Partitionable::Impl::print(out);

	out << " md-level:" << toString(get_md_level());
	out << " md-parity:" << toString(get_md_parity());

	out << " chunk-size-k:" << get_chunk_size_k();
    }

    void
    Md::Impl::process_udev_ids(vector<string>& udev_ids) const
    {
	partition(udev_ids.begin(), udev_ids.end(), string_starts_with("md-uuid-"));
    }

    Text
    Md::Impl::do_create_text(bool doing) const
    {
	return sformat(_("Create MD RAID %1$s (%2$s)"), get_displayname().c_str(),
		       get_size_string().c_str());
    }

    void
    Md::Impl::do_create() const
    {
	string cmd_line = MDADMBIN " --create " + quote(get_name()) + " --run --level=" +
	    boost::to_lower_copy(toString(md_level), locale::classic()) + " -e 1.0 --homehost=any";

	if (md_level == RAID1 || md_level == RAID5 || md_level == RAID6 || md_level == RAID10)
	    cmd_line += " -b internal";

	if (chunk_size_k > 0)
	    cmd_line += " --chunk=" + to_string(chunk_size_k);

	if (md_parity != PAR_DEFAULT)
	    cmd_line += " --parity=" + toString(md_parity);

	vector<string> devices;
	vector<string> spares;

	for (const BlkDevice* blk_device : get_devices())
	{
	    bool spare = false;

	    // TODO add get_out_holder that throws if num_children != 1, like get_single_child_of_type

	    for (const Holder* out_holder : blk_device->get_out_holders())
	    {
		if (to_md_user(out_holder)->is_spare())
		{
		    spare = true;
		    break;
		}
	    }

	    if (!spare)
		devices.push_back(blk_device->get_name());
	    else
		spares.push_back(blk_device->get_name());
	}

	cmd_line += " --raid-devices=" + to_string(devices.size());

	if (!spares.empty())
	    cmd_line += " --spare-devices=" + to_string(spares.size());

	cmd_line += " " + quote(devices) + " " + quote(spares);

	cout << cmd_line << endl;

	SystemCmd cmd(cmd_line);
	if (cmd.retcode() != 0)
	    ST_THROW(Exception("create md raid failed"));
    }

    Text
    Md::Impl::do_delete_text(bool doing) const
    {
	return sformat(_("Delete MD RAID %1$s (%2$s)"), get_displayname().c_str(),
		       get_size_string().c_str());
    }

    void
    Md::Impl::do_delete() const
    {
	// TODO split into deactivate and delete?

	string cmd_line = MDADMBIN " --stop " + quote(get_name());

	cout << cmd_line << endl;

	SystemCmd cmd(cmd_line);
	if (cmd.retcode() != 0)
	    ST_THROW(Exception("delete md raid failed"));

	for (const BlkDevice* blk_device : get_devices())
	{
	    blk_device->get_impl().wipe_device();
	}
    }

    bool
    compare_by_number(const Md* lhs, const Md* rhs)
    {
	return lhs->get_number() < rhs->get_number();
    };

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) [2004-2009] Novell, Inc.
 * Copyright (c) 2018 SUSE LLC
 *
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, contact Novell, Inc.
 *
 * To contact Novell about this file by physical or electronic mail, you may
 * find current contact information at www.novell.com.
 */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>

#include "storage/Utils/LoggerImpl.h"
#include "storage/Utils/LockImpl.h"
#include "storage/Utils/ExceptionImpl.h"
#include "storage/Utils/StorageTmpl.h"

#define LOCK_DIR "/run/libstorage-ng"

namespace storage
{

    vector<const Lock*> Lock::locks;

    int Lock::fd = -1;

    Lock::Lock(bool read_only, bool disable)
	: read_only(read_only), disabled(disable)
    {
	if (disabled)
	    return;

	y2mil("getting " << (read_only ? "read-only" : "read-write") << " lock");

	if (locks.empty())
	{
	    // If there are no locks within the same process try to take the
	    // system-wide lock.

	    if (mkdir(LOCK_DIR, 0755) == -1 && errno != EEXIST)
	    {
		y2err("creating directory for lock-file failed: " << strerror(errno));
	    }

	    fd = open(LOCK_DIR "/lock", (read_only ? O_RDONLY : O_WRONLY) | O_CREAT | O_CLOEXEC,
		      S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	    if (fd < 0)
	    {
		// Opening lock-file failed.
		y2err("opening lock-file failed: " << strerror(errno));
		ST_THROW(LockException(0));
	    }

	    struct flock lock;
	    memset(&lock, 0, sizeof(lock));
	    lock.l_type = (read_only ? F_RDLCK : F_WRLCK);
	    lock.l_whence = SEEK_SET;
	    if (fcntl(fd, F_SETLK, &lock) < 0)
	    {
		switch (errno)
		{
		    case EACCES:
		    case EAGAIN:
			// Another process has a lock. Between the two fcntl
			// calls the lock of the other process could be
			// release. In that case we don't get the pid (and it is
			// still 0).
			fcntl(fd, F_GETLK, &lock);
			close(fd);
			y2err("locked by process " << lock.l_pid);
			ST_THROW(LockException(lock.l_pid));

		    default:
			// Some other error.
			close(fd);
			y2err("getting lock failed: " << strerror(errno));
			ST_THROW(LockException(0));
		}
	    }
	}
	else
	{
	    // If there are locks within the same process check if a further
	    // lock is allowed.

	    if (read_only)
	    {
		// no read-write lock of the process allowed

		if (any_of(locks.begin(), locks.end(), [](const Lock* tmp) { return !tmp->read_only; }))
		{
		    y2err("read-write lock by same process exists");
		    ST_THROW(LockException(-2));
		}
	    }
	    else
	    {
		// no other lock of the process allowed

		y2err("lock by same process exists");
		ST_THROW(LockException(-2));
	    }
	}

	// Add this lock to the list of locks in the same process.

	locks.push_back(this);

	y2mil("lock succeeded");
    }

    Lock::~Lock() noexcept
    {
	if (disabled)
	    return;

	// Remove this lock from the list of locks in the same process.

	erase(locks, this);

	if (locks.empty())
	{
	    // If this process has no further locks release the system-wide
	    // lock.

	    close(fd);
	    fd = -1;

	    // Do not bother deleting lock-file. Likelihood of race conditions
	    // is to high.
	}
    }

}
<commit_msg>Create lock with proper permissions (bsc#1059972)<commit_after>/*
 * Copyright (c) [2004-2009] Novell, Inc.
 * Copyright (c) 2018 SUSE LLC
 *
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, contact Novell, Inc.
 *
 * To contact Novell about this file by physical or electronic mail, you may
 * find current contact information at www.novell.com.
 */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>

#include "storage/Utils/LoggerImpl.h"
#include "storage/Utils/LockImpl.h"
#include "storage/Utils/ExceptionImpl.h"
#include "storage/Utils/StorageTmpl.h"

#define LOCK_DIR "/run/libstorage-ng"

namespace storage
{

    vector<const Lock*> Lock::locks;

    int Lock::fd = -1;

    Lock::Lock(bool read_only, bool disable)
	: read_only(read_only), disabled(disable)
    {
	if (disabled)
	    return;

	y2mil("getting " << (read_only ? "read-only" : "read-write") << " lock");

	if (locks.empty())
	{
	    // If there are no locks within the same process try to take the
	    // system-wide lock.

	    if (mkdir(LOCK_DIR, 0755) == -1 && errno != EEXIST)
	    {
		y2err("creating directory for lock-file failed: " << strerror(errno));
	    }

	    fd = open(LOCK_DIR "/lock", (read_only ? O_RDONLY : O_WRONLY) | O_CREAT | O_CLOEXEC, 0600);

	    if (fd < 0)
	    {
		// Opening lock-file failed.
		y2err("opening lock-file failed: " << strerror(errno));
		ST_THROW(LockException(0));
	    }

	    struct flock lock;
	    memset(&lock, 0, sizeof(lock));
	    lock.l_type = (read_only ? F_RDLCK : F_WRLCK);
	    lock.l_whence = SEEK_SET;
	    if (fcntl(fd, F_SETLK, &lock) < 0)
	    {
		switch (errno)
		{
		    case EACCES:
		    case EAGAIN:
			// Another process has a lock. Between the two fcntl
			// calls the lock of the other process could be
			// release. In that case we don't get the pid (and it is
			// still 0).
			fcntl(fd, F_GETLK, &lock);
			close(fd);
			y2err("locked by process " << lock.l_pid);
			ST_THROW(LockException(lock.l_pid));

		    default:
			// Some other error.
			close(fd);
			y2err("getting lock failed: " << strerror(errno));
			ST_THROW(LockException(0));
		}
	    }
	}
	else
	{
	    // If there are locks within the same process check if a further
	    // lock is allowed.

	    if (read_only)
	    {
		// no read-write lock of the process allowed

		if (any_of(locks.begin(), locks.end(), [](const Lock* tmp) { return !tmp->read_only; }))
		{
		    y2err("read-write lock by same process exists");
		    ST_THROW(LockException(-2));
		}
	    }
	    else
	    {
		// no other lock of the process allowed

		y2err("lock by same process exists");
		ST_THROW(LockException(-2));
	    }
	}

	// Add this lock to the list of locks in the same process.

	locks.push_back(this);

	y2mil("lock succeeded");
    }

    Lock::~Lock() noexcept
    {
	if (disabled)
	    return;

	// Remove this lock from the list of locks in the same process.

	erase(locks, this);

	if (locks.empty())
	{
	    // If this process has no further locks release the system-wide
	    // lock.

	    close(fd);
	    fd = -1;

	    // Do not bother deleting lock-file. Likelihood of race conditions
	    // is to high.
	}
    }

}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "helpwindow.h"
#include "utils.h"

#include "ui_mainwindow.h"

#ifdef Q_WS_WIN
    #include <windows.h>
#endif

#include <QApplication>
#include <QCloseEvent>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDir>
#include <QHelpEngine>
#include <QMessageBox>
#include <QSettings>
#include <QTemporaryFile>
#include <QUrl>

#define OPENCOR_HOMEPAGE "http://opencor.sourceforge.net/"

#define OPENCOR_HELP_HOMEPAGE QUrl("qthelp://world.opencor/doc/index.html")

#define SETTINGS_INSTITUTION "World"
#define SETTINGS_GENERAL_LOCALE "General_Locale"
#define SETTINGS_GENERAL_GEOMETRY "General_Geometry"
#define SETTINGS_GENERAL_STATE "General_State"
#define SETTINGS_HELPWINDOW_ZOOMLEVEL "HelpWindow_ZoomLevel"

MainWindow::MainWindow(QWidget *pParent) :
    QMainWindow(pParent),
    mUi(new Ui::MainWindow)
{
    // Set up the GUI

    mUi->setupUi(this);

    // Set the name of the main window to that of the application

    setWindowTitle(qApp->applicationName());

    // Some basic signals/events for some actions

    connect(mUi->actionExit, SIGNAL(triggered(bool)),
            this, SLOT(close()));
    connect(mUi->actionResetAll, SIGNAL(triggered(bool)),
            this, SLOT(resetAll()));

    // Signals/events for showing/hiding the various toolbars

    connect(mUi->actionHelpToolbar, SIGNAL(triggered(bool)),
            mUi->helpToolbar, SLOT(setVisible(bool)));
    connect(mUi->helpToolbar->toggleViewAction(), SIGNAL(toggled(bool)),
            mUi->actionHelpToolbar, SLOT(setChecked(bool)));

    // Extract the help files

    QTemporaryFile tempDir;

    tempDir.open();    // Note: this is required to get a 'valid' temporary
    tempDir.close();   //       directory name...

    mTempDirName = tempDir.fileName().append(".dir");

    QDir().mkdir(mTempDirName);

    QString applicationBaseName(QFileInfo(qApp->applicationFilePath()).baseName());

    mQchFileName = mTempDirName+QDir::separator()+applicationBaseName+".qch";
    mQhcFileName = mTempDirName+QDir::separator()+applicationBaseName+".qhc";

    saveResourceAs(":qchFile", mQchFileName);
    saveResourceAs(":qhcFile", mQhcFileName);

    // Set up the help engine

    mHelpEngine = new QHelpEngine(mQhcFileName);

    mHelpEngine->setupData();

    // Help window

    mHelpWindow = new HelpWindow(mHelpEngine, OPENCOR_HELP_HOMEPAGE);

    connect(mUi->actionHelp, SIGNAL(triggered(bool)),
            mHelpWindow, SLOT(setVisible(bool)));
    connect(mHelpWindow, SIGNAL(visibilityChanged(bool)),
            mUi->actionHelp, SLOT(setChecked(bool)));

    // Default user settings

    resetAll(false);

    // Retrieve our default settings

    loadSettings();

    // Update the GUI, which may have changed (e.g. hidden toolbar) as a result
    // of loading OpenCOR's settings

    updateGUI();
}

MainWindow::~MainWindow()
{
    // Delete some internal objects

    delete mHelpEngine;
    delete mUi;

    // Delete the help files

    QFile(mQchFileName).remove();
    QFile(mQhcFileName).remove();

    QDir().rmdir(mTempDirName);
}

void MainWindow::closeEvent(QCloseEvent *pEvent)
{
    // Keep track of our default settings
    // Note: it must be done here, as opposed to the destructor, otherwise some
    //       settings (e.g. docked windows) won't be properly saved

    saveSettings();

    pEvent->accept();
}

void MainWindow::singleAppMsgRcvd(const QString&)
{
    // We have just received a message from another instance of OpenCOR, so
    // bring ourselves to the foreground
    // Note: one would normally use activateWindow(), but depending on the
    //       operating system it may or not bring OpenCOR to the foreground,
    //       so... instead we do what follows, depending on the operating
    //       system...

#ifdef Q_WS_WIN
    // Retrieve OpenCOR's window Id

    WId mwWinId = winId();

    // Restore OpenCOR, should it be minimized

    if (IsIconic(mwWinId))
        SendMessage(mwWinId, WM_SYSCOMMAND, SC_RESTORE, 0);

    // Bring OpenCOR to the foreground

    DWORD foregroundThreadPId = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
    DWORD mwThreadPId         = GetWindowThreadProcessId(mwWinId, NULL);

    if (foregroundThreadPId != mwThreadPId)
    {
        // OpenCOR's thread process Id is not that of the foreground window, so
        // attach the foreground thread to OpenCOR's, set OpenCOR to the
        // foreground, and detach the foreground thread from OpenCOR's

        AttachThreadInput(foregroundThreadPId, mwThreadPId, true);

        SetForegroundWindow(mwWinId);

        AttachThreadInput(foregroundThreadPId, mwThreadPId, false);
    }
    else
        // OpenCOR's thread process Id is that of the foreground window, so
        // just set OpenCOR to the foreground

        SetForegroundWindow(mwWinId);

    // Note: under Windows, to use activateWindow() will only highlight the
    //       application in the taskbar, since under Windows no application
    //       should be allowed to bring itself to the foreground when another
    //       application is already in the foreground. Fair enough, but it
    //       happens that, here, the user wants OpenCOR to be brought to the
    //       foreground, hence the above code to get the effect we are after...
#else
    // Do what one should normally do and which works fine under Mac OS X

    activateWindow();

    #ifdef Q_WS_X11
        raise();
        // Note: under Linux, to use activateWindow() may or not give the
        //       expected result. The above code is supposed to make sure that
        //       OpenCOR gets brought to the foreground, but that itsn't the
        //       case under Ubuntu at least (it works when you want to open a
        //       second instance of OpenCOR, but not thereafter!)...
    #endif
#endif

    // Now, we must handle the arguments that were passed to us

    // TODO: handle the arguments passed to the 'official' instance of OpenCOR
}

void MainWindow::loadSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, qApp->applicationName());

    // Retrieve the language to be used by OpenCOR, with a default just in case

    setLocale(settings.value(SETTINGS_GENERAL_LOCALE, QLocale::system().name()).toString());

    // Retrieve the geometry of the main window

    restoreGeometry(settings.value(SETTINGS_GENERAL_GEOMETRY).toByteArray());

    // Retrieve the state of the main window

    restoreState(settings.value(SETTINGS_GENERAL_STATE).toByteArray());

    // Retrieve the zoom level for the help widget, with a default value just
    // in case

    mHelpWindow->setZoomLevel(settings.value(SETTINGS_HELPWINDOW_ZOOMLEVEL, mHelpWindow->defaultZoomLevel()).toInt());
}

void MainWindow::saveSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, qApp->applicationName());

    // Keep track of the language to be used by OpenCOR

    settings.setValue(SETTINGS_GENERAL_LOCALE, mLocale);

    // Keep track of the geometry of the main window

    settings.setValue(SETTINGS_GENERAL_GEOMETRY, saveGeometry());

    // Keep track of the state of the main window

    settings.setValue(SETTINGS_GENERAL_STATE, saveState());

    // Keep track of the text size multiplier for the help widget

    settings.setValue(SETTINGS_HELPWINDOW_ZOOMLEVEL, mHelpWindow->zoomLevel());
}

void MainWindow::setLocale(const QString& pLocale)
{
    if (pLocale != mLocale)
    {
        mLocale = pLocale;

        // Specify the language to be used by OpenCOR

//        qApp->removeTranslator(qtTranslator);
//        qtTranslator->load("qt_"+pLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
//        qApp->installTranslator(qtTranslator);

//        qApp->removeTranslator(appTranslator);
//        appTranslator->load(":app_"+pLocale);
//        qApp->installTranslator(appTranslator);
    }

    // Update the checked menu item
    // Note: it has to be done every single time, since selecting a menu item
    //       will automatically toggle its checked status, so...

    mUi->actionEnglish->setChecked(pLocale.startsWith("en"));
    mUi->actionFrench->setChecked(pLocale.startsWith("fr"));
}

void MainWindow::notYetImplemented(const QString& pMsg)
{
    // Display a warning message about a particular feature having not yet been
    // implemented

    QMessageBox::warning(this, qApp->applicationName(), pMsg+tr(" has not yet been implemented..."),
                         QMessageBox::Ok, QMessageBox::Ok);
}

void MainWindow::on_actionEnglish_triggered()
{
    // Select English as the language used by OpenCOR

    setLocale("en");
}

void MainWindow::on_actionFrench_triggered()
{
    // Select French as the language used by OpenCOR

    setLocale("fr");
}

void MainWindow::updateGUI()
{
    // Update the checked status of the toolbars menu items

    mUi->actionHelpToolbar->setChecked(mUi->helpToolbar->isVisible());
}

void MainWindow::on_actionHomepage_triggered()
{
    // Look up the OpenCOR home page

    QDesktopServices::openUrl(QUrl(OPENCOR_HOMEPAGE));
}

void MainWindow::on_actionAbout_triggered()
{
    QMessageBox::about(this, qApp->applicationName(),
                       QString("")+
                       ""+
                           "
"+qApp->applicationName()+" "+qApp->applicationVersion()+"
"+
                           "
"+getOsName()+"
"+
                       ""+
                       "
"+
                       ""+qApp->applicationName()+" "+tr("is a cross-platform CellML-based modelling environment which can be used to organise, edit, simulate and analyse CellML files.")+"

"+
                       qApp->applicationName()+" "+tr("is written in C++, using the Qt framework, and is not currently released under any particular license, but this is due to change in the near future."));
}

void MainWindow::resetAll(const bool& pClearUserSettings)
{
    // Default language to be used by OpenCOR

    setLocale(QLocale::system().name());

    // Default size and position of both the main and help windows

    const double mainRatio = 3.0/5.0;
    const double helpRatio = 1.0/3.0;
    const double spaceRatio = 1.0/45.0;
    const double horizSpace = spaceRatio*qApp->desktop()->width();
    const double vertSpace  = 2.0*spaceRatio*qApp->desktop()->height();

    mHelpWindow->setVisible(false);   // By default

    addDockWidget(Qt::RightDockWidgetArea, mHelpWindow);
    // Note: the above is only required so that the help window can then be
    //       docked to the main window, should the user want to do that.
    //       Indeed, to make the help window float is not sufficient, so...

    mHelpWindow->setFloating(true);

    resize(QSize(mainRatio*qApp->desktop()->width(), mainRatio*qApp->desktop()->height()));
    mHelpWindow->resize(helpRatio*qApp->desktop()->width(), size().height());

    move(QPoint(horizSpace, vertSpace));
    mHelpWindow->move(QPoint(qApp->desktop()->width()-mHelpWindow->size().width()-horizSpace, vertSpace));

    // Default settings for the help widget

    mHelpWindow->resetAll();

    // Default visibility and location of the various toolbars

    this->addToolBar(Qt::TopToolBarArea, mUi->helpToolbar);

    mUi->helpToolbar->setVisible(true);

    // Clear all the user settings, if required

    if (pClearUserSettings)
        QSettings(SETTINGS_INSTITUTION, qApp->applicationName()).clear();
}
<commit_msg>Updated the about box.<commit_after>#include "mainwindow.h"
#include "helpwindow.h"
#include "utils.h"

#include "ui_mainwindow.h"

#ifdef Q_WS_WIN
    #include <windows.h>
#endif

#include <QApplication>
#include <QCloseEvent>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDir>
#include <QHelpEngine>
#include <QMessageBox>
#include <QSettings>
#include <QTemporaryFile>
#include <QUrl>

#define OPENCOR_HOMEPAGE "http://opencor.sourceforge.net/"

#define OPENCOR_HELP_HOMEPAGE QUrl("qthelp://world.opencor/doc/index.html")

#define SETTINGS_INSTITUTION "World"
#define SETTINGS_GENERAL_LOCALE "General_Locale"
#define SETTINGS_GENERAL_GEOMETRY "General_Geometry"
#define SETTINGS_GENERAL_STATE "General_State"
#define SETTINGS_HELPWINDOW_ZOOMLEVEL "HelpWindow_ZoomLevel"

MainWindow::MainWindow(QWidget *pParent) :
    QMainWindow(pParent),
    mUi(new Ui::MainWindow)
{
    // Set up the GUI

    mUi->setupUi(this);

    // Set the name of the main window to that of the application

    setWindowTitle(qApp->applicationName());

    // Some basic signals/events for some actions

    connect(mUi->actionExit, SIGNAL(triggered(bool)),
            this, SLOT(close()));
    connect(mUi->actionResetAll, SIGNAL(triggered(bool)),
            this, SLOT(resetAll()));

    // Signals/events for showing/hiding the various toolbars

    connect(mUi->actionHelpToolbar, SIGNAL(triggered(bool)),
            mUi->helpToolbar, SLOT(setVisible(bool)));
    connect(mUi->helpToolbar->toggleViewAction(), SIGNAL(toggled(bool)),
            mUi->actionHelpToolbar, SLOT(setChecked(bool)));

    // Extract the help files

    QTemporaryFile tempDir;

    tempDir.open();    // Note: this is required to get a 'valid' temporary
    tempDir.close();   //       directory name...

    mTempDirName = tempDir.fileName().append(".dir");

    QDir().mkdir(mTempDirName);

    QString applicationBaseName(QFileInfo(qApp->applicationFilePath()).baseName());

    mQchFileName = mTempDirName+QDir::separator()+applicationBaseName+".qch";
    mQhcFileName = mTempDirName+QDir::separator()+applicationBaseName+".qhc";

    saveResourceAs(":qchFile", mQchFileName);
    saveResourceAs(":qhcFile", mQhcFileName);

    // Set up the help engine

    mHelpEngine = new QHelpEngine(mQhcFileName);

    mHelpEngine->setupData();

    // Help window

    mHelpWindow = new HelpWindow(mHelpEngine, OPENCOR_HELP_HOMEPAGE);

    connect(mUi->actionHelp, SIGNAL(triggered(bool)),
            mHelpWindow, SLOT(setVisible(bool)));
    connect(mHelpWindow, SIGNAL(visibilityChanged(bool)),
            mUi->actionHelp, SLOT(setChecked(bool)));

    // Default user settings

    resetAll(false);

    // Retrieve our default settings

    loadSettings();

    // Update the GUI, which may have changed (e.g. hidden toolbar) as a result
    // of loading OpenCOR's settings

    updateGUI();
}

MainWindow::~MainWindow()
{
    // Delete some internal objects

    delete mHelpEngine;
    delete mUi;

    // Delete the help files

    QFile(mQchFileName).remove();
    QFile(mQhcFileName).remove();

    QDir().rmdir(mTempDirName);
}

void MainWindow::closeEvent(QCloseEvent *pEvent)
{
    // Keep track of our default settings
    // Note: it must be done here, as opposed to the destructor, otherwise some
    //       settings (e.g. docked windows) won't be properly saved

    saveSettings();

    pEvent->accept();
}

void MainWindow::singleAppMsgRcvd(const QString&)
{
    // We have just received a message from another instance of OpenCOR, so
    // bring ourselves to the foreground
    // Note: one would normally use activateWindow(), but depending on the
    //       operating system it may or not bring OpenCOR to the foreground,
    //       so... instead we do what follows, depending on the operating
    //       system...

#ifdef Q_WS_WIN
    // Retrieve OpenCOR's window Id

    WId mwWinId = winId();

    // Restore OpenCOR, should it be minimized

    if (IsIconic(mwWinId))
        SendMessage(mwWinId, WM_SYSCOMMAND, SC_RESTORE, 0);

    // Bring OpenCOR to the foreground

    DWORD foregroundThreadPId = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
    DWORD mwThreadPId         = GetWindowThreadProcessId(mwWinId, NULL);

    if (foregroundThreadPId != mwThreadPId)
    {
        // OpenCOR's thread process Id is not that of the foreground window, so
        // attach the foreground thread to OpenCOR's, set OpenCOR to the
        // foreground, and detach the foreground thread from OpenCOR's

        AttachThreadInput(foregroundThreadPId, mwThreadPId, true);

        SetForegroundWindow(mwWinId);

        AttachThreadInput(foregroundThreadPId, mwThreadPId, false);
    }
    else
        // OpenCOR's thread process Id is that of the foreground window, so
        // just set OpenCOR to the foreground

        SetForegroundWindow(mwWinId);

    // Note: under Windows, to use activateWindow() will only highlight the
    //       application in the taskbar, since under Windows no application
    //       should be allowed to bring itself to the foreground when another
    //       application is already in the foreground. Fair enough, but it
    //       happens that, here, the user wants OpenCOR to be brought to the
    //       foreground, hence the above code to get the effect we are after...
#else
    // Do what one should normally do and which works fine under Mac OS X

    activateWindow();

    #ifdef Q_WS_X11
        raise();
        // Note: under Linux, to use activateWindow() may or not give the
        //       expected result. The above code is supposed to make sure that
        //       OpenCOR gets brought to the foreground, but that itsn't the
        //       case under Ubuntu at least (it works when you want to open a
        //       second instance of OpenCOR, but not thereafter!)...
    #endif
#endif

    // Now, we must handle the arguments that were passed to us

    // TODO: handle the arguments passed to the 'official' instance of OpenCOR
}

void MainWindow::loadSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, qApp->applicationName());

    // Retrieve the language to be used by OpenCOR, with a default just in case

    setLocale(settings.value(SETTINGS_GENERAL_LOCALE, QLocale::system().name()).toString());

    // Retrieve the geometry of the main window

    restoreGeometry(settings.value(SETTINGS_GENERAL_GEOMETRY).toByteArray());

    // Retrieve the state of the main window

    restoreState(settings.value(SETTINGS_GENERAL_STATE).toByteArray());

    // Retrieve the zoom level for the help widget, with a default value just
    // in case

    mHelpWindow->setZoomLevel(settings.value(SETTINGS_HELPWINDOW_ZOOMLEVEL, mHelpWindow->defaultZoomLevel()).toInt());
}

void MainWindow::saveSettings()
{
    QSettings settings(SETTINGS_INSTITUTION, qApp->applicationName());

    // Keep track of the language to be used by OpenCOR

    settings.setValue(SETTINGS_GENERAL_LOCALE, mLocale);

    // Keep track of the geometry of the main window

    settings.setValue(SETTINGS_GENERAL_GEOMETRY, saveGeometry());

    // Keep track of the state of the main window

    settings.setValue(SETTINGS_GENERAL_STATE, saveState());

    // Keep track of the text size multiplier for the help widget

    settings.setValue(SETTINGS_HELPWINDOW_ZOOMLEVEL, mHelpWindow->zoomLevel());
}

void MainWindow::setLocale(const QString& pLocale)
{
    if (pLocale != mLocale)
    {
        mLocale = pLocale;

        // Specify the language to be used by OpenCOR

//        qApp->removeTranslator(qtTranslator);
//        qtTranslator->load("qt_"+pLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
//        qApp->installTranslator(qtTranslator);

//        qApp->removeTranslator(appTranslator);
//        appTranslator->load(":app_"+pLocale);
//        qApp->installTranslator(appTranslator);
    }

    // Update the checked menu item
    // Note: it has to be done every single time, since selecting a menu item
    //       will automatically toggle its checked status, so...

    mUi->actionEnglish->setChecked(pLocale.startsWith("en"));
    mUi->actionFrench->setChecked(pLocale.startsWith("fr"));
}

void MainWindow::notYetImplemented(const QString& pMsg)
{
    // Display a warning message about a particular feature having not yet been
    // implemented

    QMessageBox::warning(this, qApp->applicationName(), pMsg+tr(" has not yet been implemented..."),
                         QMessageBox::Ok, QMessageBox::Ok);
}

void MainWindow::on_actionEnglish_triggered()
{
    // Select English as the language used by OpenCOR

    setLocale("en");
}

void MainWindow::on_actionFrench_triggered()
{
    // Select French as the language used by OpenCOR

    setLocale("fr");
}

void MainWindow::updateGUI()
{
    // Update the checked status of the toolbars menu items

    mUi->actionHelpToolbar->setChecked(mUi->helpToolbar->isVisible());
}

void MainWindow::on_actionHomepage_triggered()
{
    // Look up the OpenCOR home page

    QDesktopServices::openUrl(QUrl(OPENCOR_HOMEPAGE));
}

void MainWindow::on_actionAbout_triggered()
{
    QMessageBox::about(this, qApp->applicationName(),
                       QString("")+
                       ""+
                           "
"+qApp->applicationName()+" "+qApp->applicationVersion()+"
"+
                           "
"+getOsName()+"
"+
                       ""+
                       "
"+
                       ""+qApp->applicationName()+" "+tr("is a cross-platform CellML-based modelling environment which can be used to organise, edit, simulate and analyse CellML files."));
}

void MainWindow::resetAll(const bool& pClearUserSettings)
{
    // Default language to be used by OpenCOR

    setLocale(QLocale::system().name());

    // Default size and position of both the main and help windows

    const double mainRatio = 3.0/5.0;
    const double helpRatio = 1.0/3.0;
    const double spaceRatio = 1.0/45.0;
    const double horizSpace = spaceRatio*qApp->desktop()->width();
    const double vertSpace  = 2.0*spaceRatio*qApp->desktop()->height();

    mHelpWindow->setVisible(false);   // By default

    addDockWidget(Qt::RightDockWidgetArea, mHelpWindow);
    // Note: the above is only required so that the help window can then be
    //       docked to the main window, should the user want to do that.
    //       Indeed, to make the help window float is not sufficient, so...

    mHelpWindow->setFloating(true);

    resize(QSize(mainRatio*qApp->desktop()->width(), mainRatio*qApp->desktop()->height()));
    mHelpWindow->resize(helpRatio*qApp->desktop()->width(), size().height());

    move(QPoint(horizSpace, vertSpace));
    mHelpWindow->move(QPoint(qApp->desktop()->width()-mHelpWindow->size().width()-horizSpace, vertSpace));

    // Default settings for the help widget

    mHelpWindow->resetAll();

    // Default visibility and location of the various toolbars

    this->addToolBar(Qt::TopToolBarArea, mUi->helpToolbar);

    mUi->helpToolbar->setVisible(true);

    // Clear all the user settings, if required

    if (pClearUserSettings)
        QSettings(SETTINGS_INSTITUTION, qApp->applicationName()).clear();
}
<|endoftext|>


<commit_before>/*
* Copyright (c) 2013 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <QtCore/QWaitCondition>
#include <QtCore/QFile>
#include <QtCore/QMap>
#include "VSScript.h"
#include "VSHelper.h"

#ifdef _WIN32
static inline QString nativeToQString(const wchar_t *str) {
	return QString::fromWCharArray(str);
}
#else
static inline QString nativeToQString(const char *str) {
	return QString::fromLocal8Bit(str);
}
#endif

const VSAPI *vsapi = NULL;
VSScript *se = NULL;
VSNodeRef *node = NULL;
FILE *outFile = NULL;

int requests = 0;
int index = 0;
int outputFrames = 0;
int requestedFrames = 0;
int completedFrames = 0;
int totalFrames = 0;
int numPlanes = 0;
bool y4m = false;
bool outputError = false;
bool showInfo = false;
QMap<int, const VSFrameRef *> reorderMap;

QString errorMessage;
QWaitCondition condition;
QMutex mutex;

void VS_CC frameDoneCallback(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg) {
    completedFrames++;

    if (f) {
        reorderMap.insert(n, f);
        while (reorderMap.contains(outputFrames)) {
            const VSFrameRef *frame = reorderMap.take(outputFrames);
            if (!outputError) {
				if (y4m) {
					if (!fwrite("FRAME\n", 6, 1, outFile)) {
						errorMessage = "Error: fwrite() call failed";
						totalFrames = requestedFrames;
						outputError = true;
					}
				}

				if (!outputError) {
					const VSFormat *fi = vsapi->getFrameFormat(frame);
					for (int p = 0; p < fi->numPlanes; p++) {
						int stride = vsapi->getStride(frame, p);
						const uint8_t *readPtr = vsapi->getReadPtr(frame, p);
						int rowSize = vsapi->getFrameWidth(frame, p) * fi->bytesPerSample;
						int height = vsapi->getFrameHeight(frame, p);
						for (int y = 0; y < height; y++) {
							if (!fwrite(readPtr, rowSize, 1, outFile)) {
								errorMessage = "Error: fwrite() call failed";
								totalFrames = requestedFrames;
								outputError = true;
								p = 100; // break out of the outer loop
								break;
							}
							readPtr += stride;
						}
					}
				}
			}
            vsapi->freeFrame(frame);
            outputFrames++;
        }
    } else {
        outputError = true;
        totalFrames = requestedFrames;
        if (errorMsg)
            errorMessage = QString("Error: Failed to retrieve frame ") + n + QString(" with error: ") + QString::fromUtf8(errorMsg);
        else
            errorMessage = QString("Error: Failed to retrieve frame ") + n;
    }

    if (requestedFrames < totalFrames) {
        vsapi->getFrameAsync(requestedFrames, node, frameDoneCallback, NULL);
        requestedFrames++;
    }

    if (totalFrames == completedFrames) {
        QMutexLocker lock(&mutex);
        condition.wakeOne();
    }
}

bool outputNode() {
    if (requests < 1) {
		const VSCoreInfo *info = vsapi->getCoreInfo(vseval_getCore());
        requests = info->numThreads;
	}

	const VSVideoInfo *vi = vsapi->getVideoInfo(node);
	totalFrames = vi->numFrames;

	if (y4m && (vi->format->colorFamily != cmGray && vi->format->colorFamily != cmYUV)) {
		errorMessage = "Error: Can only apply y4m headers to YUV and Gray format clips";
		fprintf(stderr, "%s", errorMessage.toUtf8().constData());
        return true;
	}

    QString y4mFormat;
    QString numBits;

    if (y4m) {
        if (vi->format->colorFamily == cmGray) {
            y4mFormat = "mono";
            if (vi->format->bitsPerSample > 8)
				y4mFormat = y4mFormat + QString::number(vi->format->bitsPerSample);
		} else if (vi->format->colorFamily == cmYUV) {
			if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 1)
                y4mFormat = "420";
			else if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 0)
                y4mFormat = "422";
			else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 0)
                y4mFormat = "444";
			else if (vi->format->subSamplingW == 2 && vi->format->subSamplingH == 2)
                y4mFormat = "410";
			else if (vi->format->subSamplingW == 2 && vi->format->subSamplingH == 0)
                y4mFormat = "411";
			else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 1)
                y4mFormat = "440";
			else {
				fprintf(stderr, "No y4m identifier exists for current format");
				return true;
			}

            if (vi->format->bitsPerSample > 8)
                y4mFormat = y4mFormat + "p" + QString::number(vi->format->bitsPerSample);
		} else {
			fprintf(stderr, "No y4m identifier exists for current format");
			return true;
		}
    }
	if (!y4mFormat.isEmpty())
        y4mFormat = "C" + y4mFormat + " ";

	QString header = "YUV4MPEG2 " + y4mFormat + "W" + QString::number(vi->width) + " H" + QString::number(vi->height) + " F" + QString::number(vi->fpsNum)  + ":" + QString::number(vi->fpsDen)  + " Ip A0:0\n";
    QByteArray rawHeader = header.toUtf8();

    if (y4m) {
        if (!fwrite(rawHeader.constData(), rawHeader.size(), 1, outFile)) {
            errorMessage = "Error: fwrite() call failed";
			fprintf(stderr, "%s", errorMessage.toUtf8().constData());
			outputError = true;
			return outputError;
        }
    }

	QMutexLocker lock(&mutex);

	int intitalRequestSize = std::min(requests, totalFrames);
	requestedFrames = intitalRequestSize;
	for (int n = 0; n < intitalRequestSize; n++)
		vsapi->getFrameAsync(n, node, frameDoneCallback, NULL);

	condition.wait(&mutex);

    if (outputError) {
        fprintf(stderr, "%s", errorMessage.toUtf8().constData());
    }

    return outputError;
}

const char *colorFamilyToString(int colorFamily) {
	switch (colorFamily) {
	case cmGray: return "Gray";
	case cmRGB: return "RGB";
	case cmYUV: return "YUV";
	case cmYCoCg: return "YCoCg";
	case cmCompat: return "Compat";
	}
	return "";
}

// fixme, only allow info without output
#ifdef _WIN32
int wmain(int argc, wchar_t **argv) {
#else
int main(int argc, char **argv) {
#endif

    if (argc < 3) {
        fprintf(stderr, "VSPipe usage:\n");
		fprintf(stderr, "Show script info: vspipe script.vpy - -info\n");
        fprintf(stderr, "Write to stdout: vspipe script.vpy - [options]\n");
        fprintf(stderr, "Write to file: vspipe script.vpy <outFile> [options]\n");
        fprintf(stderr, "Available options:\n");
		fprintf(stderr, "Select output index: -index N\n");
		fprintf(stderr, "Set number of concurrent frame requests: -requests N\n");
		fprintf(stderr, "Add YUV4MPEG headers: -y4m\n");
		fprintf(stderr, "Show video info: -info (overrides other options)\n");
        return 1;
    }

	QFile scriptFile(nativeToQString(argv[1]));
	if (!scriptFile.open(QIODevice::ReadOnly)) {
        fprintf(stderr, "Failed to to open script file for reading\n");
        return 1;
	}

	if (scriptFile.size() > 1024*1024*16) {
        fprintf(stderr, "Script files bigger than 16MB not allowed\n");
        return 1;
	}

    QByteArray scriptData = scriptFile.readAll();
	scriptFile.close();
    if (scriptData.isEmpty()) {
        fprintf(stderr, "Failed to read script file or file is empty\n");
        return 1;
    }

	QString outputFilename = nativeToQString(argv[2]);
	if (outputFilename == "-") {
		outFile = stdout;
	} else {
#ifdef _WIN32
		outFile = _wfopen(outputFilename.toStdWString().c_str(), L"wb");
#else
		outfile = fopen(outputFilename.toLocal8Bit(), "wb");
#endif
		if (!outFile) {
			fprintf(stderr, "Failed to open output for writing\n");
			return 1;
		}
	}

	for (int arg = 3; arg < argc; arg++) {
		QString argString = nativeToQString(argv[arg]);
		if (argString == "-y4m") {
			y4m = true;
		} else if (argString == "-info") {
			showInfo = true;
		} else if (argString == "-index") {
			bool ok = false;
			if (argc <= arg + 1) {
				fprintf(stderr, "No index number specified");
				return 1;
			}
			QString numString = nativeToQString(argv[arg+1]);
			index = numString.toInt(&ok);
			if (!ok) {
				fprintf(stderr, "Couldn't convert %s to an integer", numString.toUtf8().constData());
				return 1;
			}
			arg++;
		} else if (argString == "-requests") {
			bool ok = false;
			if (argc <= arg + 1) {
				fprintf(stderr, "No request number specified");
				return 1;
			}
			QString numString = nativeToQString(argv[arg+1]);
			requests = numString.toInt(&ok);
			if (!ok) {
				fprintf(stderr, "Couldn't convert %s to an integer", numString.toUtf8().constData());
				return 1;
			}
			arg++;
		} else {
			fprintf(stderr, "Unknown argument: %s\n", argString.toUtf8().constData());
			return 1;
		}
	}

    if (!vseval_init()) {
        fprintf(stderr, "Failed to initialize VapourSynth environment\n");
        return 1;
    }

    vsapi = vseval_getVSApi();
    if (!vsapi) {
        fprintf(stderr, "Failed to get VapourSynth API pointer\n");
        vseval_finalize();
        return 1;
    }

	if (vseval_evaluateScript(&se, scriptData.constData(), nativeToQString(argv[1]).toUtf8())) {
        fprintf(stderr, "Script evaluation failed:\n%s", vseval_getError(se));
        vseval_freeScript(se);
        vseval_finalize();
        return 1;
    }

    node = vseval_getOutput(se, index);
    if (!node) {
       fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n");
       vseval_freeScript(se);
       vseval_finalize();
       return 1;
    }

	bool error = false;
	const VSVideoInfo *vi = vsapi->getVideoInfo(node);

	if (showInfo) {
		fprintf(outFile, "Width: %d\n", vi->width);
		fprintf(outFile, "Height: %d\n", vi->height);
		fprintf(outFile, "Frames: %d\n", vi->numFrames);
		fprintf(outFile, "FPS: %d/%d\n", vi->fpsNum, vi->fpsDen);
		if (vi->format) {
			fprintf(outFile, "Format Name: %s\n", vi->format->name);
			fprintf(outFile, "Color Family: %s\n", colorFamilyToString(vi->format->colorFamily));
			fprintf(outFile, "Bits: %d\n", vi->format->bitsPerSample);
			fprintf(outFile, "SubSampling W: %d\n", vi->format->subSamplingW);
			fprintf(outFile, "SubSampling H: %d\n", vi->format->subSamplingH);
		} else {
			printf("Format Name: Variable\n");
		}
	} else {
		if (!isConstantFormat(vi) || vi->numFrames == 0) {
			fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n");
			vseval_freeScript(se);
			vseval_finalize();
			return 1;
		}

		error = outputNode();
	}

	fflush(outFile);

    vseval_freeScript(se);
    vseval_finalize();

	return error;
}
<commit_msg>Fix info output in vspipe<commit_after>/*
* Copyright (c) 2013 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <QtCore/QWaitCondition>
#include <QtCore/QFile>
#include <QtCore/QMap>
#include "VSScript.h"
#include "VSHelper.h"

#ifdef _WIN32
static inline QString nativeToQString(const wchar_t *str) {
	return QString::fromWCharArray(str);
}
#else
static inline QString nativeToQString(const char *str) {
	return QString::fromLocal8Bit(str);
}
#endif

const VSAPI *vsapi = NULL;
VSScript *se = NULL;
VSNodeRef *node = NULL;
FILE *outFile = NULL;

int requests = 0;
int index = 0;
int outputFrames = 0;
int requestedFrames = 0;
int completedFrames = 0;
int totalFrames = 0;
int numPlanes = 0;
bool y4m = false;
bool outputError = false;
bool showInfo = false;
QMap<int, const VSFrameRef *> reorderMap;

QString errorMessage;
QWaitCondition condition;
QMutex mutex;

void VS_CC frameDoneCallback(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg) {
    completedFrames++;

    if (f) {
        reorderMap.insert(n, f);
        while (reorderMap.contains(outputFrames)) {
            const VSFrameRef *frame = reorderMap.take(outputFrames);
            if (!outputError) {
				if (y4m) {
					if (!fwrite("FRAME\n", 6, 1, outFile)) {
						errorMessage = "Error: fwrite() call failed";
						totalFrames = requestedFrames;
						outputError = true;
					}
				}

				if (!outputError) {
					const VSFormat *fi = vsapi->getFrameFormat(frame);
					for (int p = 0; p < fi->numPlanes; p++) {
						int stride = vsapi->getStride(frame, p);
						const uint8_t *readPtr = vsapi->getReadPtr(frame, p);
						int rowSize = vsapi->getFrameWidth(frame, p) * fi->bytesPerSample;
						int height = vsapi->getFrameHeight(frame, p);
						for (int y = 0; y < height; y++) {
							if (!fwrite(readPtr, rowSize, 1, outFile)) {
								errorMessage = "Error: fwrite() call failed";
								totalFrames = requestedFrames;
								outputError = true;
								p = 100; // break out of the outer loop
								break;
							}
							readPtr += stride;
						}
					}
				}
			}
            vsapi->freeFrame(frame);
            outputFrames++;
        }
    } else {
        outputError = true;
        totalFrames = requestedFrames;
        if (errorMsg)
            errorMessage = QString("Error: Failed to retrieve frame ") + n + QString(" with error: ") + QString::fromUtf8(errorMsg);
        else
            errorMessage = QString("Error: Failed to retrieve frame ") + n;
    }

    if (requestedFrames < totalFrames) {
        vsapi->getFrameAsync(requestedFrames, node, frameDoneCallback, NULL);
        requestedFrames++;
    }

    if (totalFrames == completedFrames) {
        QMutexLocker lock(&mutex);
        condition.wakeOne();
    }
}

bool outputNode() {
    if (requests < 1) {
		const VSCoreInfo *info = vsapi->getCoreInfo(vseval_getCore());
        requests = info->numThreads;
	}

	const VSVideoInfo *vi = vsapi->getVideoInfo(node);
	totalFrames = vi->numFrames;

	if (y4m && (vi->format->colorFamily != cmGray && vi->format->colorFamily != cmYUV)) {
		errorMessage = "Error: Can only apply y4m headers to YUV and Gray format clips";
		fprintf(stderr, "%s", errorMessage.toUtf8().constData());
        return true;
	}

    QString y4mFormat;
    QString numBits;

    if (y4m) {
        if (vi->format->colorFamily == cmGray) {
            y4mFormat = "mono";
            if (vi->format->bitsPerSample > 8)
				y4mFormat = y4mFormat + QString::number(vi->format->bitsPerSample);
		} else if (vi->format->colorFamily == cmYUV) {
			if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 1)
                y4mFormat = "420";
			else if (vi->format->subSamplingW == 1 && vi->format->subSamplingH == 0)
                y4mFormat = "422";
			else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 0)
                y4mFormat = "444";
			else if (vi->format->subSamplingW == 2 && vi->format->subSamplingH == 2)
                y4mFormat = "410";
			else if (vi->format->subSamplingW == 2 && vi->format->subSamplingH == 0)
                y4mFormat = "411";
			else if (vi->format->subSamplingW == 0 && vi->format->subSamplingH == 1)
                y4mFormat = "440";
			else {
				fprintf(stderr, "No y4m identifier exists for current format");
				return true;
			}

            if (vi->format->bitsPerSample > 8)
                y4mFormat = y4mFormat + "p" + QString::number(vi->format->bitsPerSample);
		} else {
			fprintf(stderr, "No y4m identifier exists for current format");
			return true;
		}
    }
	if (!y4mFormat.isEmpty())
        y4mFormat = "C" + y4mFormat + " ";

	QString header = "YUV4MPEG2 " + y4mFormat + "W" + QString::number(vi->width) + " H" + QString::number(vi->height) + " F" + QString::number(vi->fpsNum)  + ":" + QString::number(vi->fpsDen)  + " Ip A0:0\n";
    QByteArray rawHeader = header.toUtf8();

    if (y4m) {
        if (!fwrite(rawHeader.constData(), rawHeader.size(), 1, outFile)) {
            errorMessage = "Error: fwrite() call failed";
			fprintf(stderr, "%s", errorMessage.toUtf8().constData());
			outputError = true;
			return outputError;
        }
    }

	QMutexLocker lock(&mutex);

	int intitalRequestSize = std::min(requests, totalFrames);
	requestedFrames = intitalRequestSize;
	for (int n = 0; n < intitalRequestSize; n++)
		vsapi->getFrameAsync(n, node, frameDoneCallback, NULL);

	condition.wait(&mutex);

    if (outputError) {
        fprintf(stderr, "%s", errorMessage.toUtf8().constData());
    }

    return outputError;
}

const char *colorFamilyToString(int colorFamily) {
	switch (colorFamily) {
	case cmGray: return "Gray";
	case cmRGB: return "RGB";
	case cmYUV: return "YUV";
	case cmYCoCg: return "YCoCg";
	case cmCompat: return "Compat";
	}
	return "";
}

// fixme, only allow info without output
#ifdef _WIN32
int wmain(int argc, wchar_t **argv) {
#else
int main(int argc, char **argv) {
#endif

    if (argc < 3) {
        fprintf(stderr, "VSPipe usage:\n");
		fprintf(stderr, "Show script info: vspipe script.vpy - -info\n");
        fprintf(stderr, "Write to stdout: vspipe script.vpy - [options]\n");
        fprintf(stderr, "Write to file: vspipe script.vpy <outFile> [options]\n");
        fprintf(stderr, "Available options:\n");
		fprintf(stderr, "Select output index: -index N\n");
		fprintf(stderr, "Set number of concurrent frame requests: -requests N\n");
		fprintf(stderr, "Add YUV4MPEG headers: -y4m\n");
		fprintf(stderr, "Show video info: -info (overrides other options)\n");
        return 1;
    }

	QFile scriptFile(nativeToQString(argv[1]));
	if (!scriptFile.open(QIODevice::ReadOnly)) {
        fprintf(stderr, "Failed to to open script file for reading\n");
        return 1;
	}

	if (scriptFile.size() > 1024*1024*16) {
        fprintf(stderr, "Script files bigger than 16MB not allowed\n");
        return 1;
	}

    QByteArray scriptData = scriptFile.readAll();
	scriptFile.close();
    if (scriptData.isEmpty()) {
        fprintf(stderr, "Failed to read script file or file is empty\n");
        return 1;
    }

	QString outputFilename = nativeToQString(argv[2]);
	if (outputFilename == "-") {
		outFile = stdout;
	} else {
#ifdef _WIN32
		outFile = _wfopen(outputFilename.toStdWString().c_str(), L"wb");
#else
		outfile = fopen(outputFilename.toLocal8Bit(), "wb");
#endif
		if (!outFile) {
			fprintf(stderr, "Failed to open output for writing\n");
			return 1;
		}
	}

	for (int arg = 3; arg < argc; arg++) {
		QString argString = nativeToQString(argv[arg]);
		if (argString == "-y4m") {
			y4m = true;
		} else if (argString == "-info") {
			showInfo = true;
		} else if (argString == "-index") {
			bool ok = false;
			if (argc <= arg + 1) {
				fprintf(stderr, "No index number specified");
				return 1;
			}
			QString numString = nativeToQString(argv[arg+1]);
			index = numString.toInt(&ok);
			if (!ok) {
				fprintf(stderr, "Couldn't convert %s to an integer", numString.toUtf8().constData());
				return 1;
			}
			arg++;
		} else if (argString == "-requests") {
			bool ok = false;
			if (argc <= arg + 1) {
				fprintf(stderr, "No request number specified");
				return 1;
			}
			QString numString = nativeToQString(argv[arg+1]);
			requests = numString.toInt(&ok);
			if (!ok) {
				fprintf(stderr, "Couldn't convert %s to an integer", numString.toUtf8().constData());
				return 1;
			}
			arg++;
		} else {
			fprintf(stderr, "Unknown argument: %s\n", argString.toUtf8().constData());
			return 1;
		}
	}

    if (!vseval_init()) {
        fprintf(stderr, "Failed to initialize VapourSynth environment\n");
        return 1;
    }

    vsapi = vseval_getVSApi();
    if (!vsapi) {
        fprintf(stderr, "Failed to get VapourSynth API pointer\n");
        vseval_finalize();
        return 1;
    }

	if (vseval_evaluateScript(&se, scriptData.constData(), nativeToQString(argv[1]).toUtf8())) {
        fprintf(stderr, "Script evaluation failed:\n%s", vseval_getError(se));
        vseval_freeScript(se);
        vseval_finalize();
        return 1;
    }

    node = vseval_getOutput(se, index);
    if (!node) {
       fprintf(stderr, "Failed to retrieve output node. Invalid index specified?\n");
       vseval_freeScript(se);
       vseval_finalize();
       return 1;
    }

	bool error = false;
	const VSVideoInfo *vi = vsapi->getVideoInfo(node);

	if (showInfo) {
		fprintf(outFile, "Width: %d\n", vi->width);
		fprintf(outFile, "Height: %d\n", vi->height);
		fprintf(outFile, "Frames: %d\n", vi->numFrames);
		fprintf(outFile, "FPS: %d/%d\n", vi->fpsNum, vi->fpsDen);
		if (vi->format) {
			fprintf(outFile, "Format Name: %s\n", vi->format->name);
			fprintf(outFile, "Color Family: %s\n", colorFamilyToString(vi->format->colorFamily));
			fprintf(outFile, "Bits: %d\n", vi->format->bitsPerSample);
			fprintf(outFile, "SubSampling W: %d\n", vi->format->subSamplingW);
			fprintf(outFile, "SubSampling H: %d\n", vi->format->subSamplingH);
		} else {
			fprintf(outFile, "Format Name: Variable\n");
		}
	} else {
		if (!isConstantFormat(vi) || vi->numFrames == 0) {
			fprintf(stderr, "Cannot output clips with varying dimensions or unknown length\n");
			vseval_freeScript(se);
			vseval_finalize();
			return 1;
		}

		error = outputNode();
	}

	fflush(outFile);

    vseval_freeScript(se);
    vseval_finalize();

	return error;
}
<|endoftext|>


<commit_before>#include "windowManager.h"

#ifdef WIN32
#include "dynamicLibrary.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#include "graphics/opengl.h"
#include "Updatable.h"
#include "Renderable.h"
#include "collisionable.h"
#include "postProcessManager.h"
#include "input.h"

#include <glm/gtc/type_ptr.hpp>
#include <cmath>
#include <SDL.h>

PVector<Window> Window::all_windows;
void* Window::gl_context = nullptr;

Window::Window(glm::vec2 virtual_size, bool fullscreen, RenderChain* render_chain, int fsaa)
: minimal_virtual_size(virtual_size), current_virtual_size(virtual_size), render_chain(render_chain), fullscreen(fullscreen), fsaa(fsaa)
{
    srand(static_cast<int32_t>(time(nullptr)));

#ifdef _WIN32
    //On Vista or newer windows, let the OS know we are DPI aware, so we won't have odd scaling issues.
    auto user32 = DynamicLibrary::open("USER32.DLL");
    if (user32)
    {
        auto SetProcessDPIAware = user32->getFunction<BOOL(WINAPI *)(void)>("SetProcessDPIAware");
        if (SetProcessDPIAware)
            SetProcessDPIAware();
    }
#endif

    create();
    sp::initOpenGL();

    all_windows.push_back(this);
}

Window::~Window()
{
}

void Window::render()
{
//#warning SDL2 TODO
/*
    if (InputHandler::keyboardIsPressed(sf::Keyboard::Return) && (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt) || sf::Keyboard::isKeyPressed(sf::Keyboard::RAlt)))
    {
        setFullscreen(!isFullscreen());
    }
*/
    SDL_GL_MakeCurrent(static_cast<SDL_Window*>(window), gl_context);

    int w, h;
    SDL_GL_GetDrawableSize(static_cast<SDL_Window*>(window), &w, &h);
    glViewport(0, 0, w, h);

    // Clear the window
    glClearColor(0.1f, 0.1f, 0.1f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //Call the first item of the rendering chain.
    sp::RenderTarget target{current_virtual_size, {w, h}};
    render_chain->render(target);
    target.finish();

    // Display things on screen
    SDL_GL_SwapWindow(static_cast<SDL_Window*>(window));
}

void Window::setFullscreen(bool new_fullscreen)
{
    if (fullscreen == new_fullscreen)
        return;
    fullscreen = new_fullscreen;
    create();
}
void Window::setFSAA(int new_fsaa)
{
    if (fsaa == new_fsaa)
        return;
    fsaa = new_fsaa;
    create();
}

void Window::setTitle(string title)
{
    SDL_SetWindowTitle(static_cast<SDL_Window*>(window), title.c_str());
}

glm::vec2 Window::mapPixelToCoords(const glm::ivec2 point) const
{
    int w, h;
    SDL_GetWindowSize(static_cast<SDL_Window*>(window), &w, &h);
    float x = float(point.x) / float(w) * float(current_virtual_size.x);
    float y = float(point.y) / float(h) * float(current_virtual_size.y);
    return glm::vec2(x, y);
}

glm::ivec2 Window::mapCoordsToPixel(const glm::vec2 point) const
{
    int w, h;
    SDL_GetWindowSize(static_cast<SDL_Window*>(window), &w, &h);
    float x = float(point.x) * float(w) / float(current_virtual_size.x);
    float y = float(point.y) * float(h) / float(current_virtual_size.y);
    return glm::ivec2(x, y);
}

void Window::create()
{
    if (window) return;

    // Create the window of the application
    auto windowWidth = static_cast<int>(minimal_virtual_size.x);
    auto windowHeight = static_cast<int>(minimal_virtual_size.y);

    SDL_Rect rect;
    SDL_GetDisplayBounds(0, &rect);
    if (fullscreen)
    {
        windowWidth = rect.w;
        windowHeight = rect.h;
    }else{
        int scale = 2;
        while(windowWidth * scale < int(rect.w) && windowHeight * scale < int(rect.h))
            scale += 1;
        windowWidth *= scale - 1;
        windowHeight *= scale - 1;

        while(windowWidth >= int(rect.w) || windowHeight >= int(rect.h) - 100)
        {
            windowWidth = static_cast<int>(std::floor(windowWidth * 0.9f));
            windowHeight = static_cast<int>(std::floor(windowHeight * 0.9f));
        }
    }

#if defined(ANDROID)
    constexpr auto context_profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
    constexpr auto context_profile_minor_version = 0;

#else
    constexpr auto context_profile_mask = SDL_GL_CONTEXT_PROFILE_CORE;
    constexpr auto context_profile_minor_version = 1;
#endif

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, context_profile_mask);
#if defined(DEBUG)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#endif
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, context_profile_minor_version);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    int flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
    if (fullscreen)
        flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
    window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowWidth, windowHeight, flags);
    if (!gl_context)
        gl_context = SDL_GL_CreateContext(static_cast<SDL_Window*>(window));
    if (SDL_GL_SetSwapInterval(-1))
        SDL_GL_SetSwapInterval(1);
    setupView();
}

void Window::handleEvent(const SDL_Event& event)
{
    switch(event.type)
    {
    case SDL_MOUSEBUTTONDOWN:
        {
            sp::io::Pointer::Button button = sp::io::Pointer::Button::Unknown;
            switch(event.button.button)
            {
            case SDL_BUTTON_LEFT: button = sp::io::Pointer::Button::Left; break;
            case SDL_BUTTON_MIDDLE: button = sp::io::Pointer::Button::Middle; break;
            case SDL_BUTTON_RIGHT: button = sp::io::Pointer::Button::Right; break;
            default: break;
            }
            mouse_button_down_mask |= 1 << int(event.button.button);
            render_chain->onPointerDown(button, mapPixelToCoords({event.button.x, event.button.y}), sp::io::Pointer::mouse);
        }
        break;
    case SDL_MOUSEMOTION:
        if (mouse_button_down_mask)
            render_chain->onPointerDrag(mapPixelToCoords({event.motion.x, event.motion.y}), sp::io::Pointer::mouse);
        else
            render_chain->onPointerMove(mapPixelToCoords({event.motion.x, event.motion.y}), sp::io::Pointer::mouse);
        break;
    case SDL_MOUSEBUTTONUP:
        mouse_button_down_mask &=~(1 << int(event.button.button));
        if (!mouse_button_down_mask)
        {
            render_chain->onPointerUp(mapPixelToCoords({event.button.x, event.button.y}), sp::io::Pointer::mouse);
            render_chain->onPointerMove(mapPixelToCoords({event.button.x, event.button.y}), sp::io::Pointer::mouse);
        }
        break;
    case SDL_FINGERDOWN:
        render_chain->onPointerDown(sp::io::Pointer::Button::Touch, {event.tfinger.x * current_virtual_size.x, event.tfinger.y * current_virtual_size.y}, event.tfinger.fingerId);
        break;
    case SDL_FINGERMOTION:
        render_chain->onPointerDrag({event.tfinger.x * current_virtual_size.x, event.tfinger.y * current_virtual_size.y}, event.tfinger.fingerId);
        break;
    case SDL_FINGERUP:
        render_chain->onPointerUp({event.tfinger.x * current_virtual_size.x, event.tfinger.y * current_virtual_size.y}, event.tfinger.fingerId);
        break;
    case SDL_TEXTINPUT:
        render_chain->onTextInput(event.text.text);
        break;
    case SDL_KEYDOWN:
        switch(event.key.keysym.sym)
        {
        case SDLK_KP_4:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_LEFT:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordLeftWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordLeft);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::LeftWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Left);
            break;
        case SDLK_KP_6:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_RIGHT:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordRightWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordRight);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::RightWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Right);
            break;
        case SDLK_KP_8:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_UP:
            if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::UpWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Up);
            break;
        case SDLK_KP_2:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_DOWN:
            if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::DownWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Down);
            break;
        case SDLK_KP_7:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_HOME:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextStartWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextStart);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::LineStartWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::LineStart);
            break;
        case SDLK_KP_1:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_END:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextEndWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextEnd);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::LineEndWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::LineEnd);
            break;
        case SDLK_KP_PERIOD:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_DELETE:
            render_chain->onTextInput(sp::TextInputEvent::Delete);
            break;
        case SDLK_BACKSPACE:
            render_chain->onTextInput(sp::TextInputEvent::Backspace);
            break;
        case SDLK_KP_ENTER:
        case SDLK_RETURN:
            render_chain->onTextInput(sp::TextInputEvent::Return);
            break;
        case SDLK_TAB:
        case SDLK_KP_TAB:
            if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::Unindent);
            else
                render_chain->onTextInput(sp::TextInputEvent::Indent);
            break;
        case SDLK_a:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::SelectAll);
            break;
        case SDLK_c:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::Copy);
            break;
        case SDLK_v:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::Paste);
            break;
        case SDLK_x:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::Cut);
            break;
        }
        break;
    case SDL_WINDOWEVENT:
        switch(event.window.event)
        {
        case SDL_WINDOWEVENT_LEAVE:
            if (!SDL_GetMouseState(nullptr, nullptr))
            {
                render_chain->onPointerLeave(-1);
            }
            break;
        case SDL_WINDOWEVENT_CLOSE:
            //close();
            break;
        case SDL_WINDOWEVENT_RESIZED:
            setupView();
            break;
        }
        break;
    case SDL_QUIT:
        //close();
        break;
    default:
        break;
    }
}

void Window::setupView()
{
    int w, h;
    SDL_GetWindowSize(static_cast<SDL_Window*>(window), &w, &h);
    glm::vec2 window_size{w, h};

    current_virtual_size = minimal_virtual_size;

    if (window_size.x / window_size.y > current_virtual_size.x / current_virtual_size.y)
    {
        current_virtual_size.x = current_virtual_size.y / window_size.y * window_size.x;
    }else{
        current_virtual_size.y = current_virtual_size.x / window_size.x * window_size.y;
    }
}
<commit_msg>Fix fullscreen toggle, and put each window on a different monitor if available.<commit_after>#include "windowManager.h"

#ifdef WIN32
#include "dynamicLibrary.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#include "graphics/opengl.h"
#include "Updatable.h"
#include "Renderable.h"
#include "collisionable.h"
#include "postProcessManager.h"
#include "input.h"

#include <glm/gtc/type_ptr.hpp>
#include <cmath>
#include <SDL.h>

PVector<Window> Window::all_windows;
void* Window::gl_context = nullptr;

Window::Window(glm::vec2 virtual_size, bool fullscreen, RenderChain* render_chain, int fsaa)
: minimal_virtual_size(virtual_size), current_virtual_size(virtual_size), render_chain(render_chain), fullscreen(fullscreen), fsaa(fsaa)
{
    srand(static_cast<int32_t>(time(nullptr)));

#ifdef _WIN32
    //On Vista or newer windows, let the OS know we are DPI aware, so we won't have odd scaling issues.
    auto user32 = DynamicLibrary::open("USER32.DLL");
    if (user32)
    {
        auto SetProcessDPIAware = user32->getFunction<BOOL(WINAPI *)(void)>("SetProcessDPIAware");
        if (SetProcessDPIAware)
            SetProcessDPIAware();
    }
#endif

    create();
    sp::initOpenGL();

    all_windows.push_back(this);
}

Window::~Window()
{
}

void Window::render()
{
//#warning SDL2 TODO
/*
    if (InputHandler::keyboardIsPressed(sf::Keyboard::Return) && (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt) || sf::Keyboard::isKeyPressed(sf::Keyboard::RAlt)))
    {
        setFullscreen(!isFullscreen());
    }
*/
    SDL_GL_MakeCurrent(static_cast<SDL_Window*>(window), gl_context);

    int w, h;
    SDL_GL_GetDrawableSize(static_cast<SDL_Window*>(window), &w, &h);
    glViewport(0, 0, w, h);

    // Clear the window
    glClearColor(0.1f, 0.1f, 0.1f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //Call the first item of the rendering chain.
    sp::RenderTarget target{current_virtual_size, {w, h}};
    render_chain->render(target);
    target.finish();

    // Display things on screen
    SDL_GL_SwapWindow(static_cast<SDL_Window*>(window));
}

void Window::setFullscreen(bool new_fullscreen)
{
    if (fullscreen == new_fullscreen)
        return;
    fullscreen = new_fullscreen;
    SDL_SetWindowFullscreen(static_cast<SDL_Window*>(window), fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
    setupView();
}

void Window::setFSAA(int new_fsaa)
{
    if (fsaa == new_fsaa)
        return;
    fsaa = new_fsaa;
    create();
}

void Window::setTitle(string title)
{
    SDL_SetWindowTitle(static_cast<SDL_Window*>(window), title.c_str());
}

glm::vec2 Window::mapPixelToCoords(const glm::ivec2 point) const
{
    int w, h;
    SDL_GetWindowSize(static_cast<SDL_Window*>(window), &w, &h);
    float x = float(point.x) / float(w) * float(current_virtual_size.x);
    float y = float(point.y) / float(h) * float(current_virtual_size.y);
    return glm::vec2(x, y);
}

glm::ivec2 Window::mapCoordsToPixel(const glm::vec2 point) const
{
    int w, h;
    SDL_GetWindowSize(static_cast<SDL_Window*>(window), &w, &h);
    float x = float(point.x) * float(w) / float(current_virtual_size.x);
    float y = float(point.y) * float(h) / float(current_virtual_size.y);
    return glm::ivec2(x, y);
}

void Window::create()
{
    if (window) return;

    int display_nr = 0;
    for(auto w : all_windows)
    {
        if (w == this)
            break;
        display_nr ++;
    }

    // Create the window of the application
    auto windowWidth = static_cast<int>(minimal_virtual_size.x);
    auto windowHeight = static_cast<int>(minimal_virtual_size.y);

    SDL_Rect rect;
    if (SDL_GetDisplayBounds(display_nr, &rect))
    {
        display_nr = 0;
        SDL_GetDisplayBounds(display_nr, &rect);
    }

    int scale = 2;
    while(windowWidth * scale < int(rect.w) && windowHeight * scale < int(rect.h))
        scale += 1;
    windowWidth *= scale - 1;
    windowHeight *= scale - 1;

    while(windowWidth >= int(rect.w) || windowHeight >= int(rect.h) - 100)
    {
        windowWidth = static_cast<int>(std::floor(windowWidth * 0.9f));
        windowHeight = static_cast<int>(std::floor(windowHeight * 0.9f));
    }

#if defined(ANDROID)
    constexpr auto context_profile_mask = SDL_GL_CONTEXT_PROFILE_ES;
    constexpr auto context_profile_minor_version = 0;

#else
    constexpr auto context_profile_mask = SDL_GL_CONTEXT_PROFILE_CORE;
    constexpr auto context_profile_minor_version = 1;
#endif

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, context_profile_mask);
#if defined(DEBUG)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#endif
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, context_profile_minor_version);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    int flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
    if (fullscreen)
        flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
    window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr), SDL_WINDOWPOS_CENTERED_DISPLAY(display_nr), windowWidth, windowHeight, flags);
    if (!gl_context)
        gl_context = SDL_GL_CreateContext(static_cast<SDL_Window*>(window));
    if (SDL_GL_SetSwapInterval(-1))
        SDL_GL_SetSwapInterval(1);
    setupView();
}

void Window::handleEvent(const SDL_Event& event)
{
    switch(event.type)
    {
    case SDL_MOUSEBUTTONDOWN:
        {
            sp::io::Pointer::Button button = sp::io::Pointer::Button::Unknown;
            switch(event.button.button)
            {
            case SDL_BUTTON_LEFT: button = sp::io::Pointer::Button::Left; break;
            case SDL_BUTTON_MIDDLE: button = sp::io::Pointer::Button::Middle; break;
            case SDL_BUTTON_RIGHT: button = sp::io::Pointer::Button::Right; break;
            default: break;
            }
            mouse_button_down_mask |= 1 << int(event.button.button);
            render_chain->onPointerDown(button, mapPixelToCoords({event.button.x, event.button.y}), sp::io::Pointer::mouse);
        }
        break;
    case SDL_MOUSEMOTION:
        if (mouse_button_down_mask)
            render_chain->onPointerDrag(mapPixelToCoords({event.motion.x, event.motion.y}), sp::io::Pointer::mouse);
        else
            render_chain->onPointerMove(mapPixelToCoords({event.motion.x, event.motion.y}), sp::io::Pointer::mouse);
        break;
    case SDL_MOUSEBUTTONUP:
        mouse_button_down_mask &=~(1 << int(event.button.button));
        if (!mouse_button_down_mask)
        {
            render_chain->onPointerUp(mapPixelToCoords({event.button.x, event.button.y}), sp::io::Pointer::mouse);
            render_chain->onPointerMove(mapPixelToCoords({event.button.x, event.button.y}), sp::io::Pointer::mouse);
        }
        break;
    case SDL_FINGERDOWN:
        render_chain->onPointerDown(sp::io::Pointer::Button::Touch, {event.tfinger.x * current_virtual_size.x, event.tfinger.y * current_virtual_size.y}, event.tfinger.fingerId);
        break;
    case SDL_FINGERMOTION:
        render_chain->onPointerDrag({event.tfinger.x * current_virtual_size.x, event.tfinger.y * current_virtual_size.y}, event.tfinger.fingerId);
        break;
    case SDL_FINGERUP:
        render_chain->onPointerUp({event.tfinger.x * current_virtual_size.x, event.tfinger.y * current_virtual_size.y}, event.tfinger.fingerId);
        break;
    case SDL_TEXTINPUT:
        render_chain->onTextInput(event.text.text);
        break;
    case SDL_KEYDOWN:
        switch(event.key.keysym.sym)
        {
        case SDLK_KP_4:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_LEFT:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordLeftWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordLeft);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::LeftWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Left);
            break;
        case SDLK_KP_6:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_RIGHT:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordRightWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::WordRight);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::RightWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Right);
            break;
        case SDLK_KP_8:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_UP:
            if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::UpWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Up);
            break;
        case SDLK_KP_2:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_DOWN:
            if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::DownWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::Down);
            break;
        case SDLK_KP_7:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_HOME:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextStartWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextStart);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::LineStartWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::LineStart);
            break;
        case SDLK_KP_1:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_END:
            if (event.key.keysym.mod & KMOD_SHIFT && event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextEndWithSelection);
            else if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::TextEnd);
            else if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::LineEndWithSelection);
            else
                render_chain->onTextInput(sp::TextInputEvent::LineEnd);
            break;
        case SDLK_KP_PERIOD:
            if (event.key.keysym.mod & KMOD_NUM)
                break;
            //fallthrough
        case SDLK_DELETE:
            render_chain->onTextInput(sp::TextInputEvent::Delete);
            break;
        case SDLK_BACKSPACE:
            render_chain->onTextInput(sp::TextInputEvent::Backspace);
            break;
        case SDLK_KP_ENTER:
        case SDLK_RETURN:
            render_chain->onTextInput(sp::TextInputEvent::Return);
            break;
        case SDLK_TAB:
        case SDLK_KP_TAB:
            if (event.key.keysym.mod & KMOD_SHIFT)
                render_chain->onTextInput(sp::TextInputEvent::Unindent);
            else
                render_chain->onTextInput(sp::TextInputEvent::Indent);
            break;
        case SDLK_a:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::SelectAll);
            break;
        case SDLK_c:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::Copy);
            break;
        case SDLK_v:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::Paste);
            break;
        case SDLK_x:
            if (event.key.keysym.mod & KMOD_CTRL)
                render_chain->onTextInput(sp::TextInputEvent::Cut);
            break;
        }
        break;
    case SDL_WINDOWEVENT:
        switch(event.window.event)
        {
        case SDL_WINDOWEVENT_LEAVE:
            if (!SDL_GetMouseState(nullptr, nullptr))
            {
                render_chain->onPointerLeave(-1);
            }
            break;
        case SDL_WINDOWEVENT_CLOSE:
            //close();
            break;
        case SDL_WINDOWEVENT_RESIZED:
            setupView();
            break;
        }
        break;
    case SDL_QUIT:
        //close();
        break;
    default:
        break;
    }
}

void Window::setupView()
{
    int w, h;
    SDL_GetWindowSize(static_cast<SDL_Window*>(window), &w, &h);
    glm::vec2 window_size{w, h};

    current_virtual_size = minimal_virtual_size;

    if (window_size.x / window_size.y > current_virtual_size.x / current_virtual_size.y)
    {
        current_virtual_size.x = current_virtual_size.y / window_size.y * window_size.x;
    }else{
        current_virtual_size.y = current_virtual_size.x / window_size.x * window_size.y;
    }
}
<|endoftext|>