Daily update on 20250721-223448 by Nitload Youmu. Additional Message : Rename StgSHarp.C to StgSharp.Native. Add SDL to repo.
Signed-off-by: Nitload <YoumuLiu_NSI@whv0t.onmicrosoft.com>
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
|
||||
#include <limits.h> // For INT_MAX
|
||||
|
||||
#include "SDL_x11video.h"
|
||||
#include "SDL_x11clipboard.h"
|
||||
#include "../SDL_clipboard_c.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
static const char *text_mime_types[] = {
|
||||
"UTF8_STRING",
|
||||
"text/plain;charset=utf-8",
|
||||
"text/plain",
|
||||
"TEXT",
|
||||
"STRING"
|
||||
};
|
||||
|
||||
// Get any application owned window handle for clipboard association
|
||||
Window GetWindow(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = _this->internal;
|
||||
|
||||
/* We create an unmapped window that exists just to manage the clipboard,
|
||||
since X11 selection data is tied to a specific window and dies with it.
|
||||
We create the window on demand, so apps that don't use the clipboard
|
||||
don't have to keep an unnecessary resource around. */
|
||||
if (data->clipboard_window == None) {
|
||||
Display *dpy = data->display;
|
||||
Window parent = RootWindow(dpy, DefaultScreen(dpy));
|
||||
XSetWindowAttributes xattr;
|
||||
data->clipboard_window = X11_XCreateWindow(dpy, parent, -10, -10, 1, 1, 0,
|
||||
CopyFromParent, InputOnly,
|
||||
CopyFromParent, 0, &xattr);
|
||||
|
||||
X11_XSelectInput(dpy, data->clipboard_window, PropertyChangeMask);
|
||||
X11_XFlush(data->display);
|
||||
}
|
||||
|
||||
return data->clipboard_window;
|
||||
}
|
||||
|
||||
static bool SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_ClipboardDataCallback callback,
|
||||
void *userdata, const char **mime_types, size_t mime_count, Uint32 sequence)
|
||||
{
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
Display *display = videodata->display;
|
||||
Window window;
|
||||
SDLX11_ClipboardData *clipboard;
|
||||
bool clipboard_owner = false;
|
||||
|
||||
window = GetWindow(_this);
|
||||
if (window == None) {
|
||||
return SDL_SetError("Couldn't find a window to own the selection");
|
||||
}
|
||||
|
||||
if (selection == XA_PRIMARY) {
|
||||
clipboard = &videodata->primary_selection;
|
||||
} else {
|
||||
clipboard = &videodata->clipboard;
|
||||
}
|
||||
|
||||
clipboard_owner = X11_XGetSelectionOwner(display, selection) == window;
|
||||
|
||||
// If we are canceling our own data we need to clean it up
|
||||
if (clipboard_owner && clipboard->sequence == 0) {
|
||||
SDL_free(clipboard->userdata);
|
||||
}
|
||||
|
||||
clipboard->callback = callback;
|
||||
clipboard->userdata = userdata;
|
||||
clipboard->mime_types = mime_types;
|
||||
clipboard->mime_count = mime_count;
|
||||
clipboard->sequence = sequence;
|
||||
|
||||
X11_XSetSelectionOwner(display, selection, window, CurrentTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *CloneDataBuffer(const void *buffer, const size_t len)
|
||||
{
|
||||
void *clone = NULL;
|
||||
if (len > 0 && buffer) {
|
||||
clone = SDL_malloc(len + sizeof(Uint32));
|
||||
if (clone) {
|
||||
SDL_memcpy(clone, buffer, len);
|
||||
SDL_memset((Uint8 *)clone + len, 0, sizeof(Uint32));
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/*
|
||||
* original_buffer is considered unusable after the function is called.
|
||||
*/
|
||||
static void *AppendDataBuffer(void *original_buffer, const size_t old_len, const void *buffer, const size_t buffer_len)
|
||||
{
|
||||
void *resized_buffer;
|
||||
|
||||
if (buffer_len > 0 && buffer) {
|
||||
resized_buffer = SDL_realloc(original_buffer, old_len + buffer_len + sizeof(Uint32));
|
||||
if (resized_buffer) {
|
||||
SDL_memcpy((Uint8 *)resized_buffer + old_len, buffer, buffer_len);
|
||||
SDL_memset((Uint8 *)resized_buffer + old_len + buffer_len, 0, sizeof(Uint32));
|
||||
}
|
||||
|
||||
return resized_buffer;
|
||||
} else {
|
||||
return original_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
static bool WaitForSelection(SDL_VideoDevice *_this, Atom selection_type, bool *flag)
|
||||
{
|
||||
Uint64 waitStart;
|
||||
Uint64 waitElapsed;
|
||||
|
||||
waitStart = SDL_GetTicks();
|
||||
*flag = true;
|
||||
while (*flag) {
|
||||
SDL_PumpEvents();
|
||||
waitElapsed = SDL_GetTicks() - waitStart;
|
||||
// Wait one second for a selection response.
|
||||
if (waitElapsed > 1000) {
|
||||
*flag = false;
|
||||
SDL_SetError("Selection timeout");
|
||||
/* We need to set the selection text so that next time we won't
|
||||
timeout, otherwise we will hang on every call to this function. */
|
||||
SetSelectionData(_this, selection_type, SDL_ClipboardTextCallback, NULL,
|
||||
text_mime_types, SDL_arraysize(text_mime_types), 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
|
||||
const char *mime_type, size_t *length)
|
||||
{
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
Display *display = videodata->display;
|
||||
Window window;
|
||||
Window owner;
|
||||
Atom selection;
|
||||
Atom seln_type;
|
||||
int seln_format;
|
||||
unsigned long count;
|
||||
unsigned long overflow;
|
||||
|
||||
SDLX11_ClipboardData *clipboard;
|
||||
void *data = NULL;
|
||||
unsigned char *src = NULL;
|
||||
bool incr_success = false;
|
||||
Atom XA_MIME = X11_XInternAtom(display, mime_type, False);
|
||||
|
||||
*length = 0;
|
||||
|
||||
// Get the window that holds the selection
|
||||
window = GetWindow(_this);
|
||||
owner = X11_XGetSelectionOwner(display, selection_type);
|
||||
if (owner == None) {
|
||||
// This requires a fallback to ancient X10 cut-buffers. We will just skip those for now
|
||||
data = NULL;
|
||||
} else if (owner == window) {
|
||||
owner = DefaultRootWindow(display);
|
||||
if (selection_type == XA_PRIMARY) {
|
||||
clipboard = &videodata->primary_selection;
|
||||
} else {
|
||||
clipboard = &videodata->clipboard;
|
||||
}
|
||||
|
||||
if (clipboard->callback) {
|
||||
const void *clipboard_data = clipboard->callback(clipboard->userdata, mime_type, length);
|
||||
data = CloneDataBuffer(clipboard_data, *length);
|
||||
}
|
||||
} else {
|
||||
// Request that the selection owner copy the data to our window
|
||||
owner = window;
|
||||
selection = videodata->atoms.SDL_SELECTION;
|
||||
X11_XConvertSelection(display, selection_type, XA_MIME, selection, owner,
|
||||
CurrentTime);
|
||||
|
||||
if (WaitForSelection(_this, selection_type, &videodata->selection_waiting) == false) {
|
||||
data = NULL;
|
||||
*length = 0;
|
||||
}
|
||||
|
||||
if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
|
||||
XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) == Success) {
|
||||
if (seln_type == XA_MIME) {
|
||||
*length = (size_t)count;
|
||||
data = CloneDataBuffer(src, count);
|
||||
} else if (seln_type == videodata->atoms.INCR) {
|
||||
while (1) {
|
||||
// Only delete the property after being done with the previous "chunk".
|
||||
X11_XDeleteProperty(display, owner, selection);
|
||||
X11_XFlush(display);
|
||||
|
||||
if (WaitForSelection(_this, selection_type, &videodata->selection_incr_waiting) == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
X11_XFree(src);
|
||||
if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
|
||||
XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) != Success) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
incr_success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*length == 0) {
|
||||
*length = (size_t)count;
|
||||
data = CloneDataBuffer(src, count);
|
||||
} else {
|
||||
data = AppendDataBuffer(data, *length, src, count);
|
||||
*length += (size_t)count;
|
||||
}
|
||||
|
||||
if (data == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (incr_success == false) {
|
||||
SDL_free(data);
|
||||
data = 0;
|
||||
*length = 0;
|
||||
}
|
||||
}
|
||||
X11_XFree(src);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types)
|
||||
{
|
||||
*num_mime_types = SDL_arraysize(text_mime_types);
|
||||
return text_mime_types;
|
||||
}
|
||||
|
||||
bool X11_SetClipboardData(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
return SetSelectionData(_this, videodata->atoms.CLIPBOARD, _this->clipboard_callback, _this->clipboard_userdata, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, _this->clipboard_sequence);
|
||||
}
|
||||
|
||||
void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
|
||||
{
|
||||
SDL_VideoData *videodata = _this->internal;
|
||||
return GetSelectionData(_this, videodata->atoms.CLIPBOARD, mime_type, length);
|
||||
}
|
||||
|
||||
bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
|
||||
{
|
||||
size_t length;
|
||||
void *data;
|
||||
data = X11_GetClipboardData(_this, mime_type, &length);
|
||||
if (data) {
|
||||
SDL_free(data);
|
||||
}
|
||||
return length > 0;
|
||||
}
|
||||
|
||||
bool X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
|
||||
{
|
||||
return SetSelectionData(_this, XA_PRIMARY, SDL_ClipboardTextCallback, SDL_strdup(text), text_mime_types, SDL_arraysize(text_mime_types), 0);
|
||||
}
|
||||
|
||||
char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this)
|
||||
{
|
||||
size_t length;
|
||||
char *text = GetSelectionData(_this, XA_PRIMARY, text_mime_types[0], &length);
|
||||
if (!text) {
|
||||
text = SDL_strdup("");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this)
|
||||
{
|
||||
bool result = false;
|
||||
char *text = X11_GetPrimarySelectionText(_this);
|
||||
if (text) {
|
||||
if (text[0] != '\0') {
|
||||
result = true;
|
||||
}
|
||||
SDL_free(text);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void X11_QuitClipboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = _this->internal;
|
||||
if (data->primary_selection.sequence == 0) {
|
||||
SDL_free(data->primary_selection.userdata);
|
||||
}
|
||||
if (data->clipboard.sequence == 0) {
|
||||
SDL_free(data->clipboard.userdata);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_X11
|
||||
Reference in New Issue
Block a user