Commit 0c90e44d authored by mercury233's avatar mercury233

update & fix clipboard

parent 00bc783d
...@@ -26,11 +26,11 @@ public: ...@@ -26,11 +26,11 @@ public:
} }
//! Copies text to the clipboard //! Copies text to the clipboard
virtual void copyToClipboard(const c16* text) const = 0; virtual void copyToClipboard(const c8* text) const = 0;
//! Get text from the clipboard //! Get text from the clipboard
/** \return Returns 0 if no string is in there. */ /** \return Returns 0 if no string is in there. */
virtual const c16* getTextFromClipboard() const = 0; virtual const c8* getTextFromClipboard() const = 0;
//! Get the processor speed in megahertz //! Get the processor speed in megahertz
/** \param MHz The integer variable to store the speed in. /** \param MHz The integer variable to store the speed in.
......
...@@ -12,6 +12,11 @@ ...@@ -12,6 +12,11 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef _IRR_WINDOWS_API_
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
namespace irr namespace irr
{ {
namespace core namespace core
...@@ -1360,14 +1365,66 @@ typedef string<c8> stringc; ...@@ -1360,14 +1365,66 @@ typedef string<c8> stringc;
//! Typedef for wide character strings //! Typedef for wide character strings
typedef string<wchar_t> stringw; typedef string<wchar_t> stringw;
//! wrap of mbstowcs //! Converts UTF-8/multibyte string to wide character string
/** On Windows: Converts from UTF-8 (encoding-agnostic, does not depend on locale).
On other platforms: Uses current locale settings (UTF-8 is recommended).
\return Pointer to newly allocated wide character string; caller must delete[].
*/
static inline wchar_t* toWideChar(const char* p) static inline wchar_t* toWideChar(const char* p)
{ {
if (!p)
{
wchar_t* ws = new wchar_t[1];
ws[0] = 0;
return ws;
}
#ifdef _IRR_WINDOWS_API_
int len = MultiByteToWideChar(CP_UTF8, 0, p, -1, NULL, 0);
if (len <= 0) len = 1;
wchar_t* ws = new wchar_t[len];
if (MultiByteToWideChar(CP_UTF8, 0, p, -1, ws, len) == 0)
ws[0] = 0;
return ws;
#else
size_t lenOld = strlen(p); size_t lenOld = strlen(p);
wchar_t* ws = new wchar_t[lenOld + 1]; wchar_t* ws = new wchar_t[lenOld + 1];
size_t lenNew = mbstowcs(ws, p, lenOld); size_t lenNew = mbstowcs(ws, p, lenOld + 1);
if (lenNew == (size_t)-1) lenNew = 0;
ws[lenNew] = 0; ws[lenNew] = 0;
return ws; return ws;
#endif
}
//! Converts wide character string to UTF-8/multibyte string
/** On Windows: Converts to UTF-8 (encoding-agnostic, does not depend on locale).
On other platforms: Uses current locale settings (UTF-8 is recommended).
\return Pointer to newly allocated multibyte string; caller must delete[].
*/
static inline char* toMultiByte(const wchar_t* p)
{
if (!p)
{
char* cs = new char[1];
cs[0] = 0;
return cs;
}
#ifdef _IRR_WINDOWS_API_
int len = WideCharToMultiByte(CP_UTF8, 0, p, -1, NULL, 0, NULL, NULL);
if (len <= 0) len = 1;
char* cs = new char[len];
if (WideCharToMultiByte(CP_UTF8, 0, p, -1, cs, len, NULL, NULL) == 0)
cs[0] = 0;
return cs;
#else
size_t lenOld = wcslen(p);
// Allocate worst-case buffer (MB_CUR_MAX bytes per wchar_t)
size_t bufSize = lenOld * MB_CUR_MAX + 1;
char* cs = new char[bufSize];
size_t lenNew = wcstombs(cs, p, bufSize - 1);
if (lenNew == (size_t)-1) lenNew = 0;
cs[lenNew] = 0;
return cs;
#endif
} }
} // end namespace core } // end namespace core
......
...@@ -48,9 +48,6 @@ typedef __int16 s16; ...@@ -48,9 +48,6 @@ typedef __int16 s16;
typedef signed short s16; typedef signed short s16;
#endif #endif
//! 16 bit character variable.
/** This is a typedef for wchar_t, it ensures portability of the engine. */
typedef wchar_t c16;
//! 32 bit unsigned variable. //! 32 bit unsigned variable.
......
...@@ -287,9 +287,10 @@ bool CGUIEditBox::processKey(const SEvent& event) ...@@ -287,9 +287,10 @@ bool CGUIEditBox::processKey(const SEvent& event)
const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
core::stringw s; core::stringw s = Text.subString(realmbgn, realmend - realmbgn);
s = Text.subString(realmbgn, realmend - realmbgn).c_str(); c8* mb = core::toMultiByte(s.c_str());
Operator->copyToClipboard(s.c_str()); Operator->copyToClipboard(mb);
delete[] mb;
} }
break; break;
case KEY_KEY_X: case KEY_KEY_X:
...@@ -300,9 +301,10 @@ bool CGUIEditBox::processKey(const SEvent& event) ...@@ -300,9 +301,10 @@ bool CGUIEditBox::processKey(const SEvent& event)
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
// copy // copy
core::stringw sc; core::stringw sc = Text.subString(realmbgn, realmend - realmbgn);
sc = Text.subString(realmbgn, realmend - realmbgn).c_str(); c8* mb = core::toMultiByte(sc.c_str());
Operator->copyToClipboard(sc.c_str()); Operator->copyToClipboard(mb);
delete[] mb;
if (isEnabled()) if (isEnabled())
{ {
...@@ -330,10 +332,12 @@ bool CGUIEditBox::processKey(const SEvent& event) ...@@ -330,10 +332,12 @@ bool CGUIEditBox::processKey(const SEvent& event)
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
// add new character // add new character
const c16* p = Operator->getTextFromClipboard(); const c8* p = Operator->getTextFromClipboard();
if (p) if (p)
{ {
irr::core::stringw widep(p); wchar_t* ws = core::toWideChar(p);
irr::core::stringw widep(ws);
delete[] ws;
if (MarkBegin == MarkEnd) if (MarkBegin == MarkEnd)
{ {
......
...@@ -1114,7 +1114,7 @@ bool CIrrDeviceLinux::run() ...@@ -1114,7 +1114,7 @@ bool CIrrDeviceLinux::run()
XChangeProperty (display, XChangeProperty (display,
req->requestor, req->requestor,
req->property, req->property,
req->target == X_ATOM_TEXT ? XA_STRING : req->target, req->target == XA_STRING ? XA_STRING : X_ATOM_UTF8_STRING,
8, // format 8, // format
PropModeReplace, PropModeReplace,
(unsigned char*) Clipboard.c_str(), (unsigned char*) Clipboard.c_str(),
...@@ -1123,10 +1123,12 @@ bool CIrrDeviceLinux::run() ...@@ -1123,10 +1123,12 @@ bool CIrrDeviceLinux::run()
} }
else if ( req->target == X_ATOM_TARGETS ) else if ( req->target == X_ATOM_TARGETS )
{ {
long data[2]; // Most preferred format first
Atom data[4];
data[0] = X_ATOM_TEXT; data[0] = X_ATOM_UTF8_STRING;
data[1] = XA_STRING; data[1] = X_ATOM_TEXT;
data[2] = XA_STRING;
data[3] = X_ATOM_TARGETS;
XChangeProperty (display, XChangeProperty (display,
req->requestor, req->requestor,
...@@ -1134,8 +1136,8 @@ bool CIrrDeviceLinux::run() ...@@ -1134,8 +1136,8 @@ bool CIrrDeviceLinux::run()
XA_ATOM, XA_ATOM,
32, // format 32, // format
PropModeReplace, PropModeReplace,
(unsigned char *) &X_ATOM_UTF8_STRING, (unsigned char *)data,
1); 4);
respond.xselection.property = req->property; respond.xselection.property = req->property;
} }
else else
...@@ -1882,36 +1884,63 @@ const c8* CIrrDeviceLinux::getTextFromClipboard() const ...@@ -1882,36 +1884,63 @@ const c8* CIrrDeviceLinux::getTextFromClipboard() const
Clipboard = ""; Clipboard = "";
if (ownerWindow != None ) if (ownerWindow != None )
{ {
XConvertSelection (display, X_ATOM_CLIPBOARD, X_ATOM_UTF8_STRING, X_ATOM_XSEL_DATA, window, CurrentTime); // First try UTF-8, then fall back to XA_STRING (ISO Latin-1)
XSync (display, 0); Atom fmtsToTry[2] = { X_ATOM_UTF8_STRING, XA_STRING };
XEvent event; for (int fmtIdx = 0; fmtIdx < 2 && Clipboard.empty(); ++fmtIdx)
do nanosleep ((const struct timespec[]){{0, 1L}}, NULL);
while (!XCheckTypedEvent (display, SelectionNotify, &event));
if ( (event.xselection.selection == X_ATOM_CLIPBOARD) && event.xselection.property )
{ {
Atom type; XConvertSelection (display, X_ATOM_CLIPBOARD, fmtsToTry[fmtIdx], X_ATOM_XSEL_DATA, window, CurrentTime);
int format; XSync (display, 0);
unsigned long size, dummy; XEvent event;
unsigned char *data; struct timespec deadline;
int result = XGetWindowProperty (event.xselection.display, clock_gettime(CLOCK_MONOTONIC, &deadline);
event.xselection.requestor, deadline.tv_sec += 1;
event.xselection.property,
0L, // offset bool gotSelectionNotify = false;
(~0L), // length (max) do
0, // Delete 0==false {
AnyPropertyType, // AnyPropertyType or property identifier struct timespec sleepTs = {0, 1000000L};
&type, // return type nanosleep (&sleepTs, NULL);
&format, // return format gotSelectionNotify = XCheckTypedEvent (display, SelectionNotify, &event) != 0;
&size, // number items if (!gotSelectionNotify)
&dummy, // remaining bytes for partial reads {
&data); // data struct timespec now;
if ( result == Success clock_gettime(CLOCK_MONOTONIC, &now);
&& (type == X_ATOM_UTF8_STRING || type == XA_STRING) ) // not implemented: INCR (partial reads) if (now.tv_sec > deadline.tv_sec || (now.tv_sec == deadline.tv_sec && now.tv_nsec >= deadline.tv_nsec))
break;
}
}
while (!gotSelectionNotify);
if (!gotSelectionNotify)
continue;
if ( (event.xselection.selection == X_ATOM_CLIPBOARD) && event.xselection.property )
{ {
Clipboard = strndup((char*)data, size); Atom type;
XFree (data); int format;
unsigned long size, dummy;
unsigned char *data;
int result = XGetWindowProperty (event.xselection.display,
event.xselection.requestor,
event.xselection.property,
0L, // offset
(~0L), // length (max)
0, // Delete 0==false
AnyPropertyType, // AnyPropertyType or property identifier
&type, // return type
&format, // return format
&size, // number items
&dummy, // remaining bytes for partial reads
&data); // data
if ( result == Success
&& data
&& (type == X_ATOM_UTF8_STRING || type == XA_STRING) ) // not implemented: INCR (partial reads)
{
Clipboard = core::stringc((const c8*)data, size);
}
if (data)
XFree (data);
XDeleteProperty (event.xselection.display, event.xselection.requestor, event.xselection.property);
} }
XDeleteProperty (event.xselection.display, event.xselection.requestor, event.xselection.property);
} }
} }
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <windows.h> #include <windows.h>
#endif #endif
#else #else
#include <locale.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#ifndef _IRR_SOLARIS_PLATFORM_ #ifndef _IRR_SOLARIS_PLATFORM_
...@@ -55,30 +54,51 @@ const core::stringc& COSOperator::getOperatingSystemVersion() const ...@@ -55,30 +54,51 @@ const core::stringc& COSOperator::getOperatingSystemVersion() const
//! copies text to the clipboard //! copies text to the clipboard
void COSOperator::copyToClipboard(const c16* text) const //! text must be encoded in the system multibyte format (UTF-8 on Linux/macOS)
void COSOperator::copyToClipboard(const c8* text) const
{ {
size_t len = wcslen(text); if (!text || text[0] == '\0')
if (len==0)
return; return;
// Windows version // Windows version
#if defined(_IRR_XBOX_PLATFORM_) #if defined(_IRR_XBOX_PLATFORM_)
#elif defined(_IRR_WINDOWS_API_) #elif defined(_IRR_WINDOWS_API_)
if (!OpenClipboard(NULL) || text == 0) // Convert UTF-8 to UTF-16 for the Windows clipboard
int wlen = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
if (wlen == 0)
return;
if (!OpenClipboard(NULL))
return; return;
EmptyClipboard(); EmptyClipboard();
HGLOBAL clipbuffer; HGLOBAL clipbuffer = GlobalAlloc(GMEM_DDESHARE, sizeof(wchar_t) * wlen);
wchar_t * buffer; if (!clipbuffer)
{
CloseClipboard();
return;
}
clipbuffer = GlobalAlloc(GMEM_DDESHARE, sizeof(wchar_t) * (len + 1)); wchar_t* buffer = (wchar_t*)GlobalLock(clipbuffer);
buffer = (wchar_t*)GlobalLock(clipbuffer); if (!buffer)
{
GlobalFree(clipbuffer);
CloseClipboard();
return;
}
wcscpy(buffer, text); if (MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, wlen) == 0)
{
GlobalUnlock(clipbuffer);
GlobalFree(clipbuffer);
CloseClipboard();
return;
}
GlobalUnlock(clipbuffer); GlobalUnlock(clipbuffer);
SetClipboardData(CF_UNICODETEXT, clipbuffer); if (!SetClipboardData(CF_UNICODETEXT, clipbuffer))
GlobalFree(clipbuffer);
CloseClipboard(); CloseClipboard();
// MacOSX version // MacOSX version
...@@ -88,18 +108,7 @@ void COSOperator::copyToClipboard(const c16* text) const ...@@ -88,18 +108,7 @@ void COSOperator::copyToClipboard(const c16* text) const
#elif defined(_IRR_COMPILE_WITH_X11_DEVICE_) #elif defined(_IRR_COMPILE_WITH_X11_DEVICE_)
if ( IrrDeviceLinux ) if ( IrrDeviceLinux )
{ IrrDeviceLinux->copyToClipboard(text);
size_t wlen = sizeof(wchar_t) * (len + 1);
char ctext[wlen];
char* oldLocale = setlocale(LC_CTYPE, NULL);
setlocale(LC_CTYPE, "");
size_t lenNew = wcstombs(ctext, text, wlen);
ctext[lenNew] = 0;
setlocale(LC_CTYPE, oldLocale);
IrrDeviceLinux->copyToClipboard(ctext);
}
#else #else
#endif #endif
...@@ -108,7 +117,8 @@ void COSOperator::copyToClipboard(const c16* text) const ...@@ -108,7 +117,8 @@ void COSOperator::copyToClipboard(const c16* text) const
//! gets text from the clipboard //! gets text from the clipboard
//! \return Returns 0 if no string is in there. //! \return Returns 0 if no string is in there.
const c16* COSOperator::getTextFromClipboard() const //! Result is encoded in the system multibyte format (UTF-8 on Linux/macOS)
const c8* COSOperator::getTextFromClipboard() const
{ {
#if defined(_IRR_XBOX_PLATFORM_) #if defined(_IRR_XBOX_PLATFORM_)
return 0; return 0;
...@@ -116,29 +126,35 @@ const c16* COSOperator::getTextFromClipboard() const ...@@ -116,29 +126,35 @@ const c16* COSOperator::getTextFromClipboard() const
if (!OpenClipboard(NULL)) if (!OpenClipboard(NULL))
return 0; return 0;
wchar_t * buffer = 0; ClipboardBuffer = "";
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
HANDLE hData = GetClipboardData( CF_UNICODETEXT ); if (hData)
buffer = (wchar_t*)GlobalLock( hData ); {
GlobalUnlock( hData ); wchar_t* wbuffer = (wchar_t*)GlobalLock(hData);
if (wbuffer)
{
// Convert UTF-16 clipboard data to UTF-8 and store in member buffer
// so the returned pointer remains valid after CloseClipboard()
int len = WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1, NULL, 0, NULL, NULL);
if (len > 0)
{
c8* tmp = new c8[len];
WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1, tmp, len, NULL, NULL);
ClipboardBuffer = tmp;
delete[] tmp;
}
GlobalUnlock(hData);
}
}
CloseClipboard(); CloseClipboard();
return buffer; return ClipboardBuffer.c_str();
#elif defined(_IRR_COMPILE_WITH_OSX_DEVICE_) #elif defined(_IRR_COMPILE_WITH_OSX_DEVICE_)
return (OSXCopyFromClipboard()); return (OSXCopyFromClipboard());
#elif defined(_IRR_COMPILE_WITH_X11_DEVICE_) #elif defined(_IRR_COMPILE_WITH_X11_DEVICE_)
if ( IrrDeviceLinux ) if ( IrrDeviceLinux )
{ return IrrDeviceLinux->getTextFromClipboard();
const c8 * p = IrrDeviceLinux->getTextFromClipboard();
char* oldLocale = setlocale(LC_CTYPE, NULL);
setlocale(LC_CTYPE, "");
wchar_t* ws = core::toWideChar(p);
setlocale(LC_CTYPE, oldLocale);
return ws;
}
return 0; return 0;
#else #else
......
...@@ -27,11 +27,11 @@ public: ...@@ -27,11 +27,11 @@ public:
virtual const core::stringc& getOperatingSystemVersion() const; virtual const core::stringc& getOperatingSystemVersion() const;
//! copies text to the clipboard //! copies text to the clipboard
virtual void copyToClipboard(const c16* text) const; virtual void copyToClipboard(const c8* text) const;
//! gets text from the clipboard //! gets text from the clipboard
//! \return Returns 0 if no string is in there. //! \return Returns 0 if no string is in there.
virtual const c16* getTextFromClipboard() const; virtual const c8* getTextFromClipboard() const;
//! gets the processor speed in megahertz //! gets the processor speed in megahertz
//! \param Mhz: //! \param Mhz:
...@@ -47,6 +47,9 @@ public: ...@@ -47,6 +47,9 @@ public:
private: private:
core::stringc OperatingSystem; core::stringc OperatingSystem;
#ifdef _IRR_WINDOWS_API_
mutable core::stringc ClipboardBuffer;
#endif
#if defined(_IRR_COMPILE_WITH_X11_DEVICE_) #if defined(_IRR_COMPILE_WITH_X11_DEVICE_)
CIrrDeviceLinux * IrrDeviceLinux; CIrrDeviceLinux * IrrDeviceLinux;
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
extern "C" { extern "C" {
#endif #endif
void OSXCopyToClipboard(const wchar_t *text); void OSXCopyToClipboard(const char *text);
wchar_t* OSXCopyFromClipboard(); const char* OSXCopyFromClipboard();
#ifdef __cplusplus #ifdef __cplusplus
} }
......
...@@ -7,31 +7,32 @@ ...@@ -7,31 +7,32 @@
#include "OSXClipboard.h" #include "OSXClipboard.h"
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
void OSXCopyToClipboard(const wchar_t *text) void OSXCopyToClipboard(const char *text)
{ {
NSString *str; if (text != NULL && strlen(text) > 0)
NSPasteboard *board;
if ((text != NULL) && (wcslen(text) > 0))
{ {
str = [[NSString alloc] initWithBytes:text length:wcslen(text)*sizeof(*text) encoding:NSUTF32LittleEndianStringEncoding]; NSString* str = [NSString stringWithUTF8String:text];
board = [NSPasteboard generalPasteboard]; if (str)
[board declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:NSApp]; {
[board setString:str forType:NSStringPboardType]; NSPasteboard* board = [NSPasteboard generalPasteboard];
[board declareTypes:@[NSPasteboardTypeString] owner:NSApp];
[board setString:str forType:NSPasteboardTypeString];
}
} }
} }
wchar_t* OSXCopyFromClipboard() const char* OSXCopyFromClipboard()
{ {
NSString* str; static irr::core::stringc buffer;
NSPasteboard* board; NSPasteboard* board = [NSPasteboard generalPasteboard];
wchar_t* result; NSString* str = [board stringForType:NSPasteboardTypeString];
if (str == nil)
result = NULL; {
board = [NSPasteboard generalPasteboard]; buffer = "";
str = [board stringForType:NSStringPboardType]; return buffer.c_str();
if (str != nil) }
result = (wchar_t*)[str cStringUsingEncoding:NSUTF32LittleEndianStringEncoding]; const char* utf8 = [str UTF8String];
return (result); buffer = utf8 ? utf8 : "";
return buffer.c_str();
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment