How to program a simple Clipboard-Textreceiver in C?

Hi

I want a simple clipboard text-receiver.

If I copy something in any program, my program here should receive it and shows it in the textview.

But i get here an endless-loop. My solution is (not here in this example), to
compare the new text with the old text, and only hold the new text, if it is
different. But I am not happy with that.

I want, that the system calls my clipboard-function (clipboard_callback), when
somewhere text is copied and then only inform my program, if a new text is
copied !

In clipboard_callback I have to call again gtk_clipboard_wait_for_text or
gtk_clipboard_request_text. Otherwise the System calls my function only one
time and I had to switch my programm off and on again.

But my program should receive text as long as it is activated.

Here my example:


#include <stdbool.h>
#include <gtk/gtk.h>

 
 
/*
    Structure:

    GtkWindow
        GtkFixed
            GtkButton
            GtkScrolledWindow
                 GtkTextView

    What i want:
    
    I want a simple clipboard text-receiver.
    
    If I copy something in any program, my program here should receive it and shows it in the textview.
    
    But i get here an endless-loop. My solution is (not here in this example), to compare the new text with
    the old text, and only hold the new text, if it is different. But I am not happy with that.
    
    I want, that the system calls my clipboard-function (clipboard_callback), when somewhere text is copied
    and then only inform my program, is a new text is copied !
    
    In clipboard_callback I have to call again gtk_clipboard_wait_for_text or gtk_clipboard_request_text.
    Otherwise the System calls my function only one time and I had to switch my programm off and on again.
    
    But my program should receive text as long as it is activated.
    

            
 
    I tried gtk_clipboard_wait_for_text instead of gtk_clipboard_request_text : Same result
        
       

*/




GtkWidget *Window,*fixed,*button_start,
            *TV, // Textview: It receives the Clipboard-Content
            *SW, // ScrolledWindow for the Textview
            *label_counting, // Shows, whether there is that unwanted endless-loop. It never should counting
            *label_infos; // Descriptiontext for the counting-label


GdkDisplay *display;

GtkTextBuffer *TVB;

GtkClipboard * Clipboard;

bool Startbutton_clicked;
unsigned long count;

bool callback__button_start(void);
void clipboard_callback(GtkClipboard *gClipboardP,const gchar *P,gpointer data);



int main(void)
{
    gtk_init(NULL,NULL);

    Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(Window),800,400);
    g_signal_connect(Window,"delete-event",(GCallback)gtk_main_quit,NULL);
    g_signal_connect(Window,"destroy",(GCallback)gtk_main_quit,NULL);

    fixed = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(Window),fixed);

    label_infos = gtk_label_new(""); 
    gtk_widget_set_margin_start(label_infos,255);
    gtk_widget_set_margin_top(label_infos,10);
    gtk_container_add(GTK_CONTAINER(fixed),label_infos);

    label_counting = gtk_label_new(""); 
    gtk_widget_set_margin_start(label_counting,255);
    gtk_widget_set_margin_top(label_counting,30);
    gtk_container_add(GTK_CONTAINER(fixed),label_counting);
        
    button_start = gtk_button_new_with_label("ON");
    gtk_container_add(GTK_CONTAINER(fixed),button_start);
    gtk_widget_set_margin_start(button_start,10);
    gtk_widget_set_margin_top(button_start,10);
    
    g_signal_connect((GtkWidget *)button_start,"clicked",(GCallback)callback__button_start,NULL);
                

    SW = gtk_scrolled_window_new(NULL,NULL);                   
    gtk_container_add(GTK_CONTAINER(fixed),SW);
    gtk_widget_set_size_request(GTK_WIDGET(SW), 200, 200);
    gtk_widget_set_margin_start(SW,5);
    gtk_widget_set_margin_top(SW,100);


    TV = gtk_text_view_new(); 
    gtk_container_add(GTK_CONTAINER(SW),TV);            


    TVB = gtk_text_view_get_buffer((GtkTextView*)TV);                
    gtk_text_buffer_set_text (TVB,"empty",-1);     



    display = gdk_display_get_default();    // For my experiments for gtk_clipboard_get_for_display
                                            // and gtk_clipboard_get_default instead of gtk_clipboard_get
                                            // But the problem still exists.


    Clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);//GDK_SELECTION_TYPE_STRING     GDK_SELECTION_CLIPBOARD 

    // I tried GDK_SELECTION_TYPE_STRING)   and   GDK_SELECTION_TYPE_STRING
    //Clipboard = gtk_clipboard_get_for_display(display,
    //                             GDK_SELECTION_TYPE_STRING);//GDK_SELECTION_TYPE_STRING

    //Clipboard = gtk_clipboard_get_default(display);




    Startbutton_clicked = false;

    gtk_widget_show_all(Window);
    gtk_main();
}



                    
                    
                    
bool callback__button_start(void)
{
    if(Startbutton_clicked == true)
    {
        Startbutton_clicked = false;
        
        gtk_label_set_text ((GtkLabel*)label_infos,"");
        gtk_label_set_text ((GtkLabel*)label_counting,"");
        
        gtk_button_set_label((GtkButton*)button_start,"ON");
    }
    else
    {
        Startbutton_clicked = true;
        count = 0;
        
        gtk_button_set_label((GtkButton*)button_start,"Off");
        
        gtk_label_set_text ((GtkLabel*)label_infos,"counting numbers here shows the unwanted loop:");

        
        
      //  gtk_clipboard_wait_for_text (Clipboard);
        
        gtk_clipboard_request_text(Clipboard,(GtkClipboardTextReceivedFunc)clipboard_callback,NULL);    
    }
    
    return true;
}



