Tuck the GdkWin32Clipdrop that we create in our GdkWin32Display, and tuck the other associated global variables into GdkWin32ClipDrop and GdkWin32Drag, as appropriate. Also, since we are already registering "GDK_WORKER_THREAD_WAKEUP" as our custom message to look for in our DND/clipboard ops, only register it once, not twice, as it's not really necessary to do so since RegisterWindowMessage() returns the same value for the same identifier that is being used.,
406 lines
13 KiB
C
406 lines
13 KiB
C
/* GDK HData Output Stream - a stream backed by a global memory buffer
|
||
*
|
||
* Copyright (C) 2018 Руслан Ижбулатов
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2.1 of the License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General
|
||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||
*
|
||
* Author: Руслан Ижбулатов <lrn1986@gmail.com>
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <windows.h>
|
||
|
||
#include "gdkprivate-win32.h"
|
||
#include "gdkhdataoutputstream-win32.h"
|
||
|
||
#include "gdkclipboard-win32.h"
|
||
#include "gdkdisplay-win32.h"
|
||
#include <glib/gi18n-lib.h>
|
||
#include "gdkwin32display.h"
|
||
#include "gdkwin32surface.h"
|
||
|
||
|
||
typedef struct _GdkWin32HDataOutputStreamPrivate GdkWin32HDataOutputStreamPrivate;
|
||
|
||
struct _GdkWin32HDataOutputStreamPrivate
|
||
{
|
||
HANDLE handle;
|
||
GdkWin32Clipdrop *clipdrop;
|
||
guchar *data;
|
||
gsize data_allocated_size;
|
||
gsize data_length;
|
||
GdkWin32ContentFormatPair pair;
|
||
guint handle_is_buffer : 1;
|
||
guint closed : 1;
|
||
};
|
||
|
||
G_DEFINE_TYPE_WITH_PRIVATE (GdkWin32HDataOutputStream, gdk_win32_hdata_output_stream, G_TYPE_OUTPUT_STREAM);
|
||
|
||
static gssize
|
||
write_stream (GdkWin32HDataOutputStream *stream,
|
||
GdkWin32HDataOutputStreamPrivate *priv,
|
||
const void *buffer,
|
||
gsize count,
|
||
GError **error)
|
||
{
|
||
gsize spillover = (priv->data_length + count) - priv->data_allocated_size;
|
||
gsize to_copy = count;
|
||
|
||
if (priv->closed)
|
||
{
|
||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("writing a closed stream"));
|
||
return -1;
|
||
}
|
||
|
||
if (spillover > 0 && !priv->handle_is_buffer)
|
||
{
|
||
guchar *new_data;
|
||
HANDLE new_handle = GlobalReAlloc (priv->handle, priv->data_allocated_size + spillover, 0);
|
||
|
||
if (new_handle != NULL)
|
||
{
|
||
new_data = g_try_realloc (priv->data, priv->data_allocated_size + spillover);
|
||
|
||
if (new_data != NULL)
|
||
{
|
||
priv->handle = new_handle;
|
||
priv->data = new_data;
|
||
priv->data_allocated_size += spillover;
|
||
}
|
||
else
|
||
{
|
||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("g_try_realloc () failed"));
|
||
return -1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DWORD error_code = GetLastError ();
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
"%s%lu", _("GlobalReAlloc() failed: "), error_code);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (priv->handle_is_buffer)
|
||
{
|
||
to_copy = MIN (count, priv->data_allocated_size - priv->data_length);
|
||
|
||
if (to_copy == 0)
|
||
{
|
||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("Ran out of buffer space (buffer size is fixed)"));
|
||
return -1;
|
||
}
|
||
|
||
memcpy (&((guchar *) priv->handle)[priv->data_length], buffer, to_copy);
|
||
}
|
||
else
|
||
memcpy (&priv->data[priv->data_length], buffer, to_copy);
|
||
|
||
priv->data_length += to_copy;
|
||
|
||
return to_copy;
|
||
}
|
||
|
||
static gssize
|
||
gdk_win32_hdata_output_stream_write (GOutputStream *output_stream,
|
||
const void *buffer,
|
||
gsize count,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
GdkWin32HDataOutputStream *stream = GDK_WIN32_HDATA_OUTPUT_STREAM (output_stream);
|
||
GdkWin32HDataOutputStreamPrivate *priv = gdk_win32_hdata_output_stream_get_instance_private (stream);
|
||
gssize result = write_stream (stream, priv, buffer, count, error);
|
||
|
||
if (result != -1)
|
||
GDK_NOTE(SELECTION, g_printerr ("CLIPBOARD: wrote %zd bytes, %" G_GSIZE_FORMAT " total now\n",
|
||
result, priv->data_length));
|
||
|
||
return result;
|
||
}
|
||
|
||
static void
|
||
gdk_win32_hdata_output_stream_write_async (GOutputStream *output_stream,
|
||
const void *buffer,
|
||
gsize count,
|
||
int io_priority,
|
||
GCancellable *cancellable,
|
||
GAsyncReadyCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
GdkWin32HDataOutputStream *stream = GDK_WIN32_HDATA_OUTPUT_STREAM (output_stream);
|
||
GdkWin32HDataOutputStreamPrivate *priv = gdk_win32_hdata_output_stream_get_instance_private (stream);
|
||
GTask *task;
|
||
gssize result;
|
||
GError *error = NULL;
|
||
|
||
task = g_task_new (stream, cancellable, callback, user_data);
|
||
g_task_set_source_tag (task, gdk_win32_hdata_output_stream_write_async);
|
||
g_task_set_priority (task, io_priority);
|
||
|
||
result = write_stream (stream, priv, buffer, count, &error);
|
||
|
||
if (result != -1)
|
||
{
|
||
GDK_NOTE (SELECTION, g_printerr ("CLIPBOARD async wrote %zd bytes, %" G_GSIZE_FORMAT " total now\n",
|
||
result, priv->data_length));
|
||
g_task_return_int (task, result);
|
||
}
|
||
else
|
||
g_task_return_error (task, error);
|
||
|
||
g_object_unref (task);
|
||
|
||
return;
|
||
}
|
||
|
||
static gssize
|
||
gdk_win32_hdata_output_stream_write_finish (GOutputStream *stream,
|
||
GAsyncResult *result,
|
||
GError **error)
|
||
{
|
||
g_return_val_if_fail (g_task_is_valid (result, stream), -1);
|
||
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_win32_hdata_output_stream_write_async, -1);
|
||
|
||
return g_task_propagate_int (G_TASK (result), error);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_win32_hdata_output_stream_close (GOutputStream *output_stream,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
GdkWin32HDataOutputStream *stream = GDK_WIN32_HDATA_OUTPUT_STREAM (output_stream);
|
||
GdkWin32HDataOutputStreamPrivate *priv = gdk_win32_hdata_output_stream_get_instance_private (stream);
|
||
guchar *hdata;
|
||
|
||
if (priv->closed)
|
||
return TRUE;
|
||
|
||
if (priv->pair.transmute)
|
||
{
|
||
guchar *transmuted_data = NULL;
|
||
gsize transmuted_data_length;
|
||
|
||
if (priv->handle_is_buffer)
|
||
{
|
||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("Can’t transmute a single handle"));
|
||
return FALSE;
|
||
}
|
||
|
||
if (!gdk_win32_clipdrop_transmute_contentformat (priv->clipdrop,
|
||
priv->pair.contentformat,
|
||
priv->pair.w32format,
|
||
priv->data,
|
||
priv->data_length,
|
||
&transmuted_data,
|
||
&transmuted_data_length))
|
||
{
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("Failed to transmute %zu bytes of data from %s to %u"),
|
||
priv->data_length,
|
||
priv->pair.contentformat,
|
||
priv->pair.w32format);
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
HANDLE new_handle;
|
||
|
||
new_handle = GlobalReAlloc (priv->handle, transmuted_data_length, 0);
|
||
|
||
if (new_handle == NULL)
|
||
{
|
||
DWORD error_code = GetLastError ();
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
"%s%lu", _("GlobalReAlloc() failed: "), error_code);
|
||
return FALSE;
|
||
}
|
||
|
||
priv->handle = new_handle;
|
||
priv->data_length = transmuted_data_length;
|
||
g_clear_pointer (&priv->data, g_free);
|
||
priv->data = transmuted_data;
|
||
}
|
||
}
|
||
|
||
if (!priv->handle_is_buffer)
|
||
{
|
||
hdata = GlobalLock (priv->handle);
|
||
|
||
if (hdata == NULL)
|
||
{
|
||
DWORD error_code = GetLastError ();
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
"%s%lu", _("GlobalLock() failed: "), error_code);
|
||
return FALSE;
|
||
}
|
||
|
||
memcpy (hdata, priv->data, priv->data_length);
|
||
GlobalUnlock (priv->handle);
|
||
g_clear_pointer (&priv->data, g_free);
|
||
}
|
||
|
||
priv->closed = 1;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gdk_win32_hdata_output_stream_close_async (GOutputStream *stream,
|
||
int io_priority,
|
||
GCancellable *cancellable,
|
||
GAsyncReadyCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
GTask *task;
|
||
GError *error = NULL;
|
||
|
||
task = g_task_new (stream, cancellable, callback, user_data);
|
||
g_task_set_source_tag (task, gdk_win32_hdata_output_stream_close_async);
|
||
g_task_set_priority (task, io_priority);
|
||
|
||
if (gdk_win32_hdata_output_stream_close (stream, NULL, &error))
|
||
g_task_return_boolean (task, TRUE);
|
||
else
|
||
g_task_return_error (task, error);
|
||
|
||
g_object_unref (task);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_win32_hdata_output_stream_close_finish (GOutputStream *stream,
|
||
GAsyncResult *result,
|
||
GError **error)
|
||
{
|
||
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
|
||
g_return_val_if_fail (g_async_result_is_tagged (result, gdk_win32_hdata_output_stream_close_async), FALSE);
|
||
|
||
return g_task_propagate_boolean (G_TASK (result), error);
|
||
}
|
||
|
||
static void
|
||
gdk_win32_hdata_output_stream_finalize (GObject *object)
|
||
{
|
||
GdkWin32HDataOutputStream *stream = GDK_WIN32_HDATA_OUTPUT_STREAM (object);
|
||
GdkWin32HDataOutputStreamPrivate *priv = gdk_win32_hdata_output_stream_get_instance_private (stream);
|
||
|
||
g_clear_pointer (&priv->data, g_free);
|
||
|
||
/* We deliberately don't close the memory object,
|
||
* as it will be used elsewhere (it's a shame that
|
||
* MS global memory handles are not refcounted and
|
||
* not duplicateable).
|
||
* Except when the stream isn't closed, which means
|
||
* that the caller never bothered to get the handle.
|
||
*/
|
||
if (!priv->closed && priv->handle)
|
||
{
|
||
if (_gdk_win32_format_uses_hdata (priv->pair.w32format))
|
||
GlobalFree (priv->handle);
|
||
else
|
||
CloseHandle (priv->handle);
|
||
}
|
||
|
||
G_OBJECT_CLASS (gdk_win32_hdata_output_stream_parent_class)->finalize (object);
|
||
}
|
||
|
||
static void
|
||
gdk_win32_hdata_output_stream_class_init (GdkWin32HDataOutputStreamClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
|
||
|
||
object_class->finalize = gdk_win32_hdata_output_stream_finalize;
|
||
|
||
output_stream_class->write_fn = gdk_win32_hdata_output_stream_write;
|
||
output_stream_class->close_fn = gdk_win32_hdata_output_stream_close;
|
||
|
||
output_stream_class->write_async = gdk_win32_hdata_output_stream_write_async;
|
||
output_stream_class->write_finish = gdk_win32_hdata_output_stream_write_finish;
|
||
output_stream_class->close_async = gdk_win32_hdata_output_stream_close_async;
|
||
output_stream_class->close_finish = gdk_win32_hdata_output_stream_close_finish;
|
||
}
|
||
|
||
static void
|
||
gdk_win32_hdata_output_stream_init (GdkWin32HDataOutputStream *stream)
|
||
{
|
||
}
|
||
|
||
GOutputStream *
|
||
gdk_win32_hdata_output_stream_new (GdkWin32Clipdrop *clipdrop,
|
||
GdkWin32ContentFormatPair *pair,
|
||
GError **error)
|
||
{
|
||
GdkWin32HDataOutputStream *stream;
|
||
GdkWin32HDataOutputStreamPrivate *priv;
|
||
HANDLE handle;
|
||
gboolean hmem;
|
||
|
||
hmem = _gdk_win32_format_uses_hdata (pair->w32format);
|
||
|
||
if (hmem)
|
||
{
|
||
handle = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, 0);
|
||
|
||
if (handle == NULL)
|
||
{
|
||
DWORD error_code = GetLastError ();
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
"%s%lu", _("GlobalAlloc() failed: "), error_code);
|
||
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
stream = g_object_new (GDK_TYPE_WIN32_HDATA_OUTPUT_STREAM, NULL);
|
||
priv = gdk_win32_hdata_output_stream_get_instance_private (stream);
|
||
priv->clipdrop = clipdrop;
|
||
priv->pair = *pair;
|
||
|
||
if (hmem)
|
||
{
|
||
priv->handle = handle;
|
||
}
|
||
else
|
||
{
|
||
priv->data_allocated_size = sizeof (priv->handle);
|
||
priv->handle_is_buffer = 1;
|
||
}
|
||
|
||
return G_OUTPUT_STREAM (stream);
|
||
}
|
||
|
||
HANDLE
|
||
gdk_win32_hdata_output_stream_get_handle (GdkWin32HDataOutputStream *stream,
|
||
gboolean *is_hdata)
|
||
{
|
||
GdkWin32HDataOutputStreamPrivate *priv;
|
||
priv = gdk_win32_hdata_output_stream_get_instance_private (stream);
|
||
|
||
if (!priv->closed)
|
||
return NULL;
|
||
|
||
if (is_hdata)
|
||
*is_hdata = _gdk_win32_format_uses_hdata (priv->pair.w32format);
|
||
|
||
return priv->handle;
|
||
}
|