|
@@ -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)
|
|
|
+{
|
|
|
+ /* Retry to open the clipboard in case another application has it open */
|
|
|
+ const int MAX_ATTEMPTS = 3;
|
|
|
+ int attempt;
|
|
|
+ HWND hwnd = NULL;
|
|
|
+
|
|
|
+ if (_this->windows) {
|
|
|
+ hwnd = _this->windows->driverdata->hwnd;
|
|
|
+ }
|
|
|
+ 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)
|
|
|
{
|
|
|
- SDL_Window *window;
|
|
|
+ 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;
|
|
|
+}
|
|
|
|
|
|
- window = _this->windows;
|
|
|
- if (window) {
|
|
|
- return window->driverdata->hwnd;
|
|
|
+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 NULL;
|
|
|
+ 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;
|
|
|
-
|
|
|
- for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
|
|
|
- if (OpenClipboard(GetWindowHandle(_this))) {
|
|
|
- HANDLE hMem;
|
|
|
- LPTSTR tstr;
|
|
|
+ if (WIN_OpenClipboard(_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)
|