<commit_before>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015  Vladimir Menshakov

    Android File Transfer For Linux is free software: you can redistribute
    it and/or modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    Android File Transfer For Linux 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 Android File Transfer For Linux.
    If not, see <http://www.gnu.org/licenses/>.
 */

#include <fuse_lowlevel.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#include <mtp/ptp/Device.h>
#include <mtp/ptp/ByteArrayObjectStream.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <mtp/ptp/ObjectPropertyListParser.h>
#include <mtp/log.h>

#include <map>
#include <string>

namespace
{
	class Exception : public std::runtime_error
	{
	public:
		Exception(const std::string &what) throw() : std::runtime_error(what + ": " + GetErrorMessage(errno)) { }
		Exception(const std::string &what, int returnCode) throw() : std::runtime_error(what + ": " + GetErrorMessage(returnCode)) { }
		static std::string GetErrorMessage(int returnCode)
		{
			char buf[1024];
			std::string text(strerror_r(returnCode, buf, sizeof(buf)));
			return text;
		}
	};

#define FUSE_CALL(...) do { int _r = __VA_ARGS__ ; if (_r < 0) throw Exception(#__VA_ARGS__ " failed", -_r); } while(false)

	struct FuseEntry : fuse_entry_param
	{
		static constexpr const double Timeout = 60.0;

		fuse_req_t Request;

		FuseEntry(fuse_req_t req): fuse_entry_param(), Request(req)
		{
			generation = 1;
			attr_timeout = entry_timeout = Timeout;
		};

		void SetFileMode()
		{ attr.st_mode = S_IFREG | 0444; }

		void SetDirectoryMode()
		{ attr.st_mode = S_IFDIR | 0755; }

		void SetFormat(mtp::ObjectFormat format)
		{
			switch(format)
			{
			case mtp::ObjectFormat::Association:
				SetDirectoryMode();
				break;
			default:
				SetFileMode();
			}
		}

		void Reply()
		{
			FUSE_CALL(fuse_reply_entry(Request, this));
		}

		void ReplyAttr()
		{
			FUSE_CALL(fuse_reply_attr(Request, &attr, Timeout));
		}

		void ReplyError(int err)
		{
			FUSE_CALL(fuse_reply_err(Request, err));
		}
	};

	struct FuseDirectory
	{
		fuse_req_t			Request;
		std::vector<char>	Data;

		FuseDirectory(fuse_req_t request): Request(request) { Data.reserve(4096); }

		void Add(mtp::u32 id, const std::string &name)
		{
			size_t size = fuse_add_direntry(Request, NULL, 0, name.c_str(), NULL, 0);
			size_t offset = Data.size();
			Data.resize(Data.size() + size);
			struct stat entry = { };
			entry.st_ino = id;
			fuse_add_direntry(Request, Data.data() + offset, size, name.c_str(), &entry, Data.size());
		}

		void Reply(off_t off, size_t size)
		{
			if ((size_t)off < size)
				fuse_reply_buf(Request, Data.data() + off, std::min<size_t>(size, Data.size() - off));
			else
				fuse_reply_buf(Request, NULL, 0);
		}
	};

	class FuseWrapper
	{
		std::mutex		_mutex;
		mtp::DevicePtr	_device;
		mtp::SessionPtr	_session;
		bool			_editObjectSupported;

		typedef std::map<std::string, mtp::u32> ChildrenObjects;
		typedef std::map<mtp::u32, ChildrenObjects> Files;
		Files			_files;

		typedef std::map<mtp::u32, mtp::ObjectFormat> ObjectTypes;
		ObjectTypes		_objectFormats;

		typedef mtp::Session::ObjectEditSessionPtr ObjectEditSessionPtr;
		typedef std::map<mtp::u32, ObjectEditSessionPtr> OpenedFiles;
		OpenedFiles		_openedFiles;

		std::map<mtp::u32, std::string>		_storages;
		std::map<std::string, mtp::u32>		_storagesByName;

	public:
		FuseWrapper()
		{ Connect(); }

		void Connect()
		{
			mtp::scoped_mutex_lock l(_mutex);

			_openedFiles.clear();
			_files.clear();
			_storages.clear();
			_storagesByName.clear();
			_session.reset();
			_device.reset();
			_device = mtp::Device::Find();
			if (!_device)
				throw std::runtime_error("no MTP device found");

			_session = _device->OpenSession(1);
			_editObjectSupported = _session->EditObjectSupported();
			if (!_editObjectSupported)
				fprintf(stderr, "your device does not have android EditObject extension, mounting read-only\n");

			mtp::msg::StorageIDs ids = _session->GetStorageIDs();
			for(size_t i = 0; i < ids.StorageIDs.size(); ++i)
			{
				mtp::u32 id = ids.StorageIDs[i];
				mtp::msg::StorageInfo si = _session->GetStorageInfo(id);
				std::string path = (!si.StorageDescription.empty()? si.StorageDescription:  si.VolumeLabel);
				if (path.empty())
				{
					char buf[64];
					snprintf(buf, sizeof(buf), "sdcard%u", (unsigned)i);
					path = buf;
				}
				_storages[id] = path;
				_storagesByName[path] = id;
			}
		}

		void Init(void *, fuse_conn_info *conn)
		{
			conn->want |= conn->capable & FUSE_CAP_BIG_WRITES; //big writes
			static const size_t MaxWriteSize = 1024 * 1024;
			if (conn->max_write < MaxWriteSize)
				conn->max_write = MaxWriteSize;
			conn->max_readahead = 0;
		}

		mtp::ObjectFormat GetObjectFormat(mtp::u32 id)
		{
			auto i = _objectFormats.find(id);
			if (i != _objectFormats.end())
				return i->second;
			mtp::ObjectFormat format = static_cast<mtp::ObjectFormat>(_session->GetObjectIntegerProperty(id, mtp::ObjectProperty::ObjectFormat));
			_objectFormats.insert(std::make_pair(id, format));
			return format;
		}

		ChildrenObjects & GetChildren(mtp::u32 parent)
		{
			{
				auto i = _files.find(parent);
				if (i != _files.end())
					return i->second;
			}

			ChildrenObjects & cache = _files[parent];
			using namespace mtp;

			msg::ObjectHandles oh;

			auto sit = _storages.find(parent);
			if (sit != _storages.end())
				oh = _session->GetObjectHandles(sit->first, mtp::ObjectFormat::Any, mtp::Session::Root);
			else
			{
				if (_session->GetObjectPropertyListSupported())
				{
					ByteArray data;
					{
						data = _session->GetObjectPropertyList(parent, ObjectFormat::Any, ObjectProperty::ObjectFilename, 0, 1);
						ObjectPropertyListParser<std::string> parser;
						parser.Parse(data, [&cache](u32 objectId, const std::string &name)
						{
							cache[name] = objectId;
						});
					}
					{
						data = _session->GetObjectPropertyList(parent, ObjectFormat::Any, ObjectProperty::ObjectFormat, 0, 1);
						ObjectPropertyListParser<u16> parser;
						parser.Parse(data, [this](u32 objectId, u16 format)
						{
							_objectFormats[objectId] = static_cast<ObjectFormat>(format);
						});
					}
					return cache;
				}
				oh = _session->GetObjectHandles(mtp::Session::AllStorages, mtp::ObjectFormat::Any, parent);
			}

			for(auto id : oh.ObjectHandles)
			{
				try
				{
					std::string name = _session->GetObjectStringProperty(id, mtp::ObjectProperty::ObjectFilename);
					_objectFormats[id] = (ObjectFormat)_session->GetObjectIntegerProperty(id, ObjectProperty::ObjectFormat);
					cache[name] = id;
				} catch(const std::exception &ex)
				{ }
			}
			return cache;
		}

		void Lookup (fuse_req_t req, fuse_ino_t parent, const char *name)
		{
			mtp::print("LOOKUP ", name, " IN ", parent);
			FuseEntry entry(req);
			if (parent != 1)
			{
				const ChildrenObjects & children = GetChildren(parent);
				auto it = children.find(name);
				if (it != children.end())
				{
					entry.ino = it->second;
					entry.SetFormat(GetObjectFormat(it->second));
					entry.Reply();
					return;
				}
			}
			else
			{
				auto sit = _storagesByName.find(name);
				if (sit != _storagesByName.end())
				{
					entry.ino = sit->second;
					entry.SetDirectoryMode();
					entry.Reply();
					return;
				}
			}
			entry.ReplyError(ENOENT);
		}

		void ReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
		{
			FuseDirectory dir(req);
			if (ino == 1)
			{
				dir.Add(1, ".");
				dir.Add(1, "..");
				for(auto it : _storages)
				{
					dir.Add(it.first, it.second);
				}
				dir.Reply(off, size);
				return;
			}
			else
			{
				const ChildrenObjects & cache = GetChildren(ino);
				dir.Add(ino, ".");
				dir.Add(1, "..");
				for(auto it : cache)
				{
					dir.Add(it.second, it.first);
				}
				dir.Reply(off, size);
				return;
			}
			fuse_reply_err(req, ENOTDIR);
		}

		void GetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
		{
			FuseEntry entry(req);
			entry.ino = ino;
			if (ino == 1)
			{
				entry.SetDirectoryMode();
				entry.ReplyAttr();
				return;
			}
			else
			{
				auto it = _storages.find((mtp::u32)ino);
				if (it != _storages.end())
				{
					entry.SetDirectoryMode();
					entry.ReplyAttr();
					return;
				}

				try {
					entry.SetFormat(GetObjectFormat(ino));
					entry.ReplyAttr();
					return;
				}
				catch(const std::exception &ex)
				{ }
			}
			entry.ReplyError(ENOENT);
		}
	};

	std::unique_ptr<FuseWrapper>	g_wrapper;

#define WRAP_EX(...) do { \
		try { return __VA_ARGS__ ; } \
		catch (const mtp::usb::DeviceNotFoundException &) \
		{ \
			g_wrapper->Connect(); \
			__VA_ARGS__ ; \
		} \
		catch (const std::exception &ex) \
		{ fprintf(stderr, #__VA_ARGS__ " failed: %s\n", ex.what()); fuse_reply_err(req, -EIO); } \
	} while(false)

	void Init (void *userdata, struct fuse_conn_info *conn)
	{ try { g_wrapper->Init(userdata, conn); } catch (const std::exception &ex) { mtp::error("init failed:", ex.what()); } }

	void Lookup (fuse_req_t req, fuse_ino_t parent, const char *name)
	{ WRAP_EX(g_wrapper->Lookup(req, parent, name)); }

	void ReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
	{ WRAP_EX(g_wrapper->ReadDir(req, ino, size, off, fi)); }

	void GetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
	{ WRAP_EX(g_wrapper->GetAttr(req, ino, fi)); }

}

int main(int argc, char **argv)
{
	try
	{ g_wrapper.reset(new FuseWrapper()); }
	catch(const std::exception &ex)
	{ fprintf(stderr, "%s\n", ex.what()); return 1; }

	struct fuse_lowlevel_ops ops = {};

	ops.init		= &Init;
	ops.lookup		= &Lookup;
	ops.readdir		= &ReadDir;
	ops.getattr		= &GetAttr;
//	ops.open		= &Open;
//	ops.create		= &Create;
//	ops.read		= &Read;
//	ops.write		= &Write;
//	ops.flush		= &Flush;
//	ops.mkdir		= &MakeDir;
//	ops.rmdir		= &RemoveDir;
//	ops.unlink		= &Unlink;
//	ops.truncate	= &Truncate;
//	ops.statfs		= &StatFS;
//	ops.chmod		= &ChangeMode;

	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
	struct fuse_chan *ch;
	char *mountpoint;
	int err = -1;

	if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
	    (ch = fuse_mount(mountpoint, &args)) != NULL) {
		struct fuse_session *se;

		se = fuse_lowlevel_new(&args, &ops,
				       sizeof(ops), NULL);
		if (se != NULL) {
			if (fuse_set_signal_handlers(se) != -1) {
				fuse_session_add_chan(se, ch);
				err = fuse_session_loop(se);
				fuse_remove_signal_handlers(se);
				fuse_session_remove_chan(ch);
			}
			fuse_session_destroy(se);
		}
		fuse_unmount(mountpoint, ch);
	}
	fuse_opt_free_args(&args);

