LCOV - code coverage report
Current view: top level - src/plugins/gtk - valent-gdk-clipboard.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 67.2 % 125 84
Test Date: 2025-11-28 01:07:15 Functions: 85.7 % 14 12
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 33.3 % 138 46

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-3.0-or-later
       2                 :             : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
       3                 :             : 
       4                 :             : #define G_LOG_DOMAIN "valent-gdk-clipboard"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <gdk/gdk.h>
       9                 :             : #include <valent.h>
      10                 :             : 
      11                 :             : #include "valent-gdk-clipboard.h"
      12                 :             : 
      13                 :             : #define CLIPBOARD_MAXSIZE (16 * 1024)
      14                 :             : 
      15                 :             : 
      16                 :             : struct _ValentGdkClipboard
      17                 :             : {
      18                 :             :   ValentClipboardAdapter  parent_instance;
      19                 :             : 
      20                 :             :   GdkClipboard           *clipboard;
      21                 :             :   int64_t                 timestamp;
      22                 :             : };
      23                 :             : 
      24   [ +  +  +  - ]:          20 : G_DEFINE_FINAL_TYPE (ValentGdkClipboard, valent_gdk_clipboard, VALENT_TYPE_CLIPBOARD_ADAPTER)
      25                 :             : 
      26                 :             : static const char * const text_mimetypes[] = {
      27                 :             :   "text/plain;charset=utf-8",
      28                 :             :   "text/plain",
      29                 :             :   "UTF8_STRING",
      30                 :             :   "STRING",
      31                 :             :   "TEXT",
      32                 :             :   "COMPOUND_TEXT",
      33                 :             :   NULL,
      34                 :             : };
      35                 :             : 
      36                 :             : 
      37                 :             : /*
      38                 :             :  * GdkClipboard Callbacks
      39                 :             :  */
      40                 :             : static void
      41                 :           3 : on_changed (GdkClipboard       *clipboard,
      42                 :             :             ValentGdkClipboard *self)
      43                 :             : {
      44                 :           3 :   ValentClipboardAdapter *adapter = VALENT_CLIPBOARD_ADAPTER (self);
      45                 :             : 
      46         [ -  + ]:           3 :   g_assert (VALENT_IS_GDK_CLIPBOARD (self));
      47         [ +  - ]:           3 :   g_assert (VALENT_IS_CLIPBOARD_ADAPTER (adapter));
      48                 :             : 
      49                 :             :   // TODO: get the actual TIMESTAMP value
      50                 :           3 :   self->timestamp = valent_timestamp_ms ();
      51                 :           3 :   valent_clipboard_adapter_changed (adapter);
      52                 :           3 : }
      53                 :             : 
      54                 :             : static void
      55                 :           0 : g_input_stream_read_bytes_cb (GInputStream *stream,
      56                 :             :                               GAsyncResult *result,
      57                 :             :                               gpointer      user_data)
      58                 :             : {
      59                 :           0 :   g_autoptr (GTask) task = G_TASK (user_data);
      60   [ #  #  #  # ]:           0 :   g_autoptr (GBytes) bytes = NULL;
      61         [ #  # ]:           0 :   g_autoptr (GError) error = NULL;
      62                 :             : 
      63                 :           0 :   bytes = g_input_stream_read_bytes_finish (stream, result, &error);
      64         [ #  # ]:           0 :   if (bytes == NULL)
      65                 :             :     {
      66                 :           0 :       g_task_return_error (task, g_steal_pointer (&error));
      67         [ #  # ]:           0 :       return;
      68                 :             :     }
      69                 :             : 
      70         [ #  # ]:           0 :   g_task_return_pointer (task,
      71                 :           0 :                          g_bytes_ref (bytes),
      72                 :             :                          (GDestroyNotify)g_bytes_unref);
      73                 :             : }
      74                 :             : 
      75                 :             : static void
      76                 :           0 : gdk_clipboard_read_cb (GdkClipboard *clipboard,
      77                 :             :                        GAsyncResult *result,
      78                 :             :                        gpointer      user_data)
      79                 :             : {
      80                 :           0 :   g_autoptr (GTask) task = G_TASK (user_data);
      81                 :           0 :   GCancellable *cancellable = g_task_get_cancellable (task);
      82         [ #  # ]:           0 :   g_autoptr (GInputStream) input = NULL;
      83                 :           0 :   const char *mimetype = NULL;
      84         [ #  # ]:           0 :   g_autoptr (GError) error = NULL;
      85                 :             : 
      86   [ #  #  #  #  :           0 :   g_assert (GDK_IS_CLIPBOARD (clipboard));
             #  #  #  # ]
      87         [ #  # ]:           0 :   g_assert (g_task_is_valid (result, clipboard));
      88                 :             : 
      89                 :           0 :   input = gdk_clipboard_read_finish (clipboard, result, &mimetype, &error);
      90         [ #  # ]:           0 :   if (input == NULL)
      91                 :             :     {
      92                 :           0 :       g_task_return_error (task, g_steal_pointer (&error));
      93         [ #  # ]:           0 :       return;
      94                 :             :     }
      95                 :             : 
      96         [ #  # ]:           0 :   g_input_stream_read_bytes_async (input,
      97                 :             :                                    G_PRIORITY_DEFAULT,
      98                 :             :                                    CLIPBOARD_MAXSIZE,
      99                 :             :                                    cancellable,
     100                 :             :                                    (GAsyncReadyCallback)g_input_stream_read_bytes_cb,
     101                 :             :                                    g_steal_pointer (&task));
     102                 :             : }
     103                 :             : 
     104                 :             : static void
     105                 :           3 : gdk_clipboard_read_text_cb (GdkClipboard *clipboard,
     106                 :             :                             GAsyncResult *result,
     107                 :             :                             gpointer      user_data)
     108                 :             : {
     109                 :           3 :   g_autoptr (GTask) task = G_TASK (user_data);
     110                 :           3 :   char *text = NULL;
     111                 :           3 :   GError *error = NULL;
     112                 :             : 
     113   [ +  -  +  -  :           3 :   g_assert (GDK_IS_CLIPBOARD (clipboard));
             +  -  -  + ]
     114         [ +  - ]:           3 :   g_assert (g_task_is_valid (result, clipboard));
     115                 :             : 
     116                 :           3 :   text = gdk_clipboard_read_text_finish (clipboard, result, &error);
     117         [ -  + ]:           3 :   if (text == NULL)
     118                 :             :     {
     119                 :           0 :       g_task_return_error (task, g_steal_pointer (&error));
     120         [ #  # ]:           0 :       return;
     121                 :             :     }
     122                 :             : 
     123         [ +  - ]:           3 :   g_task_return_pointer (task,
     124                 :           3 :                          g_bytes_new_take (text, strlen (text) + 1),
     125                 :             :                          (GDestroyNotify)g_bytes_unref);
     126                 :             : }
     127                 :             : 
     128                 :             : /*
     129                 :             :  * ValentClipboardAdapter
     130                 :             :  */
     131                 :             : static GStrv
     132                 :           4 : valent_gdk_clipboard_get_mimetypes (ValentClipboardAdapter *adapter)
     133                 :             : {
     134                 :           4 :   ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
     135                 :           4 :   GdkContentFormats *formats = NULL;
     136                 :           4 :   const char * const *mimetypes = NULL;
     137                 :             : 
     138         [ -  + ]:           4 :   g_assert (VALENT_IS_GDK_CLIPBOARD (self));
     139   [ +  -  +  -  :           4 :   g_return_val_if_fail (GDK_IS_CLIPBOARD (self->clipboard), NULL);
             +  -  +  - ]
     140                 :             : 
     141         [ -  + ]:           4 :   if ((formats = gdk_clipboard_get_formats (self->clipboard)) == NULL)
     142                 :             :     return NULL;
     143                 :             : 
     144                 :           4 :   mimetypes = gdk_content_formats_get_mime_types (formats, NULL);
     145                 :             : 
     146                 :           4 :   return g_strdupv ((char **)mimetypes);
     147                 :             : }
     148                 :             : 
     149                 :             : static int64_t
     150                 :           2 : valent_gdk_clipboard_get_timestamp (ValentClipboardAdapter *adapter)
     151                 :             : {
     152                 :           2 :   ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
     153                 :             : 
     154         [ -  + ]:           2 :   g_assert (VALENT_IS_GDK_CLIPBOARD (self));
     155   [ +  -  +  -  :           2 :   g_return_val_if_fail (GDK_IS_CLIPBOARD (self->clipboard), 0);
             +  -  +  - ]
     156                 :             : 
     157                 :           2 :   return self->timestamp;
     158                 :             : }
     159                 :             : 
     160                 :             : static void
     161                 :           3 : valent_gdk_clipboard_read_bytes (ValentClipboardAdapter *adapter,
     162                 :             :                                  const char             *mimetype,
     163                 :             :                                  GCancellable           *cancellable,
     164                 :             :                                  GAsyncReadyCallback     callback,
     165                 :             :                                  gpointer                user_data)
     166                 :             : {
     167                 :           3 :   ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
     168                 :           3 :   g_autoptr (GTask) task = NULL;
     169                 :           3 :   GdkContentFormats *formats = NULL;
     170                 :             : 
     171         [ -  + ]:           3 :   g_assert (VALENT_IS_GDK_CLIPBOARD (self));
     172   [ +  -  +  - ]:           3 :   g_assert (mimetype != NULL && *mimetype != '\0');
     173                 :             : 
     174   [ +  -  +  -  :           3 :   if G_UNLIKELY (!GDK_IS_CLIPBOARD (self->clipboard))
             +  -  -  + ]
     175                 :             :     {
     176                 :           0 :       g_task_report_new_error (adapter, callback, user_data,
     177                 :             :                                valent_gdk_clipboard_read_bytes,
     178                 :             :                                G_IO_ERROR,
     179                 :             :                                G_IO_ERROR_NOT_SUPPORTED,
     180                 :             :                                "Clipboard not available");
     181                 :           0 :       return;
     182                 :             :     }
     183                 :             : 
     184                 :             :   /* Special case for text */
     185         [ +  - ]:           3 :   if (g_strv_contains (text_mimetypes, mimetype))
     186                 :             :     {
     187                 :           3 :       task = g_task_new (adapter, cancellable, callback, user_data);
     188         [ +  - ]:           3 :       g_task_set_source_tag (task, valent_gdk_clipboard_read_bytes);
     189                 :             : 
     190                 :           3 :       gdk_clipboard_read_text_async (self->clipboard,
     191                 :             :                                      cancellable,
     192                 :             :                                      (GAsyncReadyCallback)gdk_clipboard_read_text_cb,
     193                 :             :                                      g_steal_pointer (&task));
     194                 :           3 :       return;
     195                 :             :     }
     196                 :             : 
     197         [ #  # ]:           0 :   if ((formats = gdk_clipboard_get_formats (self->clipboard)) == NULL)
     198                 :             :     {
     199                 :           0 :       g_task_report_new_error (adapter, callback, user_data,
     200                 :             :                                valent_gdk_clipboard_read_bytes,
     201                 :             :                                G_IO_ERROR,
     202                 :             :                                G_IO_ERROR_NOT_SUPPORTED,
     203                 :             :                                "Clipboard empty");
     204                 :           0 :       return;
     205                 :             :     }
     206                 :             : 
     207         [ #  # ]:           0 :   if (!gdk_content_formats_contain_mime_type (formats, mimetype))
     208                 :             :     {
     209                 :           0 :       g_task_report_new_error (adapter, callback, user_data,
     210                 :             :                                valent_gdk_clipboard_read_bytes,
     211                 :             :                                G_IO_ERROR,
     212                 :             :                                G_IO_ERROR_NOT_SUPPORTED,
     213                 :             :                                "%s format not available.",
     214                 :             :                                mimetype);
     215                 :           0 :       return;
     216                 :             :     }
     217                 :             : 
     218                 :           0 :   task = g_task_new (adapter, cancellable, callback, user_data);
     219         [ #  # ]:           0 :   g_task_set_source_tag (task, valent_gdk_clipboard_read_bytes);
     220         [ #  # ]:           0 :   g_task_set_task_data (task, g_strdup (mimetype), g_free);
     221                 :             : 
     222                 :           0 :   gdk_clipboard_read_async (self->clipboard,
     223                 :           0 :                             (const char *[]){ mimetype, NULL },
     224                 :             :                             G_PRIORITY_DEFAULT,
     225                 :             :                             cancellable,
     226                 :             :                             (GAsyncReadyCallback)gdk_clipboard_read_cb,
     227                 :             :                             g_steal_pointer (&task));
     228                 :             : }
     229                 :             : 
     230                 :             : static void
     231                 :           2 : valent_gdk_clipboard_write_bytes (ValentClipboardAdapter *adapter,
     232                 :             :                                   const char             *mimetype,
     233                 :             :                                   GBytes                 *bytes,
     234                 :             :                                   GCancellable           *cancellable,
     235                 :             :                                   GAsyncReadyCallback     callback,
     236                 :             :                                   gpointer                user_data)
     237                 :             : {
     238                 :           2 :   ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (adapter);
     239                 :           4 :   g_autoptr (GdkContentProvider) content = NULL;
     240   [ -  -  +  - ]:           2 :   g_autoptr (GTask) task = NULL;
     241                 :             : 
     242         [ -  + ]:           2 :   g_assert (VALENT_IS_GDK_CLIPBOARD (self));
     243   [ -  +  -  -  :           2 :   g_assert (bytes != NULL || (mimetype != NULL && *mimetype != '\0'));
                   -  - ]
     244   [ +  -  +  -  :           2 :   g_return_if_fail (GDK_IS_CLIPBOARD (self->clipboard));
             +  -  +  - ]
     245                 :             : 
     246         [ +  - ]:           2 :   if (bytes != NULL)
     247                 :           2 :     content = gdk_content_provider_new_for_bytes (mimetype, bytes);
     248                 :             : 
     249         [ -  + ]:           2 :   if (!gdk_clipboard_set_content (self->clipboard, content))
     250                 :             :     {
     251                 :           0 :       g_task_report_new_error (adapter, callback, user_data,
     252                 :             :                                valent_gdk_clipboard_write_bytes,
     253                 :             :                                G_IO_ERROR,
     254                 :             :                                G_IO_ERROR_FAILED,
     255                 :             :                                "Failed to set clipboard content");
     256         [ #  # ]:           0 :       return;
     257                 :             :     }
     258                 :             : 
     259                 :           2 :   task = g_task_new (adapter, cancellable, callback, user_data);
     260         [ +  - ]:           2 :   g_task_set_source_tag (task, valent_gdk_clipboard_write_bytes);
     261         [ +  - ]:           2 :   g_task_return_boolean (task, TRUE);
     262                 :             : }
     263                 :             : 
     264                 :             : /*
     265                 :             :  * ValentObject
     266                 :             :  */
     267                 :             : static void
     268                 :           2 : valent_gdk_clipboard_destroy (ValentObject *object)
     269                 :             : {
     270                 :           2 :   ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (object);
     271                 :             : 
     272         [ +  + ]:           2 :   if (self->clipboard != NULL)
     273                 :             :     {
     274                 :           1 :       g_signal_handlers_disconnect_by_data (self->clipboard, self);
     275                 :           1 :       self->clipboard = NULL;
     276                 :             :     }
     277                 :             : 
     278                 :           2 :   VALENT_OBJECT_CLASS (valent_gdk_clipboard_parent_class)->destroy (object);
     279                 :           2 : }
     280                 :             : 
     281                 :             : /*
     282                 :             :  * GObject
     283                 :             :  */
     284                 :             : static void
     285                 :           1 : valent_gdk_clipboard_constructed (GObject *object)
     286                 :             : {
     287                 :           1 :   ValentGdkClipboard *self = VALENT_GDK_CLIPBOARD (object);
     288                 :           1 :   GdkDisplay *display;
     289                 :             : 
     290                 :           1 :   G_OBJECT_CLASS (valent_gdk_clipboard_parent_class)->constructed (object);
     291                 :             : 
     292         [ +  - ]:           1 :   if ((display = gdk_display_get_default ()) != NULL)
     293                 :             :     {
     294                 :           1 :       self->clipboard = gdk_display_get_clipboard (display);
     295                 :           1 :       g_signal_connect_object (self->clipboard,
     296                 :             :                                "changed",
     297                 :             :                                G_CALLBACK (on_changed),
     298                 :             :                                self,
     299                 :             :                                G_CONNECT_DEFAULT);
     300                 :             :     }
     301                 :             :   else
     302                 :             :     {
     303                 :           0 :       valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
     304                 :             :                                              VALENT_PLUGIN_STATE_INACTIVE,
     305                 :             :                                              NULL);
     306                 :             :     }
     307                 :           1 : }
     308                 :             : 
     309                 :             : static void
     310                 :           2 : valent_gdk_clipboard_class_init (ValentGdkClipboardClass *klass)
     311                 :             : {
     312                 :           2 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     313                 :           2 :   ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
     314                 :           2 :   ValentClipboardAdapterClass *clipboard_class = VALENT_CLIPBOARD_ADAPTER_CLASS (klass);
     315                 :             : 
     316                 :           2 :   object_class->constructed = valent_gdk_clipboard_constructed;
     317                 :             : 
     318                 :           2 :   vobject_class->destroy = valent_gdk_clipboard_destroy;
     319                 :             : 
     320                 :           2 :   clipboard_class->get_mimetypes = valent_gdk_clipboard_get_mimetypes;
     321                 :           2 :   clipboard_class->get_timestamp = valent_gdk_clipboard_get_timestamp;
     322                 :           2 :   clipboard_class->read_bytes = valent_gdk_clipboard_read_bytes;
     323                 :           2 :   clipboard_class->write_bytes = valent_gdk_clipboard_write_bytes;
     324                 :             : }
     325                 :             : 
     326                 :             : static void
     327                 :           1 : valent_gdk_clipboard_init (ValentGdkClipboard *self)
     328                 :             : {
     329                 :           1 : }
     330                 :             : 
        

Generated by: LCOV version 2.0-1