clippers 0.1.2

Cross-platform clipboard management library
Documentation
#include <wrapper.hpp>

template <typename T> uint8_t* _imp_copy_image(const clip::image& img, const size_t pixel_size = sizeof(T)) {
	const clip::image_spec& spec = img.spec();
	uint8_t* rgba = new uint8_t[spec.width * spec.height * 4];

	for (unsigned long y = 0; y < spec.height; ++y) {
		const char* p = img.data() + (y * spec.bytes_per_row);
		for (unsigned long x = 0; x < spec.width; ++x, p += pixel_size) {
			uint8_t* rgba_pixel = rgba + (y * spec.width + x) * 4;

			T pixel = 0;
			std::memcpy(&pixel, p, pixel_size);

			rgba_pixel[0] = (uint8_t)((pixel & spec.red_mask) >> spec.red_shift);
			rgba_pixel[1] = (uint8_t)((pixel & spec.green_mask) >> spec.green_shift);
			rgba_pixel[2] = (uint8_t)((pixel & spec.blue_mask) >> spec.blue_shift);

			if (spec.alpha_mask) {
				rgba_pixel[3] = (uint8_t)((pixel & spec.alpha_mask) >> spec.alpha_shift);
			} else {
				rgba_pixel[3] = 255;
			}
		}
	}

	return rgba;
}

uint8_t* copy_image(const clip::image& img) {
	switch (img.spec().bits_per_pixel) {
		case 8:  return _imp_copy_image<uint8_t>(img);
		case 16: return _imp_copy_image<uint16_t>(img);
		case 24: return _imp_copy_image<uint32_t>(img, 3);
		case 32: return _imp_copy_image<uint32_t>(img);
		case 64: return _imp_copy_image<uint64_t>(img);
		default: return nullptr;
	}
}

extern "C" TaggedClipperData clipper_get_tagged_data() {
	clip::lock l;

	if (!l.locked()) return { ClipperDataTagEmpty, {} };

	{
		clip::image img;
		if (l.get_image(img)) {
			uint8_t* rgba = copy_image(img);
			if (rgba) {
				ClipperData data;
				data.image.rgba = rgba;
				data.image.width = img.spec().width;
				data.image.height = img.spec().height;
				return { ClipperDataTagImage, data };
			} else {
				return { ClipperDataTagEmpty, {} };
			}
		}
	}

	{
		size_t text_length = l.get_data_length(clip::text_format());
		if (text_length > 0) {
			ClipperData data;

			char* text = new char[text_length];
			l.get_data(clip::text_format(), text, text_length);
			data.text.text = text;
			data.text.length = text_length;

			if (text[text_length - 1] == '\0') {
				if (text_length == 1) {
					delete[] text;
					return { ClipperDataTagEmpty, {} };
				} else {
					data.text.length -= 1;
				}
			}

			return { ClipperDataTagText, data };
		}
	}

	return { ClipperDataTagEmpty, {} };
}

extern "C" ClipperSetClipboardResult clipper_set_tagged_data(TaggedClipperData tagged) {
	clip::lock l;

	if (!l.locked()) return ClipperSetClipboardResultLocked;
	if (!l.clear()) return ClipperSetClipboardResultGenericFailure;

	bool ok = false;

	switch (tagged.tag) {
		case ClipperDataTagEmpty: {
			return ClipperSetClipboardResultOk;
		}
		case ClipperDataTagText: {
			ok = l.set_data(clip::text_format(), tagged.data.text.text, tagged.data.text.length);
			break;
		}
		case ClipperDataTagImage: {
			clip::image_spec spec;
			spec.width = tagged.data.image.width;
			spec.height = tagged.data.image.height;
			spec.bits_per_pixel = 32;
			spec.bytes_per_row = spec.width * 4;
			spec.red_mask = 0x000000FF;
			spec.green_mask = 0x0000FF00;
			spec.blue_mask = 0x00FF0000;
			spec.alpha_mask = 0xFF000000;
			spec.red_shift = 0;
			spec.green_shift = 8;
			spec.blue_shift = 16;
			spec.alpha_shift = 24;

			clip::image img(static_cast<const void*>(tagged.data.image.rgba), spec);
			ok = l.set_image(img);
			break;
		}
	}

	return ok ? ClipperSetClipboardResultOk : ClipperSetClipboardResultGenericFailure;
}

extern "C" void clipper_free_tagged_data(TaggedClipperData tagged) {
	if (tagged.tag == ClipperDataTagText) {
		delete[] tagged.data.text.text;
	} else if (tagged.tag == ClipperDataTagImage) {
		delete[] tagged.data.image.rgba;
	}
}