71 lines
2.5 KiB
C
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);
|
|
}
|