	return err ? 1 : 0;
}
<commit_msg>added read support<commit_after>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015  Vladimir Menshakov

    Android File Transfer For Linux is free software: you can redistribute
    it and/or modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    Android File Transfer For Linux 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 Android File Transfer For Linux.
    If not, see <http://www.gnu.org/licenses/>.
 */

#include <fuse_lowlevel.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#include <mtp/ptp/Device.h>
#include <mtp/ptp/ByteArrayObjectStream.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <mtp/ptp/ObjectPropertyListParser.h>
#include <mtp/log.h>

#include <map>
#include <string>

namespace
{
	class Exception : public std::runtime_error
	{
	public:
		Exception(const std::string &what) throw() : std::runtime_error(what + ": " + GetErrorMessage(errno)) { }
		Exception(const std::string &what, int returnCode) throw() : std::runtime_error(what + ": " + GetErrorMessage(returnCode)) { }
		static std::string GetErrorMessage(int returnCode)
		{
			char buf[1024];
			std::string text(strerror_r(returnCode, buf, sizeof(buf)));
			return text;
		}
	};

#define FUSE_CALL(...) do { int _r = __VA_ARGS__ ; if (_r < 0) throw Exception(#__VA_ARGS__ " failed", -_r); } while(false)

	struct FuseEntry : fuse_entry_param
	{
		static constexpr const double Timeout = 60.0;

		fuse_req_t Request;

		FuseEntry(fuse_req_t req): fuse_entry_param(), Request(req)
		{
			generation = 1;
			attr_timeout = entry_timeout = Timeout;
		};

		void SetFileMode()
		{ attr.st_mode = S_IFREG | 0444; }

		void SetDirectoryMode()
		{ attr.st_mode = S_IFDIR | 0755; }

		void SetFormat(mtp::ObjectFormat format)
		{
			switch(format)
			{
			case mtp::ObjectFormat::Association:
				SetDirectoryMode();
				break;
			default:
				SetFileMode();
			}
		}

		void Reply()
		{
			FUSE_CALL(fuse_reply_entry(Request, this));
		}

		void ReplyAttr()
		{
			FUSE_CALL(fuse_reply_attr(Request, &attr, Timeout));
		}

		void ReplyError(int err)
		{
			FUSE_CALL(fuse_reply_err(Request, err));
		}
	};

	struct FuseDirectory
	{
		fuse_req_t			Request;
		std::vector<char>	Data;

		FuseDirectory(fuse_req_t request): Request(request) { Data.reserve(4096); }

		void Add(mtp::u32 id, const std::string &name)
		{
			size_t size = fuse_add_direntry(Request, NULL, 0, name.c_str(), NULL, 0);
			size_t offset = Data.size();
			Data.resize(Data.size() + size);
			struct stat entry = { };
			entry.st_ino = id;
			fuse_add_direntry(Request, Data.data() + offset, size, name.c_str(), &entry, Data.size());
		}

		void Reply(off_t off, size_t size)
		{
			if ((size_t)off < size)
				fuse_reply_buf(Request, Data.data() + off, std::min<size_t>(size, Data.size() - off));
			else
				fuse_reply_buf(Request, NULL, 0);
		}
	};

	class FuseWrapper
	{
		std::mutex		_mutex;
		mtp::DevicePtr	_device;
		mtp::SessionPtr	_session;
		bool			_editObjectSupported;

		typedef std::map<std::string, mtp::u32> ChildrenObjects;
		typedef std::map<mtp::u32, ChildrenObjects> Files;
		Files			_files;

		typedef std::map<mtp::u32, mtp::ObjectFormat> ObjectTypes;
		ObjectTypes		_objectFormats;

		typedef mtp::Session::ObjectEditSessionPtr ObjectEditSessionPtr;
		typedef std::map<mtp::u32, ObjectEditSessionPtr> OpenedFiles;
		OpenedFiles		_openedFiles;

		std::map<mtp::u32, std::string>		_storages;
		std::map<std::string, mtp::u32>		_storagesByName;

	public:
		FuseWrapper()
		{ Connect(); }

		void Connect()
		{
			mtp::scoped_mutex_lock l(_mutex);

			_openedFiles.clear();
			_files.clear();
			_storages.clear();
			_storagesByName.clear();
			_session.reset();
			_device.reset();
			_device = mtp::Device::Find();
			if (!_device)
				throw std::runtime_error("no MTP device found");

			_session = _device->OpenSession(1);
			_editObjectSupported = _session->EditObjectSupported();
			if (!_editObjectSupported)
				fprintf(stderr, "your device does not have android EditObject extension, mounting read-only\n");

			mtp::msg::StorageIDs ids = _session->GetStorageIDs();
			for(size_t i = 0; i < ids.StorageIDs.size(); ++i)
			{
				mtp::u32 id = ids.StorageIDs[i];
				mtp::msg::StorageInfo si = _session->GetStorageInfo(id);
				std::string path = (!si.StorageDescription.empty()? si.StorageDescription:  si.VolumeLabel);
				if (path.empty())
				{
					char buf[64];
					snprintf(buf, sizeof(buf), "sdcard%u", (unsigned)i);
					path = buf;
				}
				_storages[id] = path;
				_storagesByName[path] = id;
			}
		}

		void Init(void *, fuse_conn_info *conn)
		{
			conn->want |= conn->capable & FUSE_CAP_BIG_WRITES; //big writes
			static const size_t MaxWriteSize = 1024 * 1024;
			if (conn->max_write < MaxWriteSize)
				conn->max_write = MaxWriteSize;
			conn->max_readahead = 0;
		}

		mtp::ObjectFormat GetObjectFormat(mtp::u32 id)
		{
			auto i = _objectFormats.find(id);
			if (i != _objectFormats.end())
				return i->second;
			mtp::ObjectFormat format = static_cast<mtp::ObjectFormat>(_session->GetObjectIntegerProperty(id, mtp::ObjectProperty::ObjectFormat));
			_objectFormats.insert(std::make_pair(id, format));
			return format;
		}

		ChildrenObjects & GetChildren(mtp::u32 parent)
		{
			{
				auto i = _files.find(parent);
				if (i != _files.end())
					return i->second;
			}

			ChildrenObjects & cache = _files[parent];
			using namespace mtp;

			msg::ObjectHandles oh;

			auto sit = _storages.find(parent);
			if (sit != _storages.end())
				oh = _session->GetObjectHandles(sit->first, mtp::ObjectFormat::Any, mtp::Session::Root);
			else
			{
				if (_session->GetObjectPropertyListSupported())
				{
					ByteArray data;
					{
						data = _session->GetObjectPropertyList(parent, ObjectFormat::Any, ObjectProperty::ObjectFilename, 0, 1);
						ObjectPropertyListParser<std::string> parser;
						parser.Parse(data, [&cache](u32 objectId, const std::string &name)
						{
							cache[name] = objectId;
						});
					}
					{
						data = _session->GetObjectPropertyList(parent, ObjectFormat::Any, ObjectProperty::ObjectFormat, 0, 1);
						ObjectPropertyListParser<u16> parser;
						parser.Parse(data, [this](u32 objectId, u16 format)
						{
							_objectFormats[objectId] = static_cast<ObjectFormat>(format);
						});
					}
					return cache;
				}
				oh = _session->GetObjectHandles(mtp::Session::AllStorages, mtp::ObjectFormat::Any, parent);
			}

			for(auto id : oh.ObjectHandles)
			{
				try
				{
					std::string name = _session->GetObjectStringProperty(id, mtp::ObjectProperty::ObjectFilename);
					_objectFormats[id] = (ObjectFormat)_session->GetObjectIntegerProperty(id, ObjectProperty::ObjectFormat);
					cache[name] = id;
				} catch(const std::exception &ex)
				{ }
			}
			return cache;
		}

		void Lookup (fuse_req_t req, fuse_ino_t parent, const char *name)
		{
			mtp::print("LOOKUP ", name, " IN ", parent);
			FuseEntry entry(req);
			if (parent != 1)
			{
				const ChildrenObjects & children = GetChildren(parent);
				auto it = children.find(name);
				if (it != children.end())
				{
					entry.ino = it->second;
					entry.SetFormat(GetObjectFormat(it->second));
					entry.Reply();
					return;
				}
			}
			else
			{
				auto sit = _storagesByName.find(name);
				if (sit != _storagesByName.end())
				{
					entry.ino = sit->second;
					entry.SetDirectoryMode();
					entry.Reply();
					return;
				}
			}
			entry.ReplyError(ENOENT);
		}

		void ReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
		{
			FuseDirectory dir(req);
			if (ino == 1)
			{
				dir.Add(1, ".");
				dir.Add(1, "..");
				for(auto it : _storages)
				{
					dir.Add(it.first, it.second);
				}
				dir.Reply(off, size);
				return;
			}
			else
			{
				const ChildrenObjects & cache = GetChildren(ino);
				dir.Add(ino, ".");
				dir.Add(1, "..");
				for(auto it : cache)
				{
					dir.Add(it.second, it.first);
				}
				dir.Reply(off, size);
				return;
			}
			fuse_reply_err(req, ENOTDIR);
		}

		void Read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
		{
			mtp::print("READ ", off, " ", size);
			mtp::ByteArray data = _session->GetPartialObject(ino, off, size);
			mtp::HexDump("data", data, true);
			FUSE_CALL(fuse_reply_buf(req, static_cast<char *>(static_cast<void *>(data.data())), data.size()));
		}

		void GetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
		{
			FuseEntry entry(req);
			entry.ino = ino;
			if (ino == 1)
			{
				entry.SetDirectoryMode();
				entry.ReplyAttr();
				return;
			}
			else
			{
				auto it = _storages.find((mtp::u32)ino);
				if (it != _storages.end())
				{
					entry.SetDirectoryMode();
					entry.ReplyAttr();
					return;
				}

				try {
					entry.SetFormat(GetObjectFormat(ino));
					entry.attr.st_size = _session->GetObjectIntegerProperty(ino, mtp::ObjectProperty::ObjectSize);
					entry.ReplyAttr();
					return;
				}
				catch(const std::exception &ex)
				{ }
			}
			entry.ReplyError(ENOENT);
		}
	};

	std::unique_ptr<FuseWrapper>	g_wrapper;

#define WRAP_EX(...) do { \
		try { return __VA_ARGS__ ; } \
		catch (const mtp::usb::DeviceNotFoundException &) \
		{ \
			g_wrapper->Connect(); \
			__VA_ARGS__ ; \
		} \
		catch (const std::exception &ex) \
		{ fprintf(stderr, #__VA_ARGS__ " failed: %s\n", ex.what()); fuse_reply_err(req, -EIO); } \
	} while(false)

	void Init (void *userdata, struct fuse_conn_info *conn)
	{ try { g_wrapper->Init(userdata, conn); } catch (const std::exception &ex) { mtp::error("init failed:", ex.what()); } }

	void Lookup (fuse_req_t req, fuse_ino_t parent, const char *name)
	{ WRAP_EX(g_wrapper->Lookup(req, parent, name)); }

	void ReadDir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
	{ WRAP_EX(g_wrapper->ReadDir(req, ino, size, off, fi)); }

	void GetAttr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
	{ WRAP_EX(g_wrapper->GetAttr(req, ino, fi)); }

	void Read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
	{ WRAP_EX(g_wrapper->Read(req, ino, size, off, fi)); }
}

int main(int argc, char **argv)
{
	try
	{ g_wrapper.reset(new FuseWrapper()); }
	catch(const std::exception &ex)
	{ fprintf(stderr, "%s\n", ex.what()); return 1; }

	struct fuse_lowlevel_ops ops = {};

	ops.init		= &Init;
	ops.lookup		= &Lookup;
	ops.readdir		= &ReadDir;
	ops.getattr		= &GetAttr;
//	ops.open		= &Open;
//	ops.create		= &Create;
	ops.read		= &Read;
//	ops.write		= &Write;
//	ops.mkdir		= &MakeDir;
//	ops.rmdir		= &RemoveDir;
//	ops.unlink		= &Unlink;
//	ops.truncate	= &Truncate;
//	ops.statfs		= &StatFS;
//	ops.chmod		= &ChangeMode;

	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
	struct fuse_chan *ch;
	char *mountpoint;
	int err = -1;

	if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
	    (ch = fuse_mount(mountpoint, &args)) != NULL) {
		struct fuse_session *se;

		se = fuse_lowlevel_new(&args, &ops,
				       sizeof(ops), NULL);
		if (se != NULL) {
			if (fuse_set_signal_handlers(se) != -1) {
				fuse_session_add_chan(se, ch);
				err = fuse_session_loop(se);
				fuse_remove_signal_handlers(se);
				fuse_session_remove_chan(ch);
			}
			fuse_session_destroy(se);
		}
		fuse_unmount(mountpoint, ch);
	}
	fuse_opt_free_args(&args);