void clipboard_callback(GtkClipboard *gClipboardP,const gchar *P,gpointer data)
{
    char countstr[10];

    
    if(strlen(P) > 0)
    {
        if(Startbutton_clicked == true)
        {
            //printf("Test: %s\n",(char*)P);
              
            count ++;

            sprintf(countstr,"%ld",count);
            gtk_label_set_text ((GtkLabel*)label_counting,countstr);
                    
            gtk_text_buffer_set_text (TVB,P,-1);               
              
            // Experiment : But it does not solve the problem
            //Clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);//GDK_SELECTION_TYPE_STRING     GDK_SELECTION_CLIPBOARD 

            // gtk_clipboard_clear(gClipboardP); // Experiment : But it does not solve the problem
 
            //gtk_clipboard_wait_for_text(Clipboard);
            gtk_clipboard_request_text(gClipboardP,(GtkClipboardTextReceivedFunc)clipboard_callback,NULL);
          }
    }
}




In case my problem was not clear:

I want, that Linux calls my program, when something is in the clipboard.
I get the content then and then Linux will call my callback-function only then,
when something NEW is copied into the clipboard. So that Linux call only one
time my callback-function, and it is waiting then for the new one.
I tried it with an idle-function … but as soon something is in the clipboard, I
get the content every time. This is, what I call “endless-loop” here.

So I want Linux to deliver only once and then only again when there is a new one. So that it remembers that it has already delivered my program.

Hello, @gerd_breuer! You should connect to the owner-shanged signal to be notified whenever the clipboard content changes.

That will work on X11 for sure, but I don’t know about Wayland. Perhaps you can run your program with the environment variable GDK_BACKEND=x11

Try this sample:

#include <gtk/gtk.h>

GtkTextBuffer *text_buffer;
char *last_text;

static void
add_text (const char *text)
{
  gtk_text_buffer_set_text (text_buffer, text, -1);
}

static void
clipboard_text_received (GtkClipboard *clipboard,
                         const char   *text,
                         gpointer      user_data)
{
  if (!g_utf8_validate (text, -1, NULL))
    return;

  if (!last_text || g_strcmp0 (last_text, text) != 0)
    {
      g_free (last_text);
      last_text = g_strdup (text);

      add_text (text);
    }
}

static void
clipboard_changed (GtkClipboard *clipboard,
                   GdkEvent     *event,
                   gpointer      user_data)
{
  GdkEventOwnerChange *event_owner_change = (GdkEventOwnerChange*) event;

  if (event->type != GDK_OWNER_CHANGE)
    return;

  if (event_owner_change->reason == GDK_OWNER_CHANGE_NEW_OWNER)
    {
      gtk_clipboard_request_text (clipboard, clipboard_text_received, NULL);
    }
}

static void
toggled (GtkToggleButton *toggle,
         gpointer user_data)
{
  GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);

  if (gtk_toggle_button_get_active (toggle))
    {
      g_signal_connect (clipboard, "owner-change", G_CALLBACK (clipboard_changed), NULL);
    }
  else
    {
      g_signal_handlers_disconnect_by_func (clipboard, clipboard_changed, NULL);
    }
}

int main()
{
  GtkWidget *window;
  GtkWidget *grid;
  GtkWidget *toggle;
  GtkWidget *scrolled;
  GtkWidget *text_view;

  gtk_init (NULL, NULL);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  grid = gtk_grid_new ();
  toggle = gtk_toggle_button_new_with_label ("Watch");
  scrolled = gtk_scrolled_window_new (NULL, NULL);
  text_view = gtk_text_view_new ();

  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));

  gtk_window_set_default_size (GTK_WINDOW (window), 450, 300);

  gtk_widget_set_margin_top (toggle, 10);
  gtk_widget_set_margin_bottom (toggle, 10);

  gtk_widget_set_halign (toggle, GTK_ALIGN_CENTER);
  gtk_widget_set_valign (toggle, GTK_ALIGN_CENTER);

  gtk_widget_set_hexpand (text_view, TRUE);
  gtk_widget_set_vexpand (text_view, TRUE);
  gtk_widget_set_halign (text_view, GTK_ALIGN_FILL);
  gtk_widget_set_halign (text_view, GTK_ALIGN_FILL);

  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);

  gtk_container_add (GTK_CONTAINER (scrolled), text_view);

  gtk_grid_attach (GTK_GRID (grid), toggle, 0, 0, 1, 1);
  gtk_grid_attach (GTK_GRID (grid), scrolled, 0, 1, 1, 1);

  gtk_container_add (GTK_CONTAINER (window), grid);

  g_signal_connect (toggle, "toggled", G_CALLBACK (toggled), NULL);
  g_signal_connect (window, "destroy", gtk_main_quit, NULL);

  gtk_widget_show_all (window);
  gtk_main ();
}

In case you’re using Wayland, be sure to run with the environment variable GDK_BACKEND=x11

1 Like

Hi lb90

It works. You solved my problem. Thank you !

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.