diff --git a/base/CUnicode.cpp b/base/CUnicode.cpp new file mode 100644 index 00000000..48834192 --- /dev/null +++ b/base/CUnicode.cpp @@ -0,0 +1,530 @@ +#include "CUnicode.h" +#include + +// +// local utility functions +// + +inline +static +UInt16 +decode16(const UInt8* n) +{ + union x16 { + UInt8 n8[2]; + UInt16 n16; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + return c.n16; +} + +inline +static +UInt32 +decode32(const UInt8* n) +{ + union x32 { + UInt8 n8[4]; + UInt32 n32; + } c; + c.n8[0] = n[0]; + c.n8[1] = n[1]; + c.n8[2] = n[2]; + c.n8[3] = n[3]; + return c.n32; +} + +// +// CUnicode +// + +UInt32 CUnicode::s_invalid = 0x0000ffff; + +CString +CUnicode::UTF8ToUCS2(const CString& src) +{ + // get size of input string and reserve some space in output. + // include UTF8's nul terminator. + UInt32 n = src.size() + 1; + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c != s_invalid && c < 0x0000ffff) { + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUCS4(const CString& src) +{ + // get size of input string and reserve some space in output. + // include UTF8's nul terminator. + UInt32 n = src.size() + 1; + CString dst; + dst.reserve(4 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c != s_invalid) { + dst.append(reinterpret_cast(&c), 4); + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF16(const CString& src) +{ + // get size of input string and reserve some space in output. + // include UTF8's nul terminator. + UInt32 n = src.size() + 1; + CString dst; + dst.reserve(2 * n); + + // convert each character + const UInt8* data = reinterpret_cast(src.c_str()); + while (n > 0) { + UInt32 c = fromUTF8(data, n); + if (c != s_invalid && c < 0x0010ffff) { + if (c < 0x00010000) { + UInt16 ucs2 = static_cast(c); + dst.append(reinterpret_cast(&ucs2), 2); + } + else { + c -= 0x00010000; + UInt16 utf16h = static_cast(c >> 10) + 0xd800; + UInt16 utf16l = (static_cast(c) & 0x03ff) + 0xdc00; + dst.append(reinterpret_cast(&utf16h), 2); + dst.append(reinterpret_cast(&utf16l), 2); + } + } + } + + return dst; +} + +CString +CUnicode::UTF8ToUTF32(const CString& src) +{ + // FIXME -- should ensure dst has no characters over U-0010FFFF + return UTF8ToUCS4(src); +} + +CString +CUnicode::UCS2ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 1; + return doUCS2ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UCS4ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 2; + return doUCS4ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UTF16ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 1; + return doUTF16ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UTF32ToUTF8(const CString& src) +{ + UInt32 n = src.size() >> 2; + return doUTF32ToUTF8(reinterpret_cast(src.data()), n); +} + +CString +CUnicode::UTF8ToText(const CString& src) +{ + // convert to wide char + wchar_t* tmp = UTF8ToWideChar(src); + + // get length of multibyte string + mbstate_t state; + memset(&state, 0, sizeof(state)); + const wchar_t* scratch = tmp; + size_t len = wcsrtombs(NULL, &scratch, 0, &state); + if (len == (size_t)-1) { + // invalid character in src + delete[] tmp; + return CString(); + } + + // convert to multibyte + scratch = tmp; + char* dst = new char[len + 1]; + wcsrtombs(dst, &scratch, len + 1, &state); + CString text(dst); + + // clean up + delete[] dst; + delete[] tmp; + + return text; +} + +CString +CUnicode::textToUTF8(const CString& src) +{ + // get length of wide char string + mbstate_t state; + memset(&state, 0, sizeof(state)); + const char* scratch = src.c_str(); + size_t len = mbsrtowcs(NULL, &scratch, 0, &state); + if (len == (size_t)-1) { + // invalid character in src + return CString(); + } + + // convert multibyte to wide char + scratch = src.c_str(); + wchar_t* dst = new wchar_t[len + 1]; + mbsrtowcs(dst, &scratch, len + 1, &state); + + // convert to UTF8 + CString utf8 = wideCharToUTF8(dst); + + // clean up + delete[] dst; + + return utf8; +} + +wchar_t* +CUnicode::UTF8ToWideChar(const CString& src) +{ + // convert to platform's wide character encoding. + // note -- this must include a wide nul character (independent of + // the CString's nul character). +#if WINDOWS_LIKE + CString tmp = UTF8ToUCS16(src); + UInt32 size = tmp.size() >> 1; +#elif UNIX_LIKE + CString tmp = UTF8ToUCS4(src); + UInt32 size = tmp.size() >> 2; +#endif + + // copy to a wchar_t array + wchar_t* dst = new wchar_t[size]; + ::memcpy(dst, src.data(), sizeof(wchar_t) * size); + return dst; +} + +CString +CUnicode::wideCharToUTF8(const wchar_t* src) +{ + // convert from platform's wide character encoding. + // note -- this must include a wide nul character (independent of + // the CString's nul character). +#if WINDOWS_LIKE + return doUCS16ToUTF8(reinterpret_cast(src), wcslen(src)); +#elif UNIX_LIKE + return doUCS4ToUTF8(reinterpret_cast(src), wcslen(src)); +#endif +} + +CString +CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + toUTF8(dst, c); + } + + // remove extra trailing nul + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { + dst.resize(dst.size() - 1); + } + + return dst; +} + +CString +CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 4, --n) { + UInt32 c = decode32(data); + toUTF8(dst, c); + } + + // remove extra trailing nul + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { + dst.resize(dst.size() - 1); + } + + return dst; +} + +CString +CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n) +{ + // make some space + CString dst; + dst.reserve(n); + + // convert each character + for (; n > 0; data += 2, --n) { + UInt32 c = decode16(data); + if (c < 0x0000d800 || c > 0x0000dfff) { + toUTF8(dst, c); + } + else if (n == 1) { + // error -- missing second word + } + else if (c >= 0x0000d800 && c <= 0x0000dbff) { + UInt32 c2 = decode16(data); + data += 2; + --n; + if (c2 < 0x0000dc00 || c2 > 0x0000dfff) { + // error -- [d800,dbff] not followed by [dc00,dfff] + } + else { + c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000; + toUTF8(dst, c); + } + } + else { + // error -- [dc00,dfff] without leading [d800,dbff] + } + } + + // remove extra trailing nul + if (dst.size() > 0 && dst[dst.size() - 1] == '\0') { + dst.resize(dst.size() - 1); + } + + return dst; +} + +CString +CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n) +{ + // FIXME -- should check that src has no characters over U-0010FFFF + return doUCS4ToUTF8(data, n); +} + +UInt32 +CUnicode::fromUTF8(const UInt8*& data, UInt32& n) +{ + assert(data != NULL); + assert(n != 0); + + // compute character encoding length, checking for overlong + // sequences (i.e. characters that don't use the shortest + // possible encoding). + UInt32 size; + if (data[0] < 0x80) { + // 0xxxxxxx + size = 1; + } + else if (data[0] < 0xc0) { + // 10xxxxxx -- in the middle of a multibyte character. skip + // until we find a start byte and return error. + do { + --n; + ++data; + } while (n > 0 && (data[0] & 0xc0) == 0x80); + return s_invalid; + } + else if (data[0] < 0xe0) { + // 110xxxxx + size = 2; + } + else if (data[0] < 0xf0) { + // 1110xxxx + size = 3; + } + else if (data[0] < 0xf8) { + // 11110xxx + size = 4; + } + else if (data[0] < 0xfc) { + // 111110xx + size = 5; + } + else if (data[0] < 0xfe) { + // 1111110x + size = 6; + } + else { + // invalid sequence. dunno how many bytes to skip so skip one. + --n; + ++data; + return s_invalid; + } + + // make sure we have enough data + if (size > n) { + data += n; + n = 0; + return s_invalid; + } + + // extract character + UInt32 c; + switch (size) { + case 1: + c = static_cast(data[0]); + break; + + case 2: + c = ((static_cast(data[0]) & 0x1f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 3: + c = ((static_cast(data[0]) & 0x0f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[2]) & 0x3f) ); + break; + + case 4: + c = ((static_cast(data[0]) & 0x07) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 5: + c = ((static_cast(data[0]) & 0x03) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + case 6: + c = ((static_cast(data[0]) & 0x01) << 30) | + ((static_cast(data[1]) & 0x3f) << 24) | + ((static_cast(data[1]) & 0x3f) << 18) | + ((static_cast(data[1]) & 0x3f) << 12) | + ((static_cast(data[1]) & 0x3f) << 6) | + ((static_cast(data[1]) & 0x3f) ); + break; + + default: + assert(0 && "invalid size"); + } + + // update parameters + data += size; + n -= size; + + // check for characters that didn't use the smallest possible encoding + static UInt32 s_minChar[] = { + 0, + 0x00000000, + 0x00000080, + 0x00000800, + 0x00010000, + 0x00200000, + 0x04000000 + }; + if (c < s_minChar[size]) { + return s_invalid; + } + + // check that all bytes after the first have the pattern 10xxxxxx. + UInt8 a = 0x80; + switch (size) { + case 6: + a |= data[5]; + // fall through + + case 5: + a |= data[4]; + // fall through + + case 4: + a |= data[3]; + // fall through + + case 3: + a |= data[2]; + // fall through + + case 2: + a |= data[1]; + } + if ((a & 0xc0) != 0x80) { + return s_invalid; + } + + return c; +} + +void +CUnicode::toUTF8(CString& dst, const UInt32 c) +{ + UInt8 data[6]; + + if (c < 0x00000080) { + data[0] = static_cast(c); + dst.append(reinterpret_cast(data), 1); + } + else if (c < 0x00000800) { + data[0] = static_cast((c >> 6) & 0x0000001f) + 0xc0; + data[1] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 2); + } + else if (c < 0x00010000) { + data[0] = static_cast((c >> 12) & 0x0000000f) + 0xe0; + data[1] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[2] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 3); + } + else if (c < 0x00200000) { + data[0] = static_cast((c >> 18) & 0x00000007) + 0xf0; + data[1] = static_cast((c >> 12) & 0x0000003f) + 0x80; + data[2] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[3] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 4); + } + else if (c < 0x04000000) { + data[0] = static_cast((c >> 24) & 0x00000003) + 0xf8; + data[1] = static_cast((c >> 18) & 0x0000003f) + 0x80; + data[2] = static_cast((c >> 12) & 0x0000003f) + 0x80; + data[3] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[4] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 5); + } + else if (c < 0x80000000) { + data[0] = static_cast((c >> 30) & 0x00000001) + 0xfc; + data[1] = static_cast((c >> 24) & 0x0000003f) + 0x80; + data[2] = static_cast((c >> 18) & 0x0000003f) + 0x80; + data[3] = static_cast((c >> 12) & 0x0000003f) + 0x80; + data[4] = static_cast((c >> 6) & 0x0000003f) + 0x80; + data[5] = static_cast(c & 0x0000003f) + 0x80; + dst.append(reinterpret_cast(data), 6); + } + else { + // invalid character + } +} diff --git a/base/CUnicode.h b/base/CUnicode.h new file mode 100644 index 00000000..4bcabe60 --- /dev/null +++ b/base/CUnicode.h @@ -0,0 +1,48 @@ +#ifndef CUNICODE_H +#define CUNICODE_H + +#include "CString.h" +#include "BasicTypes.h" +#include + +class CUnicode { +public: + static CString UTF8ToUCS2(const CString&); + static CString UTF8ToUCS4(const CString&); + static CString UTF8ToUTF16(const CString&); + static CString UTF8ToUTF32(const CString&); + + static CString UCS2ToUTF8(const CString&); + static CString UCS4ToUTF8(const CString&); + static CString UTF16ToUTF8(const CString&); + static CString UTF32ToUTF8(const CString&); + + // convert UTF-8 to/from the current locale's encoding + static CString UTF8ToText(const CString&); + static CString textToUTF8(const CString&); + +private: + // convert UTF8 to nul terminated wchar_t string (using whatever + // encoding is native to the platform). caller must delete[] + // the returned string. + static wchar_t* UTF8ToWideChar(const CString&); + + // convert nul terminated wchar_t string (in platform's native + // encoding) to UTF8. + static CString wideCharToUTF8(const wchar_t*); + + // internal conversion to UTF8 + static CString doUCS2ToUTF8(const UInt8* src, UInt32 n); + static CString doUCS4ToUTF8(const UInt8* src, UInt32 n); + static CString doUTF16ToUTF8(const UInt8* src, UInt32 n); + static CString doUTF32ToUTF8(const UInt8* src, UInt32 n); + + // convert characters to/from UTF8 + static UInt32 fromUTF8(const UInt8*& src, UInt32& size); + static void toUTF8(CString& dst, const UInt32 c); + +private: + static UInt32 s_invalid; +}; + +#endif diff --git a/base/Makefile.am b/base/Makefile.am index 07ee18bc..fe67d1ce 100644 --- a/base/Makefile.am +++ b/base/Makefile.am @@ -8,12 +8,14 @@ libbase_a_SOURCES = \ CLog.cpp \ CStopwatch.cpp \ CString.cpp \ + CUnicode.cpp \ XBase.cpp \ BasicTypes.h \ CFunctionJob.h \ CLog.h \ CStopwatch.h \ CString.h \ + CUnicode.h \ IInterface.h \ IJob.h \ TMethodJob.h \ diff --git a/platform/CXWindowsClipboard.cpp b/platform/CXWindowsClipboard.cpp index c2cd225e..a0f0de55 100644 --- a/platform/CXWindowsClipboard.cpp +++ b/platform/CXWindowsClipboard.cpp @@ -1,4 +1,7 @@ #include "CXWindowsClipboard.h" +#include "CXWindowsClipboardTextConverter.h" +#include "CXWindowsClipboardUCS2Converter.h" +#include "CXWindowsClipboardUTF8Converter.h" #include "CXWindowsUtil.h" #include "CThread.h" #include "CLog.h" @@ -52,6 +55,20 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, break; } + // add converters + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "text/plain;charset=UTF-8")); + m_converters.push_back(new CXWindowsClipboardUTF8Converter(m_display, + "UTF8_STRING")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/plain;charset=ISO-10646-UCS-2")); + m_converters.push_back(new CXWindowsClipboardUCS2Converter(m_display, + "text/unicode")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "text/plain")); + m_converters.push_back(new CXWindowsClipboardTextConverter(m_display, + "STRING")); + // we have no data clearCache(); } @@ -59,6 +76,7 @@ CXWindowsClipboard::CXWindowsClipboard(Display* display, CXWindowsClipboard::~CXWindowsClipboard() { clearReplies(); + clearConverters(); } void @@ -124,7 +142,7 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, // handle targets CString data; - Atom type = None; + Atom type = None; int format = 0; if (target == m_atomTargets) { type = getTargetsData(data, &format); @@ -132,9 +150,17 @@ CXWindowsClipboard::addSimpleRequest(Window requestor, else if (target == m_atomTimestamp) { type = getTimestampData(data, &format); } - else if (target == m_atomString || - target == m_atomText) { - type = getStringData(data, &format); + else { + IXWindowsClipboardConverter* converter = getConverter(target); + IClipboard::EFormat clipboardFormat = converter->getFormat(); + if (!m_added[clipboardFormat]) { + type = None; + } + else { + type = converter->getAtom(); + format = converter->getDataSize(); + data = converter->fromIClipboard(m_data[clipboardFormat]); + } } if (type != None) { @@ -334,17 +360,41 @@ CXWindowsClipboard::get(EFormat format) const return m_data[format]; } -IClipboard::EFormat -CXWindowsClipboard::getFormat(Atom src) const +void +CXWindowsClipboard::clearConverters() { - // FIXME -- handle more formats (especially mime-type-like formats - // and various character encodings like unicode). - if (src == m_atomString || - src == m_atomText /*|| - src == m_atomCompoundText*/) { - return IClipboard::kText; + for (ConverterList::iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + delete *index; } - return IClipboard::kNumFormats; + m_converters.clear(); +} + +IXWindowsClipboardConverter* +CXWindowsClipboard::getConverter(Atom target, bool onlyIfNotAdded) const +{ + IXWindowsClipboardConverter* converter = NULL; + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getAtom() == target) { + break; + } + } + if (converter == NULL) { + log((CLOG_DEBUG1 " no converter for target %d", target)); + return NULL; + } + + // optionally skip already handled targets + if (onlyIfNotAdded) { + if (m_added[converter->getFormat()]) { + log((CLOG_DEBUG1 " skipping handled format %d", converter->getFormat())); + return NULL; + } + } + + return converter; } void @@ -438,44 +488,29 @@ CXWindowsClipboard::icccmFillCache() const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); for (UInt32 i = 0; i < numTargets; ++i) { - // determine the expected clipboard format + // see if we have a converter for this target Atom target = targets[i]; - IClipboard::EFormat expectedFormat = getFormat(target); - if (expectedFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target %d", target)); - continue; - } - log((CLOG_DEBUG1 " source target %d -> %d", target, expectedFormat)); - - // skip already handled targets - if (m_added[expectedFormat]) { - log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); + IXWindowsClipboardConverter* converter = getConverter(target, true); + if (converter == NULL) { continue; } + // get the data Atom actualTarget; CString targetData; if (!icccmGetSelection(target, &actualTarget, &targetData)) { log((CLOG_DEBUG1 " no data for target", target)); continue; } - logc(actualTarget != target, (CLOG_DEBUG1 " actual target is %d", actualTarget)); - - // use the actual format, not the expected - IClipboard::EFormat actualFormat = getFormat(actualTarget); - if (actualFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target %d", actualTarget)); - continue; - } - if (m_added[actualFormat]) { - log((CLOG_DEBUG1 " skipping handled format %d", actualFormat)); + if (actualTarget != target) { + log((CLOG_DEBUG1 " expected (%d) and actual (%d) targets differ", target, actualTarget)); continue; } // add to clipboard and note we've done it - m_data[actualFormat] = targetData; - m_added[actualFormat] = true; - log((CLOG_DEBUG " added format %d for target %d", actualFormat, target)); + m_data[converter->getFormat()] = converter->toIClipboard(targetData); + m_added[converter->getFormat()] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } @@ -676,42 +711,36 @@ CXWindowsClipboard::motifFillCache() continue; } - // determine the expected clipboard format - Atom target = motifFormat->m_type; - IClipboard::EFormat expectedFormat = getFormat(target); - if (expectedFormat == IClipboard::kNumFormats) { - log((CLOG_DEBUG1 " no format for target %d", target)); - continue; - } - log((CLOG_DEBUG1 " source target %d -> %d", target, expectedFormat)); - // skip already handled targets - if (m_added[expectedFormat]) { - log((CLOG_DEBUG1 " skipping handled format %d", expectedFormat)); + // see if we have a converter for this target + Atom target = motifFormat->m_type; + IXWindowsClipboardConverter* converter = getConverter(target, true); + if (converter == NULL) { continue; } // get the data (finally) SInt32 length = motifFormat->m_length; sprintf(name, "_MOTIF_CLIP_ITEM_%d", motifFormat->m_data); - Atom atomData = XInternAtom(m_display, name, False); - data = ""; + Atom atomData = XInternAtom(m_display, name, False), atomTarget; + CString targetData; if (!CXWindowsUtil::getWindowProperty(m_display, root, - atomData, &data, - &target, &format, False)) { + atomData, &targetData, + &atomTarget, &format, False)) { + log((CLOG_DEBUG1 " no data for target", target)); continue; } - if (target != atomData) { + if (atomTarget != atomData) { continue; } // truncate data to length specified in the format - data.erase(length); + targetData.erase(length); // add to clipboard and note we've done it - m_data[expectedFormat] = data; - m_added[expectedFormat] = true; - log((CLOG_DEBUG " added format %d for target %d", expectedFormat, motifFormat->m_type)); + m_data[converter->getFormat()] = converter->toIClipboard(targetData); + m_added[converter->getFormat()] = true; + log((CLOG_DEBUG " added format %d for target %d", converter->getFormat(), target)); } } @@ -1061,7 +1090,7 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const { assert(format != NULL); - // construct response + // add standard targets Atom atom; atom = m_atomTargets; data.append(reinterpret_cast(&atom), sizeof(Atom)); @@ -1069,11 +1098,17 @@ CXWindowsClipboard::getTargetsData(CString& data, int* format) const data.append(reinterpret_cast(&atom), sizeof(Atom)); atom = m_atomTimestamp; data.append(reinterpret_cast(&atom), sizeof(Atom)); - if (m_added[kText]) { - atom = m_atomString; - data.append(reinterpret_cast(&atom), sizeof(Atom)); - atom = m_atomText; - data.append(reinterpret_cast(&atom), sizeof(Atom)); + + // add targets we can convert to + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + IXWindowsClipboardConverter* converter = *index; + + // skip formats we don't have + if (!m_added[converter->getFormat()]) { + atom = converter->getAtom(); + data.append(reinterpret_cast(&atom), sizeof(Atom)); + } } *format = 32; @@ -1092,21 +1127,6 @@ CXWindowsClipboard::getTimestampData(CString& data, int* format) const return m_atomTimestamp; } -Atom -CXWindowsClipboard::getStringData(CString& data, int* format) const -{ - assert(format != NULL); - - if (m_added[kText]) { - data = m_data[kText]; - *format = 8; - return m_atomString; - } - else { - return None; - } -} - // // CXWindowsClipboard::CICCCMGetClipboard diff --git a/platform/CXWindowsClipboard.h b/platform/CXWindowsClipboard.h index 940ab71c..e738b07e 100644 --- a/platform/CXWindowsClipboard.h +++ b/platform/CXWindowsClipboard.h @@ -5,12 +5,15 @@ #include "ClipboardTypes.h" #include "stdmap.h" #include "stdlist.h" +#include "stdvector.h" #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy #else # include #endif +class IXWindowsClipboardConverter; + class CXWindowsClipboard : public IClipboard { public: CXWindowsClipboard(Display*, Window, ClipboardID); @@ -51,6 +54,17 @@ public: virtual CString get(EFormat) const; private: + // remove all converters from our list + void clearConverters(); + + // get the converter for a clipboard format. returns NULL if no + // suitable converter. iff onlyIfNotAdded is true then also + // return NULL if a suitable converter was found but we already + // have data of the converter's clipboard format. + IXWindowsClipboardConverter* + getConverter(Atom target, + bool onlyIfNotAdded = false) const; + // convert target atom to clipboard format EFormat getFormat(Atom target) const; @@ -218,9 +232,10 @@ private: // data conversion methods Atom getTargetsData(CString&, int* format) const; Atom getTimestampData(CString&, int* format) const; - Atom getStringData(CString&, int* format) const; private: + typedef std::vector ConverterList; + Display* m_display; Window m_window; ClipboardID m_id; @@ -245,6 +260,9 @@ private: CReplyMap m_replies; CReplyEventMask m_eventMasks; + // clipboard format converters + ConverterList m_converters; + // atoms we'll need Atom m_atomTargets; Atom m_atomMultiple; @@ -263,4 +281,31 @@ private: Atom m_atomGDKSelection; }; +class IXWindowsClipboardConverter : public IInterface { +public: + // accessors + + // return the clipboard format this object converts from/to + virtual IClipboard::EFormat + getFormat() const = 0; + + // return the atom representing the X selection format that + // this object converts from/to + virtual Atom getAtom() const = 0; + + // return the size (in bits) of data elements returned by + // toIClipboard(). + virtual int getDataSize() const = 0; + + // convert from the IClipboard format to the X selection format. + // the input data must be in the IClipboard format returned by + // getFormat(). the return data will be in the X selection + // format returned by getAtom(). + virtual CString fromIClipboard(const CString&) const = 0; + + // convert from the X selection format to the IClipboard format + // (i.e., the reverse of fromIClipboard()). + virtual CString toIClipboard(const CString&) const = 0; +}; + #endif diff --git a/platform/CXWindowsClipboardTextConverter.cpp b/platform/CXWindowsClipboardTextConverter.cpp new file mode 100644 index 00000000..be3b8c93 --- /dev/null +++ b/platform/CXWindowsClipboardTextConverter.cpp @@ -0,0 +1,48 @@ +#include "CXWindowsClipboardTextConverter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardTextConverter +// + +CXWindowsClipboardTextConverter::CXWindowsClipboardTextConverter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardTextConverter::~CXWindowsClipboardTextConverter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardTextConverter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardTextConverter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardTextConverter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardTextConverter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToText(data); +} + +CString +CXWindowsClipboardTextConverter::toIClipboard(const CString& data) const +{ + return CUnicode::textToUTF8(data); +} diff --git a/platform/CXWindowsClipboardTextConverter.h b/platform/CXWindowsClipboardTextConverter.h new file mode 100644 index 00000000..b73e06a5 --- /dev/null +++ b/platform/CXWindowsClipboardTextConverter.h @@ -0,0 +1,23 @@ +#ifndef CXWINDOWSCLIPBOARDTEXTCONVERTER_H +#define CXWINDOWSCLIPBOARDTEXTCONVERTER_H + +#include "CXWindowsClipboard.h" + +class CXWindowsClipboardTextConverter : public IXWindowsClipboardConverter { +public: + CXWindowsClipboardTextConverter(Display* display, const char* name); + virtual ~CXWindowsClipboardTextConverter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/platform/CXWindowsClipboardUCS2Converter.cpp b/platform/CXWindowsClipboardUCS2Converter.cpp new file mode 100644 index 00000000..1f892f55 --- /dev/null +++ b/platform/CXWindowsClipboardUCS2Converter.cpp @@ -0,0 +1,48 @@ +#include "CXWindowsClipboardUCS2Converter.h" +#include "CUnicode.h" + +// +// CXWindowsClipboardUCS2Converter +// + +CXWindowsClipboardUCS2Converter::CXWindowsClipboardUCS2Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUCS2Converter::~CXWindowsClipboardUCS2Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUCS2Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUCS2Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUCS2Converter::getDataSize() const +{ + return 16; +} + +CString +CXWindowsClipboardUCS2Converter::fromIClipboard(const CString& data) const +{ + return CUnicode::UTF8ToUCS2(data); +} + +CString +CXWindowsClipboardUCS2Converter::toIClipboard(const CString& data) const +{ + return CUnicode::UCS2ToUTF8(data); +} diff --git a/platform/CXWindowsClipboardUCS2Converter.h b/platform/CXWindowsClipboardUCS2Converter.h new file mode 100644 index 00000000..1a87c84d --- /dev/null +++ b/platform/CXWindowsClipboardUCS2Converter.h @@ -0,0 +1,23 @@ +#ifndef CXWINDOWSCLIPBOARDUCS2CONVERTER_H +#define CXWINDOWSCLIPBOARDUCS2CONVERTER_H + +#include "CXWindowsClipboard.h" + +class CXWindowsClipboardUCS2Converter : public IXWindowsClipboardConverter { +public: + CXWindowsClipboardUCS2Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUCS2Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/platform/CXWindowsClipboardUTF8Converter.cpp b/platform/CXWindowsClipboardUTF8Converter.cpp new file mode 100644 index 00000000..c461a321 --- /dev/null +++ b/platform/CXWindowsClipboardUTF8Converter.cpp @@ -0,0 +1,47 @@ +#include "CXWindowsClipboardUTF8Converter.h" + +// +// CXWindowsClipboardUTF8Converter +// + +CXWindowsClipboardUTF8Converter::CXWindowsClipboardUTF8Converter( + Display* display, const char* name) : + m_atom(XInternAtom(display, name, False)) +{ + // do nothing +} + +CXWindowsClipboardUTF8Converter::~CXWindowsClipboardUTF8Converter() +{ + // do nothing +} + +IClipboard::EFormat +CXWindowsClipboardUTF8Converter::getFormat() const +{ + return IClipboard::kText; +} + +Atom +CXWindowsClipboardUTF8Converter::getAtom() const +{ + return m_atom; +} + +int +CXWindowsClipboardUTF8Converter::getDataSize() const +{ + return 8; +} + +CString +CXWindowsClipboardUTF8Converter::fromIClipboard(const CString& data) const +{ + return data; +} + +CString +CXWindowsClipboardUTF8Converter::toIClipboard(const CString& data) const +{ + return data; +} diff --git a/platform/CXWindowsClipboardUTF8Converter.h b/platform/CXWindowsClipboardUTF8Converter.h new file mode 100644 index 00000000..ed7313a1 --- /dev/null +++ b/platform/CXWindowsClipboardUTF8Converter.h @@ -0,0 +1,23 @@ +#ifndef CXWINDOWSCLIPBOARDUTF8CONVERTER_H +#define CXWINDOWSCLIPBOARDUTF8CONVERTER_H + +#include "CXWindowsClipboard.h" + +class CXWindowsClipboardUTF8Converter : public IXWindowsClipboardConverter { +public: + CXWindowsClipboardUTF8Converter(Display* display, const char* name); + virtual ~CXWindowsClipboardUTF8Converter(); + + // IXWindowsClipboardConverter overrides + virtual IClipboard::EFormat + getFormat() const; + virtual Atom getAtom() const; + virtual int getDataSize() const; + virtual CString fromIClipboard(const CString&) const; + virtual CString toIClipboard(const CString&) const; + +private: + Atom m_atom; +}; + +#endif diff --git a/platform/Makefile.am b/platform/Makefile.am index 704526b0..4257ecbd 100644 --- a/platform/Makefile.am +++ b/platform/Makefile.am @@ -4,22 +4,28 @@ DEPTH = .. # FIXME -- add CUnixPlatform.cpp as an unbuilt source noinst_LIBRARIES = libplatform.a -libplatform_a_SOURCES = \ - CPlatform.cpp \ - CXWindowsClipboard.cpp \ - CXWindowsScreen.cpp \ - CXWindowsScreenSaver.cpp \ - CXWindowsUtil.cpp \ - CPlatform.h \ - CUnixPlatform.h \ - CXWindowsClipboard.h \ - CXWindowsScreen.h \ - CXWindowsScreenSaver.h \ - CXWindowsUtil.h \ - IPlatform.h \ +libplatform_a_SOURCES = \ + CPlatform.cpp \ + CXWindowsClipboard.cpp \ + CXWindowsClipboardTextConverter.cpp \ + CXWindowsClipboardUCS2Converter.cpp \ + CXWindowsClipboardUTF8Converter.cpp \ + CXWindowsScreen.cpp \ + CXWindowsScreenSaver.cpp \ + CXWindowsUtil.cpp \ + CPlatform.h \ + CUnixPlatform.h \ + CXWindowsClipboard.h \ + CXWindowsClipboardTextConverter.h \ + CXWindowsClipboardUCS2Converter.h \ + CXWindowsClipboardUTF8Converter.h \ + CXWindowsScreen.h \ + CXWindowsScreenSaver.h \ + CXWindowsUtil.h \ + IPlatform.h \ $(NULL) -INCLUDES = \ - -I$(DEPTH)/base \ - -I$(DEPTH)/mt \ - -I$(DEPTH)/synergy \ +INCLUDES = \ + -I$(DEPTH)/base \ + -I$(DEPTH)/mt \ + -I$(DEPTH)/synergy \ $(NULL) diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 66e35d2a..2c145315 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -13,7 +13,13 @@ public: typedef UInt32 Time; // known clipboard formats. kNumFormats must be last and - // formats must be sequential starting from zero. + // formats must be sequential starting from zero. clipboard + // data set via add() and retrieved via get() is in one of + // these formats. platform dependent clipboard subclasses + // can and should present any suitable formats derivable + // from these formats (e.g. UCS-16 encoded unicode). + // + // kText: UTF-8 encoded unicode (ISO-10646), newline is LF enum EFormat { kText, kNumFormats }; // manipulators