	return err ? 1 : 0;
}
<|endoftext|>


<commit_before><commit_msg>Fix leak: explicitly delete seed clones not cached in local buffer<commit_after><|endoftext|>


<commit_before>#if defined(LINUX)
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <errno.h>
typedef uint64_t u64;
#include "include/wq.hh"
#include "user/dirit.hh"
#include "include/percpu.hh"
#define ST_SIZE(st)  (st).st_size
#define ST_ISDIR(st) S_ISDIR((st).st_mode)
#define BSIZ 256

#else // assume xv6

#include "types.h"
#include "stat.h"
#include "user.h"
#include "lib.h"
#include "fs.h"
#include "uspinlock.h"
#include "wq.hh"
#include "dirit.hh"
#include "percpu.hh"
#define ST_SIZE(st)  (st).size
#define ST_ISDIR(st) ((st).type == T_DIR)
#define stderr 2
#define BSIZ (DIRSIZ+1)
#endif

template <typename T>
struct reducer_opadd
{
  reducer_opadd(T v) {
    for (int i = 0; i < NCPU; i++)
      v_[i] = 0;
    *v_ = v;
  }

  T operator+=(T i) {
    (*v_) += i;
    return *v_;
  }

  T get_value() {
    T t = 0;
    for (int i = 0; i < NCPU; i++)
      t += v_[i];
    return t;
  }

  percpu<T> v_;
};

static size_t
du(int fd)
{
  struct stat st;
  if (fstat(fd, &st) < 0) {
    fprintf(stderr, "du: cannot stat\n");
    close(fd);
    return 0;
  }

  // XXX(sbw) size should use an add reducer
  reducer_opadd<size_t> size(ST_SIZE(st));
  if (ST_ISDIR(st)) {
    dirit di(fd);
    wq_for<dirit>(di,
                  [](dirit &i)->bool { return !i.end(); },
                  [&](const char *name)->void
    {
      if (!strcmp(name, ".") || !strcmp(name, "..")) {
        free((void*)name);
        return;
      }

      int nfd = openat(fd, name, 0);
      if (nfd >= 0)
        size += du(nfd);  // should go into work queue
      free((void*)name);
    });
  } else {
    close(fd);
  }

  return size.get_value();
}

int
main(int ac, char **av)
{
  initwq();
  printf("%ld\n", du(open(".", 0)));
  return 0;
}
<commit_msg>Tighter capture scope<commit_after>#if defined(LINUX)
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <errno.h>
typedef uint64_t u64;
#include "include/wq.hh"
#include "user/dirit.hh"
#include "include/percpu.hh"
#define ST_SIZE(st)  (st).st_size
#define ST_ISDIR(st) S_ISDIR((st).st_mode)
#define BSIZ 256

#else // assume xv6

#include "types.h"
#include "stat.h"
#include "user.h"
#include "lib.h"
#include "fs.h"
#include "uspinlock.h"
#include "wq.hh"
#include "dirit.hh"
#include "percpu.hh"
#define ST_SIZE(st)  (st).size
#define ST_ISDIR(st) ((st).type == T_DIR)
#define stderr 2
#define BSIZ (DIRSIZ+1)
#endif

template <typename T>
struct reducer_opadd
{
  reducer_opadd(T v) {
    for (int i = 0; i < NCPU; i++)
      v_[i] = 0;
    *v_ = v;
  }

  T operator+=(T i) {
    (*v_) += i;
    return *v_;
  }

  T get_value() {
    T t = 0;
    for (int i = 0; i < NCPU; i++)
      t += v_[i];
    return t;
  }

  percpu<T> v_;
};

static size_t
du(int fd)
{
  struct stat st;
  if (fstat(fd, &st) < 0) {
    fprintf(stderr, "du: cannot stat\n");
    close(fd);
    return 0;
  }

  reducer_opadd<size_t> size(ST_SIZE(st));
  if (ST_ISDIR(st)) {
    dirit di(fd);
    wq_for<dirit>(di,
                  [](dirit &i)->bool { return !i.end(); },
                  [&size, &fd](const char *name)->void
    {
      if (!strcmp(name, ".") || !strcmp(name, "..")) {
        free((void*)name);
        return;
      }

      int nfd = openat(fd, name, 0);
      if (nfd >= 0)
        size += du(nfd);  // should go into work queue
      free((void*)name);
    });
  } else {
    close(fd);
  }

  return size.get_value();
}

int
main(int ac, char **av)
{
  initwq();
  printf("%ld\n", du(open(".", 0)));
  return 0;
}
<|endoftext|>


<commit_before>//
// TreeView.cpp
//
// Copyright (c) 2001 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef _MSC_VER
#pragma warning( disable : 4786 )
#endif

#include "vtlib/vtlib.h"

#include "App.h"
#include "TreeView.h"
#include "menu_id.h"
#include "Frame.h"

#include "vtdata/Content.h"
#include "vtdata/vtLog.h"

DECLARE_APP(vtApp)

// under Windows the icons are in the .rc file
#ifndef __WXMSW__
	#include "icon1.xpm"
	#include "icon2.xpm"
	#include "icon3.xpm"
	#include "icon4.xpm"
	#include "icon5.xpm"
	#include "mondrian.xpm"
#endif

//////////////////////////////////////////////////////////////////////////

// MyTreeCtrl implementation

MyTreeCtrl::MyTreeCtrl(wxWindow *parent, const wxWindowID id,
					   const wxPoint& pos, const wxSize& size,
					   long style)
		  : wxTreeCtrl(parent, id, pos, size, style)
{
	m_imageListNormal = NULL;
	m_bUpdating = false;

	CreateImageList(16);

	// Add some items to the tree
	RefreshTreeItems(NULL);
}

void MyTreeCtrl::CreateImageList(int size)
{
	delete m_imageListNormal;

	if ( size == -1 )
	{
		m_imageListNormal = NULL;
		return;
	}

	// Make an image list containing small icons
	m_imageListNormal = new wxImageList(size, size, TRUE);

	// should correspond to TreeCtrlIcon_xxx enum
	wxIcon icons[5];
	icons[0] = wxICON(icon8);
	icons[1] = wxICON(icon4);
	icons[2] = wxICON(icon11);
	icons[3] = wxICON(icon3);
	icons[4] = wxICON(icon12);

	int sizeOrig = icons[0].GetWidth();
	for ( size_t i = 0; i < WXSIZEOF(icons); i++ )
	{
		if ( size == sizeOrig )
			m_imageListNormal->Add(icons[i]);
		else
			m_imageListNormal->Add(wxBitmap(icons[i]).ConvertToImage().Rescale(size, size));
	}

	SetImageList(m_imageListNormal);
}

MyTreeCtrl::~MyTreeCtrl()
{
	delete m_imageListNormal;
}

wxTreeItemId rootId;

void MyTreeCtrl::RefreshTreeItems(vtFrame *pFrame)
{
	VTLOG(" RefreshTreeItems\n");

	m_bUpdating = true;

	DeleteAllItems();

	rootId = AddRoot(_T("Content"), MyTreeCtrl::TreeCtrlIcon_Content);
	SetItemBold(rootId);

	if (!pFrame)
	{
		m_bUpdating = false;
		return;
	}

	vtContentManager *pMan = &(pFrame->m_Man);

	unsigned int i, j;
	wxString2 str, str2;

	unsigned int iItems = pMan->NumItems();
	for (i = 0; i < iItems; i++)
	{
		vtItem *pItem = pMan->GetItem(i);

		str = pItem->m_name;

		wxTreeItemId hItem;
		hItem = AppendItem(rootId, str, TreeCtrlIcon_Item, TreeCtrlIcon_ItemSelected);

		SetItemData(hItem, new MyTreeItemData(pItem));

		if (pItem == pFrame->m_pCurrentItem)
			SelectItem(hItem);

		for (j = 0; j < pItem->NumModels(); j++)
		{
			vtModel *pModel = pItem->GetModel(j);

			str = (const char *) pModel->m_filename;
			int tri_count = pFrame->GetModelTriCount(pModel);
			str2.Printf(_T(" (%d tris)"), tri_count);
			str += str2;

			wxTreeItemId hItem2;
			hItem2 = AppendItem(hItem, str, TreeCtrlIcon_Model, TreeCtrlIcon_ModelSelected);

			SetItemData(hItem2, new MyTreeItemData(pModel));

			if (pModel == pFrame->m_pCurrentModel)
				SelectItem(hItem2);
		}
		Expand(hItem);
	}

	Expand(rootId);
	m_bUpdating = false;
}

void MyTreeCtrl::RefreshTreeStatus(vtFrame *pFrame)
{
	m_bUpdating = true;

	wxTreeItemId root = GetRootItem();
	wxTreeItemId parent, item;
	long cookie = 0, cookie2 = 1;

	for (parent = GetFirstChild(root, cookie); parent; parent = GetNextChild(root, cookie))
	{
		for (item = GetFirstChild(parent, cookie2); item; item = GetNextChild(parent, cookie2))
		{
			MyTreeItemData *data = (MyTreeItemData *)GetItemData(item);
			if (data)
			{
				if (data->m_pItem == pFrame->m_pCurrentItem)
					SelectItem(item);
				if (data->m_pModel == pFrame->m_pCurrentModel)
					SelectItem(item);
			}
		}
	}
	m_bUpdating = false;
}

// avoid repetition
#define TREE_EVENT_HANDLER(name)			\
void MyTreeCtrl::name(wxTreeEvent& event)	\
{											\
 /*	wxLogMessage(#name); */					\
	event.Skip();							\
}

TREE_EVENT_HANDLER(OnBeginRDrag)
TREE_EVENT_HANDLER(OnDeleteItem)
TREE_EVENT_HANDLER(OnGetInfo)
TREE_EVENT_HANDLER(OnSetInfo)
TREE_EVENT_HANDLER(OnItemExpanded)
TREE_EVENT_HANDLER(OnItemExpanding)
TREE_EVENT_HANDLER(OnItemCollapsed)
TREE_EVENT_HANDLER(OnSelChanging)
TREE_EVENT_HANDLER(OnTreeKeyDown)

#undef TREE_EVENT_HANDLER

void MyTreeCtrl::OnSelChanged(wxTreeEvent& event)
{
	// don't inform the rest of the interface, if it's currently informing us
	// that's a bad feedback loop
	if (m_bUpdating)
		return;

	wxTreeItemId item = event.GetItem();
	if (!IsSelected(item))
		return;

	bool bSelection = event.IsSelection();

	MyTreeItemData *data = (MyTreeItemData *)GetItemData(item);
	vtModel *mod = NULL;
	vtItem *ite = NULL;
	if (data)
	{
		mod = data->m_pModel;
		ite = data->m_pItem;
		if (mod)
		{
			wxTreeItemId pitem = GetItemParent(item);
			data = (MyTreeItemData *)GetItemData(pitem);
			ite = data->m_pItem;
		}
	}

	GetMainFrame()->SetCurrentItemAndModel(ite, mod);
}

void MyTreeCtrl::OnBeginDrag(wxTreeEvent& event)
{
}

void MyTreeCtrl::OnEndDrag(wxTreeEvent& event)
{
}

