Files

71 lines
2.5 KiB
C

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
#include "watch.h"
static GQueue changes = G_QUEUE_INIT;
static gboolean watchEnabled;
static unsigned long long watchEpoch;
void clipboard_watch_free(ClipboardChange *change) {
if (--change->references) return;
g_free(change->text);
g_free(change);
}
static void text_received(GtkClipboard *clipboard, const gchar *text, gpointer data) {
(void)clipboard;
ClipboardChange *change = data;
change->text = g_strdup(text);
change->done = TRUE;
clipboard_watch_free(change);
}
static void targets_received(GtkClipboard *clipboard, GdkAtom *targets, gint count, gpointer data) {
ClipboardChange *change = data;
if (!watchEnabled || watchEpoch != change->epoch || !targets ||
gtk_targets_include_image(targets, count, FALSE) ||
gtk_targets_include_uri(targets, count) ||
!gtk_targets_include_text(targets, count)) {
change->done = TRUE;
clipboard_watch_free(change);
return;
}
// No image or file payload is requested, only a text target.
gtk_clipboard_request_text(clipboard, text_received, change);
}
static void owner_changed(GtkClipboard *clipboard, GdkEventOwnerChange *event, gpointer data) {
(void)data;
if (!watchEnabled) return;
if (g_queue_get_length(&changes) >= 128)
clipboard_watch_free(g_queue_pop_head(&changes));
ClipboardChange *change = g_new0(ClipboardChange, 1);
change->epoch = watchEpoch;
change->timestamp = event->time;
change->deadline = g_get_monotonic_time() + 3 * G_TIME_SPAN_SECOND;
change->references = 2; // queue and asynchronous callback
g_queue_push_tail(&changes, change);
gtk_clipboard_request_targets(clipboard, targets_received, change);
}
int clipboard_watch_init(void) {
if (!gtk_init_check(NULL, NULL)) return 0;
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
if (!clipboard) return 0;
g_signal_connect(clipboard, "owner-change", G_CALLBACK(owner_changed), NULL);
return 1;
}
void clipboard_watch_enable(int enabled, unsigned long long epoch) {
watchEnabled = enabled;
watchEpoch = epoch;
}
void clipboard_watch_iterate(void) {
while (gtk_events_pending()) gtk_main_iteration_do(FALSE);
}
ClipboardChange *clipboard_watch_next(void) {
ClipboardChange *change = g_queue_peek_head(&changes);
if (!change || (!change->done && g_get_monotonic_time() < change->deadline)) return NULL;
return g_queue_pop_head(&changes);
}