Added support for clipboard data on Windows
The only supported image format is image/bmpmain
parent
35876da3c4
commit
443868143c
|
@ -31,17 +31,190 @@
|
|||
#else
|
||||
#define TEXT_FORMAT CF_TEXT
|
||||
#endif
|
||||
#define IMAGE_FORMAT CF_DIB
|
||||
#define IMAGE_MIME_TYPE "image/bmp"
|
||||
|
||||
/* Get any application owned window handle for clipboard association */
|
||||
static HWND GetWindowHandle(SDL_VideoDevice *_this)
|
||||
/* Assume we can directly read and write BMP fields without byte swapping */
|
||||
SDL_COMPILE_TIME_ASSERT(verify_byte_order, SDL_BYTEORDER == SDL_LIL_ENDIAN);
|
||||
|
||||
static const char bmp_magic[2] = { 'B', 'M' };
|
||||
|
||||
static BOOL WIN_OpenClipboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_Window *window;
|
||||
/* Retry to open the clipboard in case another application has it open */
|
||||
const int MAX_ATTEMPTS = 3;
|
||||
int attempt;
|
||||
HWND hwnd = NULL;
|
||||
|
||||
window = _this->windows;
|
||||
if (window) {
|
||||
return window->driverdata->hwnd;
|
||||
if (_this->windows) {
|
||||
hwnd = _this->windows->driverdata->hwnd;
|
||||
}
|
||||
return NULL;
|
||||
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
|
||||
if (OpenClipboard(hwnd)) {
|
||||
return TRUE;
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void WIN_CloseClipboard(void)
|
||||
{
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
static HANDLE WIN_ConvertBMPtoDIB(const void *bmp, size_t bmp_size)
|
||||
{
|
||||
HANDLE hMem = NULL;
|
||||
|
||||
if (bmp && bmp_size > sizeof(BITMAPFILEHEADER) && SDL_memcmp(bmp, bmp_magic, sizeof(bmp_magic)) == 0) {
|
||||
BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp;
|
||||
BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)((Uint8 *)bmp + sizeof(BITMAPFILEHEADER));
|
||||
size_t bih_size = pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
|
||||
size_t pixels_size = pbih->biSizeImage;
|
||||
|
||||
if (pbfh->bfOffBits >= (sizeof(BITMAPFILEHEADER) + bih_size) &&
|
||||
(pbfh->bfOffBits + pixels_size) <= bmp_size) {
|
||||
const Uint8 *pixels = (const Uint8 *)bmp + pbfh->bfOffBits;
|
||||
size_t dib_size = bih_size + pixels_size;
|
||||
hMem = GlobalAlloc(GMEM_MOVEABLE, dib_size);
|
||||
if (hMem) {
|
||||
LPVOID dst = GlobalLock(hMem);
|
||||
if (dst) {
|
||||
SDL_memcpy(dst, pbih, bih_size);
|
||||
SDL_memcpy((Uint8 *)dst + bih_size, pixels, pixels_size);
|
||||
GlobalUnlock(hMem);
|
||||
} else {
|
||||
WIN_SetError("GlobalLock()");
|
||||
GlobalFree(hMem);
|
||||
hMem = NULL;
|
||||
}
|
||||
} else {
|
||||
SDL_OutOfMemory();
|
||||
}
|
||||
} else {
|
||||
SDL_SetError("Invalid BMP data");
|
||||
}
|
||||
} else {
|
||||
SDL_SetError("Invalid BMP data");
|
||||
}
|
||||
return hMem;
|
||||
}
|
||||
|
||||
static void *WIN_ConvertDIBtoBMP(HANDLE hMem, size_t *size)
|
||||
{
|
||||
void *bmp = NULL;
|
||||
size_t mem_size = GlobalSize(hMem);
|
||||
|
||||
if (mem_size > sizeof(BITMAPINFOHEADER)) {
|
||||
LPVOID dib = GlobalLock(hMem);
|
||||
if (dib) {
|
||||
BITMAPINFOHEADER *pbih = (BITMAPINFOHEADER *)dib;
|
||||
size_t bih_size = pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
|
||||
size_t dib_size = bih_size + pbih->biSizeImage;
|
||||
if (dib_size <= mem_size) {
|
||||
size_t bmp_size = sizeof(BITMAPFILEHEADER) + dib_size;
|
||||
bmp = SDL_malloc(bmp_size);
|
||||
if (bmp) {
|
||||
BITMAPFILEHEADER *pbfh = (BITMAPFILEHEADER *)bmp;
|
||||
pbfh->bfType = 0x4d42; /* bmp_magic */
|
||||
pbfh->bfSize = (DWORD)bmp_size;
|
||||
pbfh->bfReserved1 = 0;
|
||||
pbfh->bfReserved2 = 0;
|
||||
pbfh->bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + bih_size);
|
||||
SDL_memcpy((Uint8 *)bmp + sizeof(BITMAPFILEHEADER), dib, dib_size);
|
||||
*size = bmp_size;
|
||||
} else {
|
||||
SDL_OutOfMemory();
|
||||
}
|
||||
} else {
|
||||
SDL_SetError("Invalid BMP data");
|
||||
}
|
||||
GlobalUnlock(hMem);
|
||||
} else {
|
||||
WIN_SetError("GlobalLock()");
|
||||
}
|
||||
} else {
|
||||
SDL_SetError("Invalid BMP data");
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
static int WIN_SetClipboardImage(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = _this->driverdata;
|
||||
int result = 0;
|
||||
|
||||
if (WIN_OpenClipboard(_this)) {
|
||||
HANDLE hMem;
|
||||
size_t clipboard_data_size;
|
||||
const void *clipboard_data;
|
||||
|
||||
clipboard_data = _this->clipboard_callback(_this->clipboard_userdata, IMAGE_MIME_TYPE, &clipboard_data_size);
|
||||
hMem = WIN_ConvertBMPtoDIB(clipboard_data, clipboard_data_size);
|
||||
if (hMem) {
|
||||
/* Save the image to the clipboard */
|
||||
EmptyClipboard();
|
||||
if (!SetClipboardData(IMAGE_FORMAT, hMem)) {
|
||||
result = WIN_SetError("Couldn't set clipboard data");
|
||||
}
|
||||
data->clipboard_count = GetClipboardSequenceNumber();
|
||||
} else {
|
||||
/* WIN_ConvertBMPtoDIB() set the error */
|
||||
result = -1;
|
||||
}
|
||||
|
||||
WIN_CloseClipboard();
|
||||
} else {
|
||||
result = WIN_SetError("Couldn't open clipboard");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int WIN_SetClipboardData(SDL_VideoDevice *_this)
|
||||
{
|
||||
size_t i;
|
||||
int result = 0;
|
||||
|
||||
/* Right now only BMP data is supported for interchange with other applications */
|
||||
for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
|
||||
if (SDL_strcmp(_this->clipboard_mime_types[i], IMAGE_MIME_TYPE) == 0) {
|
||||
result = WIN_SetClipboardImage(_this);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *WIN_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
|
||||
{
|
||||
void *data = NULL;
|
||||
|
||||
if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
|
||||
if (IsClipboardFormatAvailable(IMAGE_FORMAT)) {
|
||||
if (WIN_OpenClipboard(_this)) {
|
||||
HANDLE hMem;
|
||||
|
||||
hMem = GetClipboardData(IMAGE_FORMAT);
|
||||
if (hMem) {
|
||||
data = WIN_ConvertDIBtoBMP(hMem, size);
|
||||
} else {
|
||||
WIN_SetError("Couldn't get clipboard data");
|
||||
}
|
||||
WIN_CloseClipboard();
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
SDL_bool WIN_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
|
||||
{
|
||||
if (SDL_strcmp(mime_type, IMAGE_MIME_TYPE) == 0) {
|
||||
if (IsClipboardFormatAvailable(IMAGE_FORMAT)) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
int WIN_SetClipboardText(SDL_VideoDevice *_this, const char *text)
|
||||
|
@ -49,7 +222,7 @@ int WIN_SetClipboardText(SDL_VideoDevice *_this, const char *text)
|
|||
SDL_VideoData *data = _this->driverdata;
|
||||
int result = 0;
|
||||
|
||||
if (OpenClipboard(GetWindowHandle(_this))) {
|
||||
if (WIN_OpenClipboard(_this)) {
|
||||
HANDLE hMem;
|
||||
LPTSTR tstr;
|
||||
SIZE_T i, size;
|
||||
|
@ -90,10 +263,12 @@ int WIN_SetClipboardText(SDL_VideoDevice *_this, const char *text)
|
|||
result = WIN_SetError("Couldn't set clipboard data");
|
||||
}
|
||||
data->clipboard_count = GetClipboardSequenceNumber();
|
||||
} else {
|
||||
result = SDL_OutOfMemory();
|
||||
}
|
||||
SDL_free(tstr);
|
||||
|
||||
CloseClipboard();
|
||||
WIN_CloseClipboard();
|
||||
} else {
|
||||
result = WIN_SetError("Couldn't open clipboard");
|
||||
}
|
||||
|
@ -105,27 +280,23 @@ char *WIN_GetClipboardText(SDL_VideoDevice *_this)
|
|||
char *text = NULL;
|
||||
|
||||
if (IsClipboardFormatAvailable(TEXT_FORMAT)) {
|
||||
/* Retry to open the clipboard in case another application has it open */
|
||||
const int MAX_ATTEMPTS = 3;
|
||||
int attempt;
|
||||
if (WIN_OpenClipboard(_this)) {
|
||||
HANDLE hMem;
|
||||
LPTSTR tstr;
|
||||
|
||||
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
|
||||
if (OpenClipboard(GetWindowHandle(_this))) {
|
||||
HANDLE hMem;
|
||||
LPTSTR tstr;
|
||||
|
||||
hMem = GetClipboardData(TEXT_FORMAT);
|
||||
if (hMem) {
|
||||
tstr = (LPTSTR)GlobalLock(hMem);
|
||||
hMem = GetClipboardData(TEXT_FORMAT);
|
||||
if (hMem) {
|
||||
tstr = (LPTSTR)GlobalLock(hMem);
|
||||
if (tstr) {
|
||||
text = WIN_StringToUTF8(tstr);
|
||||
GlobalUnlock(hMem);
|
||||
} else {
|
||||
WIN_SetError("Couldn't get clipboard data");
|
||||
WIN_SetError("Couldn't lock clipboard data");
|
||||
}
|
||||
CloseClipboard();
|
||||
break;
|
||||
} else {
|
||||
WIN_SetError("Couldn't get clipboard data");
|
||||
}
|
||||
SDL_Delay(10);
|
||||
WIN_CloseClipboard();
|
||||
}
|
||||
}
|
||||
if (text == NULL) {
|
||||
|
@ -136,13 +307,10 @@ char *WIN_GetClipboardText(SDL_VideoDevice *_this)
|
|||
|
||||
SDL_bool WIN_HasClipboardText(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_bool result = SDL_FALSE;
|
||||
char *text = WIN_GetClipboardText(_this);
|
||||
if (text) {
|
||||
result = text[0] != '\0' ? SDL_TRUE : SDL_FALSE;
|
||||
SDL_free(text);
|
||||
if (IsClipboardFormatAvailable(TEXT_FORMAT)) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
return result;
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
void WIN_CheckClipboardUpdate(struct SDL_VideoData *data)
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
/* Forward declaration */
|
||||
struct SDL_VideoData;
|
||||
|
||||
int WIN_SetClipboardData(SDL_VideoDevice *_this);
|
||||
void *WIN_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
|
||||
SDL_bool WIN_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
|
||||
extern int WIN_SetClipboardText(SDL_VideoDevice *_this, const char *text);
|
||||
extern char *WIN_GetClipboardText(SDL_VideoDevice *_this);
|
||||
extern SDL_bool WIN_HasClipboardText(SDL_VideoDevice *_this);
|
||||
|
|
|
@ -257,6 +257,9 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
|
|||
device->ClearComposition = WIN_ClearComposition;
|
||||
device->IsTextInputShown = WIN_IsTextInputShown;
|
||||
|
||||
device->SetClipboardData = WIN_SetClipboardData;
|
||||
device->GetClipboardData = WIN_GetClipboardData;
|
||||
device->HasClipboardData = WIN_HasClipboardData;
|
||||
device->SetClipboardText = WIN_SetClipboardText;
|
||||
device->GetClipboardText = WIN_GetClipboardText;
|
||||
device->HasClipboardText = WIN_HasClipboardText;
|
||||
|
|
Loading…
Reference in New Issue