void MyTreeCtrl::OnBeginLabelEdit(wxTreeEvent& event)
{
	VTLOG("OnBeginLabelEdit\n");

	// If not an item prevent this items label editing
	wxTreeItemId itemId = event.GetItem();
	MyTreeItemData *data = (MyTreeItemData *)GetItemData(itemId);
	if (data != NULL && data->m_pItem != NULL && data->m_pModel == NULL)
	{
		// allow edit
	}
	else
	{
		VTLOG("You can't edit this item.\n");
		event.Veto();
	}
}

void MyTreeCtrl::OnEndLabelEdit(wxTreeEvent& event)
{
	VTLOG("OnEndLabelEdit\n");

	wxString2 result = event.GetLabel();
	wxTreeItemId itemId = event.GetItem();
	MyTreeItemData *data = (MyTreeItemData *)GetItemData(itemId);
	if (data != NULL && data->m_pItem != NULL)
	{
//		RefreshTreeStatus(frame);	// no need; tree already updated
		GetMainFrame()->SetItemName(data->m_pItem, result.vt_str());
	}
}

void MyTreeCtrl::OnItemCollapsing(wxTreeEvent& event)
{
#if 0
	wxLogMessage("OnItemCollapsing");

	// for testing, prevent the user from collapsing the first child folder
	wxTreeItemId itemId = event.GetItem();
	if ( IsTestItem(itemId) )
	{
		wxMessageBox("You can't collapse this item.");

		event.Veto();
	}
#endif
}

void MyTreeCtrl::OnItemActivated(wxTreeEvent& event)
{
#if 0
	// show some info about this item
	wxTreeItemId itemId = event.GetItem();
	MyTreeItemData *item = (MyTreeItemData *)GetItemData(itemId);

	if ( item != NULL )
	{
		item->ShowInfo(this);
	}
	wxLogMessage("OnItemActivated");
#endif
}

void MyTreeCtrl::OnRMouseDClick(wxMouseEvent& event)
{
#if 0
	wxTreeItemId id = HitTest(event.GetPosition());
	if ( !id )
		wxLogMessage("No item under mouse");
	else
	{
		MyTreeItemData *item = (MyTreeItemData *)GetItemData(id);
		if ( item )
			wxLogMessage("Item '%s' under mouse", item->GetDesc());
	}
#endif
}

/////////////////////////////////////////////////

BEGIN_EVENT_TABLE(MyTreeCtrl, wxTreeCtrl)
	EVT_TREE_BEGIN_DRAG(ID_TREECTRL, MyTreeCtrl::OnBeginDrag)
	EVT_TREE_BEGIN_RDRAG(ID_TREECTRL, MyTreeCtrl::OnBeginRDrag)
	EVT_TREE_END_DRAG(ID_TREECTRL, MyTreeCtrl::OnEndDrag)
	EVT_TREE_BEGIN_LABEL_EDIT(ID_TREECTRL, MyTreeCtrl::OnBeginLabelEdit)
	EVT_TREE_END_LABEL_EDIT(ID_TREECTRL, MyTreeCtrl::OnEndLabelEdit)
	EVT_TREE_DELETE_ITEM(ID_TREECTRL, MyTreeCtrl::OnDeleteItem)
	EVT_TREE_SET_INFO(ID_TREECTRL, MyTreeCtrl::OnSetInfo)
	EVT_TREE_ITEM_EXPANDED(ID_TREECTRL, MyTreeCtrl::OnItemExpanded)
	EVT_TREE_ITEM_EXPANDING(ID_TREECTRL, MyTreeCtrl::OnItemExpanding)
	EVT_TREE_ITEM_COLLAPSED(ID_TREECTRL, MyTreeCtrl::OnItemCollapsed)
	EVT_TREE_ITEM_COLLAPSING(ID_TREECTRL, MyTreeCtrl::OnItemCollapsing)
	EVT_TREE_SEL_CHANGED(ID_TREECTRL, MyTreeCtrl::OnSelChanged)
	EVT_TREE_SEL_CHANGING(ID_TREECTRL, MyTreeCtrl::OnSelChanging)
	EVT_TREE_KEY_DOWN(ID_TREECTRL, MyTreeCtrl::OnTreeKeyDown)
	EVT_TREE_ITEM_ACTIVATED(ID_TREECTRL, MyTreeCtrl::OnItemActivated)
	EVT_RIGHT_DCLICK(MyTreeCtrl::OnRMouseDClick)
END_EVENT_TABLE()

<commit_msg>remove unused tricount display<commit_after>//
// TreeView.cpp
//
// Copyright (c) 2001 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef _MSC_VER
#pragma warning( disable : 4786 )
#endif

#include "vtlib/vtlib.h"

#include "App.h"
#include "TreeView.h"
#include "menu_id.h"
#include "Frame.h"

#include "vtdata/Content.h"
#include "vtdata/vtLog.h"

DECLARE_APP(vtApp)

// under Windows the icons are in the .rc file
#ifndef __WXMSW__
	#include "icon1.xpm"
	#include "icon2.xpm"
	#include "icon3.xpm"
	#include "icon4.xpm"
	#include "icon5.xpm"
	#include "mondrian.xpm"
#endif

//////////////////////////////////////////////////////////////////////////

// MyTreeCtrl implementation

MyTreeCtrl::MyTreeCtrl(wxWindow *parent, const wxWindowID id,
					   const wxPoint& pos, const wxSize& size,
					   long style)
		  : wxTreeCtrl(parent, id, pos, size, style)
{
	m_imageListNormal = NULL;
	m_bUpdating = false;

	CreateImageList(16);

	// Add some items to the tree
	RefreshTreeItems(NULL);
}

void MyTreeCtrl::CreateImageList(int size)
{
	delete m_imageListNormal;

	if ( size == -1 )
	{
		m_imageListNormal = NULL;
		return;
	}

	// Make an image list containing small icons
	m_imageListNormal = new wxImageList(size, size, TRUE);

	// should correspond to TreeCtrlIcon_xxx enum
	wxIcon icons[5];
	icons[0] = wxICON(icon8);
	icons[1] = wxICON(icon4);
	icons[2] = wxICON(icon11);
	icons[3] = wxICON(icon3);
	icons[4] = wxICON(icon12);

	int sizeOrig = icons[0].GetWidth();
	for ( size_t i = 0; i < WXSIZEOF(icons); i++ )
	{
		if ( size == sizeOrig )
			m_imageListNormal->Add(icons[i]);
		else
			m_imageListNormal->Add(wxBitmap(icons[i]).ConvertToImage().Rescale(size, size));
	}

	SetImageList(m_imageListNormal);
}

MyTreeCtrl::~MyTreeCtrl()
{
	delete m_imageListNormal;
}

wxTreeItemId rootId;

void MyTreeCtrl::RefreshTreeItems(vtFrame *pFrame)
{
	VTLOG(" RefreshTreeItems\n");

	m_bUpdating = true;

	DeleteAllItems();

	rootId = AddRoot(_T("Content"), MyTreeCtrl::TreeCtrlIcon_Content);
	SetItemBold(rootId);

	if (!pFrame)
	{
		m_bUpdating = false;
		return;
	}

	vtContentManager *pMan = &(pFrame->m_Man);

	unsigned int i, j;
	wxString2 str, str2;

	unsigned int iItems = pMan->NumItems();
	for (i = 0; i < iItems; i++)
	{
		vtItem *pItem = pMan->GetItem(i);

		str = pItem->m_name;

		wxTreeItemId hItem;
		hItem = AppendItem(rootId, str, TreeCtrlIcon_Item, TreeCtrlIcon_ItemSelected);

		SetItemData(hItem, new MyTreeItemData(pItem));

		if (pItem == pFrame->m_pCurrentItem)
			SelectItem(hItem);

		for (j = 0; j < pItem->NumModels(); j++)
		{
			vtModel *pModel = pItem->GetModel(j);

			str = (const char *) pModel->m_filename;

			wxTreeItemId hItem2;
			hItem2 = AppendItem(hItem, str, TreeCtrlIcon_Model, TreeCtrlIcon_ModelSelected);

			SetItemData(hItem2, new MyTreeItemData(pModel));

			if (pModel == pFrame->m_pCurrentModel)
				SelectItem(hItem2);
		}
		Expand(hItem);
	}

	Expand(rootId);
	m_bUpdating = false;
}

void MyTreeCtrl::RefreshTreeStatus(vtFrame *pFrame)
{
	m_bUpdating = true;

	wxTreeItemId root = GetRootItem();
	wxTreeItemId parent, item;
	long cookie = 0, cookie2 = 1;

	for (parent = GetFirstChild(root, cookie); parent; parent = GetNextChild(root, cookie))
	{
		for (item = GetFirstChild(parent, cookie2); item; item = GetNextChild(parent, cookie2))
		{
			MyTreeItemData *data = (MyTreeItemData *)GetItemData(item);
			if (data)
			{
				if (data->m_pItem == pFrame->m_pCurrentItem)
					SelectItem(item);
				if (data->m_pModel == pFrame->m_pCurrentModel)
					SelectItem(item);
			}
		}
	}
	m_bUpdating = false;
}

// avoid repetition
#define TREE_EVENT_HANDLER(name)			\
void MyTreeCtrl::name(wxTreeEvent& event)	\
{											\
 /*	wxLogMessage(#name); */					\
	event.Skip();							\
}

TREE_EVENT_HANDLER(OnBeginRDrag)
TREE_EVENT_HANDLER(OnDeleteItem)
TREE_EVENT_HANDLER(OnGetInfo)
TREE_EVENT_HANDLER(OnSetInfo)
TREE_EVENT_HANDLER(OnItemExpanded)
TREE_EVENT_HANDLER(OnItemExpanding)
TREE_EVENT_HANDLER(OnItemCollapsed)
TREE_EVENT_HANDLER(OnSelChanging)
TREE_EVENT_HANDLER(OnTreeKeyDown)

#undef TREE_EVENT_HANDLER

void MyTreeCtrl::OnSelChanged(wxTreeEvent& event)
{
	// don't inform the rest of the interface, if it's currently informing us
	// that's a bad feedback loop
	if (m_bUpdating)
		return;

	wxTreeItemId item = event.GetItem();
	if (!IsSelected(item))
		return;

	bool bSelection = event.IsSelection();

	MyTreeItemData *data = (MyTreeItemData *)GetItemData(item);
	vtModel *mod = NULL;
	vtItem *ite = NULL;
	if (data)
	{
		mod = data->m_pModel;
		ite = data->m_pItem;
		if (mod)
		{
			wxTreeItemId pitem = GetItemParent(item);
			data = (MyTreeItemData *)GetItemData(pitem);
			ite = data->m_pItem;
		}
	}

	GetMainFrame()->SetCurrentItemAndModel(ite, mod);
}

void MyTreeCtrl::OnBeginDrag(wxTreeEvent& event)
{
}

void MyTreeCtrl::OnEndDrag(wxTreeEvent& event)
{
}

void MyTreeCtrl::OnBeginLabelEdit(wxTreeEvent& event)
{
	VTLOG("OnBeginLabelEdit\n");

	// If not an item prevent this items label editing
	wxTreeItemId itemId = event.GetItem();
	MyTreeItemData *data = (MyTreeItemData *)GetItemData(itemId);
	if (data != NULL && data->m_pItem != NULL && data->m_pModel == NULL)
	{
		// allow edit
	}
	else
	{
		VTLOG("You can't edit this item.\n");
		event.Veto();
	}
}

void MyTreeCtrl::OnEndLabelEdit(wxTreeEvent& event)
{
	VTLOG("OnEndLabelEdit\n");

	wxString2 result = event.GetLabel();
	wxTreeItemId itemId = event.GetItem();
	MyTreeItemData *data = (MyTreeItemData *)GetItemData(itemId);
	if (data != NULL && data->m_pItem != NULL)
	{
//		RefreshTreeStatus(frame);	// no need; tree already updated
		GetMainFrame()->SetItemName(data->m_pItem, result.vt_str());
	}
}

