/* 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;
}
}
else
handle = NULL; /* silence compiler */
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;
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland