forked from AuroraMiddleware/gtk
402 lines
13 KiB
C
402 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 "gdkintl.h"
|
||
#include "gdkwin32display.h"
|
||
#include "gdkwin32surface.h"
|
||
|
||
|
||
typedef struct _GdkWin32HDataOutputStreamPrivate GdkWin32HDataOutputStreamPrivate;
|
||
|
||
struct _GdkWin32HDataOutputStreamPrivate
|
||
{
|
||
HANDLE handle;
|
||
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_transmute_contentformat (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 (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->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;
|
||
}
|