void MyTreeCtrl::OnItemCollapsing(wxTreeEvent& event)
{
#if 0
	wxLogMessage("OnItemCollapsing");

	// for testing, prevent the user from collapsing the first child folder
	wxTreeItemId itemId = event.GetItem();
	if ( IsTestItem(itemId) )
	{
		wxMessageBox("You can't collapse this item.");

		event.Veto();
	}
#endif
}

void MyTreeCtrl::OnItemActivated(wxTreeEvent& event)
{
#if 0
	// show some info about this item
	wxTreeItemId itemId = event.GetItem();
	MyTreeItemData *item = (MyTreeItemData *)GetItemData(itemId);

	if ( item != NULL )
	{
		item->ShowInfo(this);
	}
	wxLogMessage("OnItemActivated");
#endif
}

void MyTreeCtrl::OnRMouseDClick(wxMouseEvent& event)
{
#if 0
	wxTreeItemId id = HitTest(event.GetPosition());
	if ( !id )
		wxLogMessage("No item under mouse");
	else
	{
		MyTreeItemData *item = (MyTreeItemData *)GetItemData(id);
		if ( item )
			wxLogMessage("Item '%s' under mouse", item->GetDesc());
	}
#endif
}

/////////////////////////////////////////////////

BEGIN_EVENT_TABLE(MyTreeCtrl, wxTreeCtrl)
	EVT_TREE_BEGIN_DRAG(ID_TREECTRL, MyTreeCtrl::OnBeginDrag)
	EVT_TREE_BEGIN_RDRAG(ID_TREECTRL, MyTreeCtrl::OnBeginRDrag)
	EVT_TREE_END_DRAG(ID_TREECTRL, MyTreeCtrl::OnEndDrag)
	EVT_TREE_BEGIN_LABEL_EDIT(ID_TREECTRL, MyTreeCtrl::OnBeginLabelEdit)
	EVT_TREE_END_LABEL_EDIT(ID_TREECTRL, MyTreeCtrl::OnEndLabelEdit)
	EVT_TREE_DELETE_ITEM(ID_TREECTRL, MyTreeCtrl::OnDeleteItem)
	EVT_TREE_SET_INFO(ID_TREECTRL, MyTreeCtrl::OnSetInfo)
	EVT_TREE_ITEM_EXPANDED(ID_TREECTRL, MyTreeCtrl::OnItemExpanded)
	EVT_TREE_ITEM_EXPANDING(ID_TREECTRL, MyTreeCtrl::OnItemExpanding)
	EVT_TREE_ITEM_COLLAPSED(ID_TREECTRL, MyTreeCtrl::OnItemCollapsed)
	EVT_TREE_ITEM_COLLAPSING(ID_TREECTRL, MyTreeCtrl::OnItemCollapsing)
	EVT_TREE_SEL_CHANGED(ID_TREECTRL, MyTreeCtrl::OnSelChanged)
	EVT_TREE_SEL_CHANGING(ID_TREECTRL, MyTreeCtrl::OnSelChanging)
	EVT_TREE_KEY_DOWN(ID_TREECTRL, MyTreeCtrl::OnTreeKeyDown)
	EVT_TREE_ITEM_ACTIVATED(ID_TREECTRL, MyTreeCtrl::OnItemActivated)
	EVT_RIGHT_DCLICK(MyTreeCtrl::OnRMouseDClick)
END_EVENT_TABLE()

<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/

#include "cpphighlighter.h"
#include <cpptools/cppdoxygen.h>

#include <Token.h>
#include <cplusplus/SimpleLexer.h>
#include <cpptools/cpptoolsreuse.h>
#include <texteditor/basetextdocumentlayout.h>

#include <QTextDocument>
#include <QDebug>

using namespace CppEditor::Internal;
using namespace TextEditor;
using namespace CPlusPlus;

CppHighlighter::CppHighlighter(QTextDocument *document) :
    TextEditor::SyntaxHighlighter(document)
{
}

void CppHighlighter::highlightBlock(const QString &text)
{
    const int previousState = previousBlockState();
    int state = 0, initialBraceDepth = 0;
    if (previousState != -1) {
        state = previousState & 0xff;
        initialBraceDepth = previousState >> 8;
    }

    int braceDepth = initialBraceDepth;

    SimpleLexer tokenize;
    tokenize.setQtMocRunEnabled(false);
    tokenize.setObjCEnabled(false);
    tokenize.setCxx0xEnabled(true);

    int initialState = state;
    const QList<Token> tokens = tokenize(text, initialState);
    state = tokenize.state(); // refresh the state

    int foldingIndent = initialBraceDepth;
    if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) {
        userData->setFoldingIndent(0);
        userData->setFoldingStartIncluded(false);
        userData->setFoldingEndIncluded(false);
    }

    if (tokens.isEmpty()) {
        setCurrentBlockState(previousState);
        BaseTextDocumentLayout::clearParentheses(currentBlock());
        if (text.length())  {// the empty line can still contain whitespace
            if (!initialState)
                setFormat(0, text.length(), m_formats[CppVisualWhitespace]);
            else
                setFormat(0, text.length(), m_formats[CppCommentFormat]);
        }
        BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
        return;
    }

    const unsigned firstNonSpace = tokens.first().begin();

    Parentheses parentheses;
    parentheses.reserve(20); // assume wizard level ;-)

    bool expectPreprocessorKeyword = false;
    bool onlyHighlightComments = false;

    for (int i = 0; i < tokens.size(); ++i) {
        const Token &tk = tokens.at(i);

        unsigned previousTokenEnd = 0;
        if (i != 0) {
            // mark the whitespaces
            previousTokenEnd = tokens.at(i - 1).begin() +
                               tokens.at(i - 1).length();
        }

        if (previousTokenEnd != tk.begin()) {
            if (initialState && tk.isComment())
                setFormat(previousTokenEnd, tk.begin() - previousTokenEnd, m_formats[CppCommentFormat]);
            else
                setFormat(previousTokenEnd, tk.begin() - previousTokenEnd, m_formats[CppVisualWhitespace]);
        }

        if (tk.is(T_LPAREN) || tk.is(T_LBRACE) || tk.is(T_LBRACKET)) {
            const QChar c = text.at(tk.begin());
            parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.begin()));
            if (tk.is(T_LBRACE)) {
                ++braceDepth;

                // if a folding block opens at the beginning of a line, treat the entire line
                // as if it were inside the folding block
                if (tk.begin() == firstNonSpace) {
                    ++foldingIndent;
                    BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true);
                }
            }
        } else if (tk.is(T_RPAREN) || tk.is(T_RBRACE) || tk.is(T_RBRACKET)) {
            const QChar c = text.at(tk.begin());
            parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.begin()));
            if (tk.is(T_RBRACE)) {
                --braceDepth;
                if (braceDepth < foldingIndent) {
                    // unless we are at the end of the block, we reduce the folding indent
                    if (i == tokens.size()-1 || tokens.at(i+1).is(T_SEMICOLON))
                        BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true);
                    else
                        foldingIndent = qMin(braceDepth, foldingIndent);
                }
            }
        }

        bool highlightCurrentWordAsPreprocessor = expectPreprocessorKeyword;

        if (expectPreprocessorKeyword)
            expectPreprocessorKeyword = false;

        if (onlyHighlightComments && !tk.isComment())
            continue;

        if (i == 0 && tk.is(T_POUND)) {
            highlightLine(text, tk.begin(), tk.length(), m_formats[CppPreprocessorFormat]);
            expectPreprocessorKeyword = true;
        } else if (highlightCurrentWordAsPreprocessor &&
                   (tk.isKeyword() || tk.is(T_IDENTIFIER)) && isPPKeyword(text.midRef(tk.begin(), tk.length()))) {
            setFormat(tk.begin(), tk.length(), m_formats[CppPreprocessorFormat]);
            const QStringRef ppKeyword = text.midRef(tk.begin(), tk.length());
            if (ppKeyword == QLatin1String("error")
                    || ppKeyword == QLatin1String("warning")
                    || ppKeyword == QLatin1String("pragma")) {
                onlyHighlightComments = true;
            }

        } else if (tk.is(T_NUMERIC_LITERAL))
            setFormat(tk.begin(), tk.length(), m_formats[CppNumberFormat]);

        else if (tk.isStringLiteral() || tk.isCharLiteral())
            setFormat(tk.begin(), tk.length(), m_formats[CppStringFormat]);

        else if (tk.isComment()) {
            if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT))
                setFormat(tk.begin(), tk.length(), m_formats[CppCommentFormat]);

            else // a doxygen comment
                highlightDoxygenComment(text, tk.begin(), tk.length());

            // we need to insert a close comment parenthesis, if
            //  - the line starts in a C Comment (initalState != 0)
            //  - the first token of the line is a T_COMMENT (i == 0 && tk.is(T_COMMENT))
            //  - is not a continuation line (tokens.size() > 1 || ! state)
            if (initialState && i == 0 && (tokens.size() > 1 || ! state)) {
                --braceDepth;
                // unless we are at the end of the block, we reduce the folding indent
                if (i == tokens.size()-1)
                    BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true);
                else
                    foldingIndent = qMin(braceDepth, foldingIndent);
                const int tokenEnd = tk.begin() + tk.length() - 1;
                parentheses.append(Parenthesis(Parenthesis::Closed, QLatin1Char('-'), tokenEnd));

                // clear the initial state.
                initialState = 0;
            }

        } else if (tk.isKeyword() || CppTools::isQtKeyword(text.midRef(tk.begin(), tk.length())) || tk.isObjCAtKeyword())
            setFormat(tk.begin(), tk.length(), m_formats[CppKeywordFormat]);

        else if (tk.isOperator())
            setFormat(tk.begin(), tk.length(), m_formats[CppOperatorFormat]);

        else if (i == 0 && tokens.size() > 1 && tk.is(T_IDENTIFIER) && tokens.at(1).is(T_COLON))
            setFormat(tk.begin(), tk.length(), m_formats[CppLabelFormat]);

        else if (tk.is(T_IDENTIFIER))
            highlightWord(text.midRef(tk.begin(), tk.length()), tk.begin(), tk.length());

    }

    // mark the trailing white spaces
    {
        const Token tk = tokens.last();
        const int lastTokenEnd = tk.begin() + tk.length();
        if (text.length() > lastTokenEnd)
            highlightLine(text, lastTokenEnd, text.length() - lastTokenEnd, QTextCharFormat());
    }

    if (! initialState && state && ! tokens.isEmpty()) {
        parentheses.append(Parenthesis(Parenthesis::Opened, QLatin1Char('+'),
                                       tokens.last().begin()));
        ++braceDepth;
    }

    BaseTextDocumentLayout::setParentheses(currentBlock(), parentheses);

    // if the block is ifdefed out, we only store the parentheses, but

    // do not adjust the brace depth.
    if (BaseTextDocumentLayout::ifdefedOut(currentBlock())) {
        braceDepth = initialBraceDepth;
        foldingIndent = initialBraceDepth;
    }

    BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);

    // optimization: if only the brace depth changes, we adjust subsequent blocks
    // to have QSyntaxHighlighter stop the rehighlighting
    int currentState = currentBlockState();
    if (currentState != -1) {
        int oldState = currentState & 0xff;
        int oldBraceDepth = currentState >> 8;
        if (oldState == tokenize.state() && oldBraceDepth != braceDepth) {
            BaseTextDocumentLayout::FoldValidator foldValidor;
            foldValidor.setup(qobject_cast<BaseTextDocumentLayout *>(document()->documentLayout()));
            int delta = braceDepth - oldBraceDepth;
            QTextBlock block = currentBlock().next();
            while (block.isValid() && block.userState() != -1) {
                BaseTextDocumentLayout::changeBraceDepth(block, delta);
                BaseTextDocumentLayout::changeFoldingIndent(block, delta);
                foldValidor.process(block);
                block = block.next();
            }
            foldValidor.finalize();
        }
    }

    setCurrentBlockState((braceDepth << 8) | tokenize.state());
}

bool CppHighlighter::isPPKeyword(const QStringRef &text) const
{
    switch (text.length())
    {
    case 2:
        if (text.at(0) == QLatin1Char('i') && text.at(1) == QLatin1Char('f'))
            return true;
        break;

    case 4:
        if (text.at(0) == QLatin1Char('e')
            && (text == QLatin1String("elif") || text == QLatin1String("else")))
            return true;
        break;

    case 5:
        switch (text.at(0).toLatin1()) {
        case 'i':
            if (text == QLatin1String("ifdef"))
                return true;
            break;
          case 'u':
            if (text == QLatin1String("undef"))
                return true;
            break;
        case 'e':
            if (text == QLatin1String("endif") || text == QLatin1String("error"))
                return true;
            break;
        }
        break;

    case 6:
        switch (text.at(0).toLatin1()) {
        case 'i':
            if (text == QLatin1String("ifndef") || text == QLatin1String("import"))
                return true;
            break;
        case 'd':
            if (text == QLatin1String("define"))
                return true;
            break;
        case 'p':
            if (text == QLatin1String("pragma"))
                return true;
            break;
        }
        break;

    case 7:
        switch (text.at(0).toLatin1()) {
        case 'i':
            if (text == QLatin1String("include"))
                return true;
            break;
        case 'w':
            if (text == QLatin1String("warning"))
                return true;
            break;
        }
        break;

    case 12:
        if (text.at(0) == QLatin1Char('i') && text == QLatin1String("include_next"))
            return true;
        break;

    default:
        break;
    }

    return false;
}

void CppHighlighter::highlightLine(const QString &text, int position, int length,
                                   const QTextCharFormat &format)
{
    const QTextCharFormat visualSpaceFormat = m_formats[CppVisualWhitespace];

    const int end = position + length;
    int index = position;

    while (index != end) {
        const bool isSpace = text.at(index).isSpace();
        const int start = index;

        do { ++index; }
        while (index != end && text.at(index).isSpace() == isSpace);

        const int tokenLength = index - start;
        if (isSpace)
            setFormat(start, tokenLength, visualSpaceFormat);
        else if (format.isValid())
            setFormat(start, tokenLength, format);
    }
}

void CppHighlighter::highlightWord(QStringRef word, int position, int length)
{
    // try to highlight Qt 'identifiers' like QObject and Q_PROPERTY

    if (word.length() > 2 && word.at(0) == QLatin1Char('Q')) {
        if (word.at(1) == QLatin1Char('_') // Q_
            || (word.at(1) == QLatin1Char('T') && word.at(2) == QLatin1Char('_'))) { // QT_
            for (int i = 1; i < word.length(); ++i) {
                const QChar &ch = word.at(i);
                if (! (ch.isUpper() || ch == QLatin1Char('_')))
                    return;
            }

            setFormat(position, length, m_formats[CppTypeFormat]);
        }
    }
}

void CppHighlighter::highlightDoxygenComment(const QString &text, int position, int)
{
    int initial = position;

    const QChar *uc = text.unicode();
    const QChar *it = uc + position;

    const QTextCharFormat &format = m_formats[CppDoxygenCommentFormat];
    const QTextCharFormat &kwFormat = m_formats[CppDoxygenTagFormat];

    while (! it->isNull()) {
        if (it->unicode() == QLatin1Char('\\') ||
            it->unicode() == QLatin1Char('@')) {
            ++it;

            const QChar *start = it;
            while (it->isLetterOrNumber() || it->unicode() == '_')
                ++it;

            int k = CppTools::classifyDoxygenTag(start, it - start);
            if (k != CppTools::T_DOXY_IDENTIFIER) {
                highlightLine(text, initial, start - uc - initial, format);
                setFormat(start - uc - 1, it - start + 1, kwFormat);
                initial = it - uc;
            }
        } else
            ++it;
    }

    highlightLine(text, initial, it - uc - initial, format);
}

<commit_msg>Editor: Fix visual whitespace highlighting<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/

#include "cpphighlighter.h"

#include <Token.h>
#include <cplusplus/SimpleLexer.h>
#include <cplusplus/Lexer.h>
#include <cpptools/cppdoxygen.h>
#include <cpptools/cpptoolsreuse.h>
#include <texteditor/basetextdocumentlayout.h>

#include <QTextDocument>
#include <QDebug>

using namespace CppEditor::Internal;
using namespace TextEditor;
using namespace CPlusPlus;

CppHighlighter::CppHighlighter(QTextDocument *document) :
    TextEditor::SyntaxHighlighter(document)
{
}

void CppHighlighter::highlightBlock(const QString &text)
{
    const int previousState = previousBlockState();
    int state = 0, initialBraceDepth = 0;
    if (previousState != -1) {
        state = previousState & 0xff;
        initialBraceDepth = previousState >> 8;
    }

    int braceDepth = initialBraceDepth;

    SimpleLexer tokenize;
    tokenize.setQtMocRunEnabled(false);
    tokenize.setObjCEnabled(false);
    tokenize.setCxx0xEnabled(true);

    int initialState = state;
    const QList<Token> tokens = tokenize(text, initialState);
    state = tokenize.state(); // refresh the state

    int foldingIndent = initialBraceDepth;
    if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) {
        userData->setFoldingIndent(0);
        userData->setFoldingStartIncluded(false);
        userData->setFoldingEndIncluded(false);
    }

    if (tokens.isEmpty()) {
        setCurrentBlockState(previousState);
        BaseTextDocumentLayout::clearParentheses(currentBlock());
        if (text.length())  {// the empty line can still contain whitespace
            if (initialState == Lexer::State_MultiLineComment)
                highlightLine(text, 0, text.length(), m_formats[CppCommentFormat]);
            else if (initialState == Lexer::State_MultiLineDoxyComment)
                highlightLine(text, 0, text.length(), m_formats[CppDoxygenCommentFormat]);
            else
                setFormat(0, text.length(), m_formats[CppVisualWhitespace]);
        }
        BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
        return;
    }

    const unsigned firstNonSpace = tokens.first().begin();

    Parentheses parentheses;
    parentheses.reserve(20); // assume wizard level ;-)

    bool expectPreprocessorKeyword = false;
    bool onlyHighlightComments = false;

    for (int i = 0; i < tokens.size(); ++i) {
        const Token &tk = tokens.at(i);

        unsigned previousTokenEnd = 0;
        if (i != 0) {
            // mark the whitespaces
            previousTokenEnd = tokens.at(i - 1).begin() +
                               tokens.at(i - 1).length();
        }

        if (previousTokenEnd != tk.begin())
            setFormat(previousTokenEnd, tk.begin() - previousTokenEnd, m_formats[CppVisualWhitespace]);

        if (tk.is(T_LPAREN) || tk.is(T_LBRACE) || tk.is(T_LBRACKET)) {
            const QChar c = text.at(tk.begin());
            parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.begin()));
            if (tk.is(T_LBRACE)) {
                ++braceDepth;

                // if a folding block opens at the beginning of a line, treat the entire line
                // as if it were inside the folding block
                if (tk.begin() == firstNonSpace) {
                    ++foldingIndent;
                    BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true);
                }
            }
        } else if (tk.is(T_RPAREN) || tk.is(T_RBRACE) || tk.is(T_RBRACKET)) {
            const QChar c = text.at(tk.begin());
            parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.begin()));
            if (tk.is(T_RBRACE)) {
                --braceDepth;
                if (braceDepth < foldingIndent) {
                    // unless we are at the end of the block, we reduce the folding indent
                    if (i == tokens.size()-1 || tokens.at(i+1).is(T_SEMICOLON))
                        BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true);
                    else
                        foldingIndent = qMin(braceDepth, foldingIndent);
                }
            }
        }

        bool highlightCurrentWordAsPreprocessor = expectPreprocessorKeyword;

        if (expectPreprocessorKeyword)
            expectPreprocessorKeyword = false;

        if (onlyHighlightComments && !tk.isComment())
            continue;

        if (i == 0 && tk.is(T_POUND)) {
            highlightLine(text, tk.begin(), tk.length(), m_formats[CppPreprocessorFormat]);
            expectPreprocessorKeyword = true;
        } else if (highlightCurrentWordAsPreprocessor &&
                   (tk.isKeyword() || tk.is(T_IDENTIFIER)) && isPPKeyword(text.midRef(tk.begin(), tk.length()))) {
            setFormat(tk.begin(), tk.length(), m_formats[CppPreprocessorFormat]);
            const QStringRef ppKeyword = text.midRef(tk.begin(), tk.length());
            if (ppKeyword == QLatin1String("error")
                    || ppKeyword == QLatin1String("warning")
                    || ppKeyword == QLatin1String("pragma")) {
                onlyHighlightComments = true;
            }

        } else if (tk.is(T_NUMERIC_LITERAL))
            setFormat(tk.begin(), tk.length(), m_formats[CppNumberFormat]);

        else if (tk.isStringLiteral() || tk.isCharLiteral())
            highlightLine(text, tk.begin(), tk.length(), m_formats[CppStringFormat]);

        else if (tk.isComment()) {
            const int startPosition = initialState ? previousTokenEnd : tk.begin();
            if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT))
                highlightLine(text, startPosition, tk.end() - startPosition, m_formats[CppCommentFormat]);

            else // a doxygen comment
                highlightDoxygenComment(text, startPosition, tk.end() - startPosition);

            // we need to insert a close comment parenthesis, if
            //  - the line starts in a C Comment (initalState != 0)
            //  - the first token of the line is a T_COMMENT (i == 0 && tk.is(T_COMMENT))
            //  - is not a continuation line (tokens.size() > 1 || ! state)
            if (initialState && i == 0 && (tokens.size() > 1 || ! state)) {
                --braceDepth;
                // unless we are at the end of the block, we reduce the folding indent
                if (i == tokens.size()-1)
                    BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true);
                else
                    foldingIndent = qMin(braceDepth, foldingIndent);
                const int tokenEnd = tk.begin() + tk.length() - 1;
                parentheses.append(Parenthesis(Parenthesis::Closed, QLatin1Char('-'), tokenEnd));

                // clear the initial state.
                initialState = 0;
            }

        } else if (tk.isKeyword() || CppTools::isQtKeyword(text.midRef(tk.begin(), tk.length())) || tk.isObjCAtKeyword())
            setFormat(tk.begin(), tk.length(), m_formats[CppKeywordFormat]);

        else if (tk.isOperator())
            setFormat(tk.begin(), tk.length(), m_formats[CppOperatorFormat]);

        else if (i == 0 && tokens.size() > 1 && tk.is(T_IDENTIFIER) && tokens.at(1).is(T_COLON))
            setFormat(tk.begin(), tk.length(), m_formats[CppLabelFormat]);

        else if (tk.is(T_IDENTIFIER))
            highlightWord(text.midRef(tk.begin(), tk.length()), tk.begin(), tk.length());

    }

    // mark the trailing white spaces
    const int lastTokenEnd = tokens.last().end();
    if (text.length() > lastTokenEnd)
        highlightLine(text, lastTokenEnd, text.length() - lastTokenEnd, m_formats[CppVisualWhitespace]);

    if (! initialState && state && ! tokens.isEmpty()) {
        parentheses.append(Parenthesis(Parenthesis::Opened, QLatin1Char('+'),
                                       tokens.last().begin()));
        ++braceDepth;
    }

    BaseTextDocumentLayout::setParentheses(currentBlock(), parentheses);

    // if the block is ifdefed out, we only store the parentheses, but

    // do not adjust the brace depth.
    if (BaseTextDocumentLayout::ifdefedOut(currentBlock())) {
        braceDepth = initialBraceDepth;
        foldingIndent = initialBraceDepth;
    }

    BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);

    // optimization: if only the brace depth changes, we adjust subsequent blocks
    // to have QSyntaxHighlighter stop the rehighlighting
    int currentState = currentBlockState();
    if (currentState != -1) {
        int oldState = currentState & 0xff;
        int oldBraceDepth = currentState >> 8;
        if (oldState == tokenize.state() && oldBraceDepth != braceDepth) {
            BaseTextDocumentLayout::FoldValidator foldValidor;
            foldValidor.setup(qobject_cast<BaseTextDocumentLayout *>(document()->documentLayout()));
            int delta = braceDepth - oldBraceDepth;
            QTextBlock block = currentBlock().next();
            while (block.isValid() && block.userState() != -1) {
                BaseTextDocumentLayout::changeBraceDepth(block, delta);
                BaseTextDocumentLayout::changeFoldingIndent(block, delta);
                foldValidor.process(block);
                block = block.next();
            }
            foldValidor.finalize();
        }
    }

    setCurrentBlockState((braceDepth << 8) | tokenize.state());
}

bool CppHighlighter::isPPKeyword(const QStringRef &text) const
{
    switch (text.length())
    {
    case 2:
        if (text.at(0) == QLatin1Char('i') && text.at(1) == QLatin1Char('f'))
            return true;
        break;

    case 4:
        if (text.at(0) == QLatin1Char('e')
            && (text == QLatin1String("elif") || text == QLatin1String("else")))
            return true;
        break;

    case 5:
        switch (text.at(0).toLatin1()) {
        case 'i':
            if (text == QLatin1String("ifdef"))
                return true;
            break;
          case 'u':
            if (text == QLatin1String("undef"))
                return true;
            break;
        case 'e':
            if (text == QLatin1String("endif") || text == QLatin1String("error"))
                return true;
            break;
        }
        break;

    case 6:
        switch (text.at(0).toLatin1()) {
        case 'i':
            if (text == QLatin1String("ifndef") || text == QLatin1String("import"))
                return true;
            break;
        case 'd':
            if (text == QLatin1String("define"))
                return true;
            break;
        case 'p':
            if (text == QLatin1String("pragma"))
                return true;
            break;
        }
        break;

    case 7:
        switch (text.at(0).toLatin1()) {
        case 'i':
            if (text == QLatin1String("include"))
                return true;
            break;
        case 'w':
            if (text == QLatin1String("warning"))
                return true;
            break;
        }
        break;

    case 12:
        if (text.at(0) == QLatin1Char('i') && text == QLatin1String("include_next"))
            return true;
        break;

    default:
        break;
    }

    return false;
}

void CppHighlighter::highlightLine(const QString &text, int position, int length,
                                   const QTextCharFormat &format)
{
    QTextCharFormat visualSpaceFormat = m_formats[CppVisualWhitespace];
    visualSpaceFormat.setBackground(format.background());

    const int end = position + length;
    int index = position;

    while (index != end) {
        const bool isSpace = text.at(index).isSpace();
        const int start = index;

        do { ++index; }
        while (index != end && text.at(index).isSpace() == isSpace);

        const int tokenLength = index - start;
        if (isSpace)
            setFormat(start, tokenLength, visualSpaceFormat);
        else if (format.isValid())
            setFormat(start, tokenLength, format);
    }
}

void CppHighlighter::highlightWord(QStringRef word, int position, int length)
{
    // try to highlight Qt 'identifiers' like QObject and Q_PROPERTY

    if (word.length() > 2 && word.at(0) == QLatin1Char('Q')) {
        if (word.at(1) == QLatin1Char('_') // Q_
            || (word.at(1) == QLatin1Char('T') && word.at(2) == QLatin1Char('_'))) { // QT_
            for (int i = 1; i < word.length(); ++i) {
                const QChar &ch = word.at(i);
                if (! (ch.isUpper() || ch == QLatin1Char('_')))
                    return;
            }

            setFormat(position, length, m_formats[CppTypeFormat]);
        }
    }
}

void CppHighlighter::highlightDoxygenComment(const QString &text, int position, int)
{
    int initial = position;

    const QChar *uc = text.unicode();
    const QChar *it = uc + position;

    const QTextCharFormat &format = m_formats[CppDoxygenCommentFormat];
    const QTextCharFormat &kwFormat = m_formats[CppDoxygenTagFormat];

    while (! it->isNull()) {
        if (it->unicode() == QLatin1Char('\\') ||
            it->unicode() == QLatin1Char('@')) {
            ++it;

            const QChar *start = it;
            while (it->isLetterOrNumber() || it->unicode() == '_')
                ++it;

            int k = CppTools::classifyDoxygenTag(start, it - start);
            if (k != CppTools::T_DOXY_IDENTIFIER) {
                highlightLine(text, initial, start - uc - initial, format);
                setFormat(start - uc - 1, it - start + 1, kwFormat);
                initial = it - uc;
            }
        } else
            ++it;
    }

    highlightLine(text, initial, it - uc - initial, format);
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2014 Harm Hanemaaijer <fgenfb@yahoo.com>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

// X11 (low-level) OpenGL(GLX) interface.
// Uses X11 handling in x11-common.c.
// Currently requires freeglut to get around issues with
// initializing GLEW and destroying atemporary window at
// start-up.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <sys/time.h>
#include <assert.h>
#include <GL/glew.h>
#include <GL/glxew.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>
// We don´t really use glut, but only use it to create a temporary
// rendering context before initializing GLEW.
#include <GL/glut.h>
#include <GL/freeglut_ext.h>

#include "sre.h"
#include "sreBackend.h"
#include "x11-common.h"
#include "gui-common.h"

class sreBackendGLX11 : public sreBackend {
public :
    virtual void Initialize(int *argc, char ***argv, int window_width, int window_height);
    virtual void Finalize();
    virtual void GLSwapBuffers();
    virtual void GLSync();
    virtual double GetCurrentTime();
    virtual void ProcessGUIEvents();
    virtual void ToggleFullScreenMode(int& width, int& height, bool pan_with_mouse);
    virtual void HideCursor();
    virtual void RestoreCursor();
    virtual void WarpCursor(int x, int y);
};

sreBackend *sreCreateBackendGLX11() {
    sreBackend *b = new sreBackendGLX11;
    b->name = "OpenGL 3.0+ X11 (low-level)";
    return b;
}

typedef struct
{
   uint32_t screen_width;
   uint32_t screen_height;
   Display *XDisplay;
   GLXWindow XWindow;
   GLXContext context;
} GL_STATE_T;

static GL_STATE_T *state;
static GLXFBConfig chosen_fb_config;

#define check() assert(glGetError() == 0)

// Default Open GL context settings: 8-bit truecolor with alpha,
// 24-bit depth buffer, 8-bit stencil, 4 sample MSAA.

static GLint visual_attributes[] = {
    GLX_X_RENDERABLE, True,
    GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
    GLX_RENDER_TYPE, GLX_RGBA_BIT,
    GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
    GLX_RED_SIZE, 8,
    GLX_GREEN_SIZE, 8,
    GLX_BLUE_SIZE, 8,
    GLX_ALPHA_SIZE, 8,
    GLX_DEPTH_SIZE, 24,
    GLX_STENCIL_SIZE, 8,
    GLX_DOUBLEBUFFER, True,
#ifndef NO_MULTI_SAMPLE
    GLX_SAMPLE_BUFFERS, 1,	// Use MSAA
    GLX_SAMPLES, 4,
#endif
    None
};

void CloseGlutWindow() {
}

void sreBackendGLX11::Initialize(int *argc, char ***argv, int window_width, int window_height) {
    // To call GLX functions with glew, we need to call glewInit()
    // first, but it needs an active OpenGL context to be present. So we have to
    // create a temporary GL context.
    glutInit(argc, *argv);
    int glut_window = glutCreateWindow("GLEW Test");
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        /* Problem: glewInit failed, something is seriously wrong. */
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        exit(1);
    }
    fprintf(stdout, "Status: Using GLEW %s.\n", glewGetString(GLEW_VERSION));
//    glutCloseFunc(CloseGlutWindow);
    glutHideWindow();
    glutDestroyWindow(glut_window);

    // Problem: How to completely hide the glut window? It doesn't disappear. Requires
    // freeglut to fix.
    glutMainLoopEvent();

    state = (GL_STATE_T *)malloc(sizeof(GL_STATE_T));
    // Clear application state
    memset(state, 0, sizeof(*state));

    X11OpenDisplay();
    state->XDisplay = (Display *)X11GetDisplay();
//    XDestroyWindow(state->XDisplay, glut_window);

    // Require GLX >= 1.3
    GLint glx_major, glx_minor;
    GLint result = glXQueryVersion(state->XDisplay, &glx_major, &glx_minor);
    if (glx_major < 1 || (glx_major == 1 && glx_minor < 3)) {
        printf("Error: GLX version major reported is %d.%d, need at least 1.3.\n",
            glx_major, glx_minor);
        exit(1);
    }
    printf("GLX version: %d.%d\n", glx_major, glx_minor);

    // Obtain appropriate GLX framebuffer configurations.
    GLint num_config;
    GLXFBConfig *fb_config = glXChooseFBConfig(state->XDisplay, X11GetScreenIndex(),
        visual_attributes, &num_config);
    assert(result != GL_FALSE);
    if (num_config == 0) {
        printf("GLX returned no suitable framebuffer configurations.\n");
        exit(1);
    }
    printf("OpenGL (GLX): %d framebuffer configurations returned.\n", num_config);
    for (int i = 0; i < num_config; i++ ) {
        XVisualInfo *vi = glXGetVisualFromFBConfig(state->XDisplay, fb_config[i]);
        if (vi) {
            int samp_buf, samples;
            glXGetFBConfigAttrib(state->XDisplay, fb_config[i], GLX_SAMPLE_BUFFERS, &samp_buf);
            glXGetFBConfigAttrib(state->XDisplay, fb_config[i], GLX_SAMPLES, &samples);
            printf( "  Matching framebuffer config %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
                " SAMPLES = %d\n", i, vi->visualid, samp_buf, samples);
            XFree(vi);
        }
    }
    chosen_fb_config = fb_config[0];
    XFree(fb_config);

    XVisualInfo *vi = glXGetVisualFromFBConfig(state->XDisplay, chosen_fb_config);
    printf("Chosen visual ID = 0x%x\n", vi->visualid );

    // Create an X window with that visual.
    X11CreateWindow(window_width, window_height, vi, "SRE OpenGL 3.0+ X11 demo");
    XFree(vi);
    state->XWindow = (GLXWindow)X11GetWindow();

    if (!GLXEW_ARB_create_context) {
        // Create GLX rendering context.
        printf("Creating old-style (GLX 1.3) context.\n");
        state->context = glXCreateNewContext(state->XDisplay, chosen_fb_config,
            GLX_RGBA_TYPE, 0, True);
     }
    else {
        int context_attribs[] = {
            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
            //GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
            None
        };
        printf( "Creating OpenGL 3.0 context.\n" );
        state->context = glXCreateContextAttribsARB(state->XDisplay,
            chosen_fb_config, 0, True, context_attribs);
    }
    assert(state->context != NULL);
    check();

    int stencil_bits = 8;
    int depth_bits = 24;

    printf("Opened OpenGL context of size %d x %d with 32-bit pixels, %d-bit depthbuffer and %d-bit stencil.\n", window_width,
        window_height, depth_bits, stencil_bits);
    // Verifying that context is a direct context
    if (!glXIsDirect(state->XDisplay, state->context)) {
        printf("Indirect GLX rendering context obtained.\n");
    }
    else {
        printf("Direct GLX rendering context obtained.\n");
    }

    glXMakeCurrent(state->XDisplay, X11GetWindow(), state->context);
    check();

    sreInitialize(window_width, window_height, sreBackendSwapBuffers);
}

void sreBackendGLX11::Finalize() {
    // Clear screen.
    glClear(GL_COLOR_BUFFER_BIT);
    sre_internal_backend->GLSync();

    glXMakeCurrent(state->XDisplay, 0, NULL);
    glXDestroyContext(state->XDisplay, state->context);
    X11DestroyWindow();
    X11CloseDisplay();
}

void sreBackendGLX11::GLSwapBuffers() {
    glXSwapBuffers(state->XDisplay, state->XWindow);
}

void sreBackendGLX11::GLSync() {
    glXSwapBuffers(state->XDisplay, state->XWindow);
    glXWaitGL();
}

double sreBackendGLX11::GetCurrentTime() {
    return X11GetCurrentTime();
}

void sreBackendGLX11::ProcessGUIEvents() {
    X11ProcessGUIEvents();
}

void sreBackendGLX11::ToggleFullScreenMode(int& width, int& height, bool pan_with_mouse) {
    X11ToggleFullScreenMode(width, height, pan_with_mouse);
}

void sreBackendGLX11::HideCursor() {
    X11HideCursor();
}

void sreBackendGLX11::RestoreCursor() {
    X11RestoreCursor();
}

void sreBackendGLX11::WarpCursor(int x, int y) {
    X11WarpCursor(x, y);
}

<commit_msg>Fix compilation of OpenGL (PC) version<commit_after>/*

Copyright (c) 2014 Harm Hanemaaijer <fgenfb@yahoo.com>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

// X11 (low-level) OpenGL(GLX) interface.
// Uses X11 handling in x11-common.c.
// Currently requires freeglut to get around issues with
// initializing GLEW and destroying atemporary window at
// start-up.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <sys/time.h>
#include <assert.h>
#include <GL/glew.h>
#include <GL/glxew.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>
// We don´t really use glut, but only use it to create a temporary
// rendering context before initializing GLEW.
#include <GL/glut.h>
#include <GL/freeglut_ext.h>

#include "sre.h"
#include "sreBackend.h"
#include "x11-common.h"
#include "gui-common.h"

class sreBackendGLX11 : public sreBackend {
public :
    virtual void Initialize(int *argc, char ***argv, int window_width, int window_height);
    virtual void Finalize();
    virtual void GLSwapBuffers();
    virtual void GLSync();
    virtual double GetCurrentTime();
    virtual void ProcessGUIEvents();
    virtual void ToggleFullScreenMode(int& width, int& height, bool pan_with_mouse);
    virtual void HideCursor();
    virtual void RestoreCursor();
    virtual void WarpCursor(int x, int y);
};

sreBackend *sreCreateBackendGLX11() {
    sreBackend *b = new sreBackendGLX11;
    b->name = "OpenGL 3.0+ X11 (low-level)";
    return b;
}

typedef struct
{
   uint32_t screen_width;
   uint32_t screen_height;
   Display *XDisplay;
   GLXWindow XWindow;
   GLXContext context;
} GL_STATE_T;

static GL_STATE_T *state;
static GLXFBConfig chosen_fb_config;

#define check() assert(glGetError() == 0)

// Default Open GL context settings: 8-bit truecolor with alpha,
// 24-bit depth buffer, 8-bit stencil, 4 sample MSAA.

static GLint visual_attributes[] = {
    GLX_X_RENDERABLE, True,
    GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
    GLX_RENDER_TYPE, GLX_RGBA_BIT,
    GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
    GLX_RED_SIZE, 8,
    GLX_GREEN_SIZE, 8,
    GLX_BLUE_SIZE, 8,
    GLX_ALPHA_SIZE, 8,
    GLX_DEPTH_SIZE, 24,
    GLX_STENCIL_SIZE, 8,
    GLX_DOUBLEBUFFER, True,
#ifndef NO_MULTI_SAMPLE
    GLX_SAMPLE_BUFFERS, 1,	// Use MSAA
    GLX_SAMPLES, 4,
#endif
    None
};

void CloseGlutWindow() {
}

void sreBackendGLX11::Initialize(int *argc, char ***argv, int window_width, int window_height) {
    // To call GLX functions with glew, we need to call glewInit()
    // first, but it needs an active OpenGL context to be present. So we have to
    // create a temporary GL context.
    glutInit(argc, *argv);
    int glut_window = glutCreateWindow("GLEW Test");
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        /* Problem: glewInit failed, something is seriously wrong. */
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        exit(1);
    }
    fprintf(stdout, "Status: Using GLEW %s.\n", glewGetString(GLEW_VERSION));
//    glutCloseFunc(CloseGlutWindow);
    glutHideWindow();
    glutDestroyWindow(glut_window);

    // Problem: How to completely hide the glut window? It doesn't disappear. Requires
    // freeglut to fix.
    glutMainLoopEvent();

    state = (GL_STATE_T *)malloc(sizeof(GL_STATE_T));
    // Clear application state
    memset(state, 0, sizeof(*state));

    X11OpenDisplay();
    state->XDisplay = (Display *)X11GetDisplay();
//    XDestroyWindow(state->XDisplay, glut_window);

    // Require GLX >= 1.3
    GLint glx_major, glx_minor;
    GLint result = glXQueryVersion(state->XDisplay, &glx_major, &glx_minor);
    if (glx_major < 1 || (glx_major == 1 && glx_minor < 3)) {
        printf("Error: GLX version major reported is %d.%d, need at least 1.3.\n",
            glx_major, glx_minor);
        exit(1);
    }
    printf("GLX version: %d.%d\n", glx_major, glx_minor);

    // Obtain appropriate GLX framebuffer configurations.
    GLint num_config;
    GLXFBConfig *fb_config = glXChooseFBConfig(state->XDisplay, X11GetScreenIndex(),
        visual_attributes, &num_config);
    assert(result != GL_FALSE);
    if (num_config == 0) {
        printf("GLX returned no suitable framebuffer configurations.\n");
        exit(1);
    }
    printf("OpenGL (GLX): %d framebuffer configurations returned.\n", num_config);
    for (int i = 0; i < num_config; i++ ) {
        XVisualInfo *vi = glXGetVisualFromFBConfig(state->XDisplay, fb_config[i]);
        if (vi) {
            int samp_buf, samples;
            glXGetFBConfigAttrib(state->XDisplay, fb_config[i], GLX_SAMPLE_BUFFERS, &samp_buf);
            glXGetFBConfigAttrib(state->XDisplay, fb_config[i], GLX_SAMPLES, &samples);
            printf( "  Matching framebuffer config %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
                " SAMPLES = %d\n", i, vi->visualid, samp_buf, samples);
            XFree(vi);
        }
    }
    chosen_fb_config = fb_config[0];
    XFree(fb_config);

    XVisualInfo *vi = glXGetVisualFromFBConfig(state->XDisplay, chosen_fb_config);
    printf("Chosen visual ID = 0x%x\n", vi->visualid );

    // Create an X window with that visual.
    X11CreateWindow(window_width, window_height, vi, "SRE OpenGL 3.0+ X11 demo");
    XFree(vi);
    state->XWindow = (GLXWindow)X11GetWindow();

    if (!GLXEW_ARB_create_context) {
        // Create GLX rendering context.
        printf("Creating old-style (GLX 1.3) context.\n");
        state->context = glXCreateNewContext(state->XDisplay, chosen_fb_config,
            GLX_RGBA_TYPE, 0, True);
     }
    else {
        int context_attribs[] = {
            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
            //GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
            None
        };
        printf( "Creating OpenGL 3.0 context.\n" );
        state->context = glXCreateContextAttribsARB(state->XDisplay,
            chosen_fb_config, 0, True, context_attribs);
    }
    assert(state->context != NULL);
    check();

    int stencil_bits = 8;
    int depth_bits = 24;

    printf("Opened OpenGL context of size %d x %d with 32-bit pixels, %d-bit depthbuffer and %d-bit stencil.\n", window_width,
        window_height, depth_bits, stencil_bits);
    // Verifying that context is a direct context
    if (!glXIsDirect(state->XDisplay, state->context)) {
        printf("Indirect GLX rendering context obtained.\n");
    }
    else {
        printf("Direct GLX rendering context obtained.\n");
    }

    glXMakeCurrent(state->XDisplay, X11GetWindow(), state->context);
    check();

    sreInitialize(window_width, window_height, sreBackendGLSwapBuffers);
}

void sreBackendGLX11::Finalize() {
    // Clear screen.
    glClear(GL_COLOR_BUFFER_BIT);
    sre_internal_backend->GLSync();

    glXMakeCurrent(state->XDisplay, 0, NULL);
    glXDestroyContext(state->XDisplay, state->context);
    X11DestroyWindow();
    X11CloseDisplay();
}

void sreBackendGLX11::GLSwapBuffers() {
    glXSwapBuffers(state->XDisplay, state->XWindow);
}

void sreBackendGLX11::GLSync() {
    glXSwapBuffers(state->XDisplay, state->XWindow);
    glXWaitGL();
}

double sreBackendGLX11::GetCurrentTime() {
    return X11GetCurrentTime();
}

void sreBackendGLX11::ProcessGUIEvents() {
    X11ProcessGUIEvents();
}

void sreBackendGLX11::ToggleFullScreenMode(int& width, int& height, bool pan_with_mouse) {
    X11ToggleFullScreenMode(width, height, pan_with_mouse);
}

void sreBackendGLX11::HideCursor() {
    X11HideCursor();
}

void sreBackendGLX11::RestoreCursor() {
    X11RestoreCursor();
}

void sreBackendGLX11::WarpCursor(int x, int y) {
    X11WarpCursor(x, y);
}

<|endoftext|>


<commit_before>/***********************************************************************
    filename:   SVGDataManager.cpp
    created:    30th July 2013
    author:     Lukas Meindl
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2013 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/

#include "CEGUI/svg/SVGDataManager.h"
#include "CEGUI/svg/SVGData.h"
#include "CEGUI/Exceptions.h"
#include "CEGUI/Logger.h"

namespace CEGUI
{
//----------------------------------------------------------------------------//
// Singleton instance pointer
template<> SVGDataManager* Singleton<SVGDataManager>::ms_Singleton = 0;

SVGDataManager::SVGDataManager()
{

}

SVGDataManager::~SVGDataManager()
{

}

/*************************************************************************
Return singleton object
*************************************************************************/
SVGDataManager&	SVGDataManager::getSingleton(void)
{
	return Singleton<SVGDataManager>::getSingleton();
}

/*************************************************************************
	Return singleton pointer
*************************************************************************/
SVGDataManager*	SVGDataManager::getSingletonPtr(void)
{
	return Singleton<SVGDataManager>::getSingletonPtr();
}

SVGData& SVGDataManager::create(const String& name)
{
    if (d_svgDataMap.find(name) != d_svgDataMap.end())
        CEGUI_THROW(AlreadyExistsException(
        "Image already exists: " + name));

    SVGData* svg_data = CEGUI_NEW_AO SVGData();
    d_svgDataMap[name] = svg_data;

    char addr_buff[32];
    sprintf(addr_buff, "%p", static_cast<void*>(&svg_data));

    Logger::getSingleton().logEvent(
        "[ImageManager] Created image: '" + name + "' (" + addr_buff +
        ")");

    return *svg_data;
}

}
<commit_msg>MOD: Output changes<commit_after>/***********************************************************************
    filename:   SVGDataManager.cpp
    created:    30th July 2013
    author:     Lukas Meindl
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2013 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/

#include "CEGUI/svg/SVGDataManager.h"
#include "CEGUI/svg/SVGData.h"
#include "CEGUI/Exceptions.h"
#include "CEGUI/Logger.h"

namespace CEGUI
{
//----------------------------------------------------------------------------//
// Singleton instance pointer
template<> SVGDataManager* Singleton<SVGDataManager>::ms_Singleton = 0;

//----------------------------------------------------------------------------//
SVGDataManager::SVGDataManager()
{

}

//----------------------------------------------------------------------------//
SVGDataManager::~SVGDataManager()
{

}

//----------------------------------------------------------------------------//
SVGDataManager&	SVGDataManager::getSingleton(void)
{
	return Singleton<SVGDataManager>::getSingleton();
}

//----------------------------------------------------------------------------//
SVGDataManager*	SVGDataManager::getSingletonPtr(void)
{
	return Singleton<SVGDataManager>::getSingletonPtr();
}

//----------------------------------------------------------------------------//
SVGData& SVGDataManager::create(const String& name)
{
    if (d_svgDataMap.find(name) != d_svgDataMap.end())
        CEGUI_THROW(AlreadyExistsException(
        "SVGData already exists: " + name));

    SVGData* svg_data = CEGUI_NEW_AO SVGData();
    d_svgDataMap[name] = svg_data;

    char addr_buff[32];
    sprintf(addr_buff, "%p", static_cast<void*>(&svg_data));

    Logger::getSingleton().logEvent(
        "[SVGDataManager] Created SVGData: '" + name + "' (" + addr_buff +
        ")");

    return *svg_data;
}

//----------------------------------------------------------------------------//
}
<|endoftext|>