blob: 38b9183192e67b71df6878a002e0d0cd07ad209d [file] [log] [blame]
jacquelinecchuang617fd262024-04-19 03:11:14 +08001/*
2 * BRLTTY - A background process providing access to the console screen (when in
3 * text mode) for a blind person using a refreshable braille display.
4 *
5 * Copyright (C) 1995-2023 by The BRLTTY Developers.
6 *
7 * BRLTTY comes with ABSOLUTELY NO WARRANTY.
8 *
9 * This is free software, placed under the terms of the
10 * GNU Lesser General Public License, as published by the Free Software
11 * Foundation; either version 2.1 of the License, or (at your option) any
12 * later version. Please see the file LICENSE-LGPL for details.
13 *
14 * Web Page: http://brltty.app/
15 *
16 * This software is maintained by Dave Mielke <dave@mielke.cc>.
17 */
18
19#include "prologue.h"
20
21#include <string.h>
22#include <ctype.h>
23
24#include "log.h"
25#include "clipboard.h"
26#include "utf8.h"
27#include "queue.h"
28#include "lock.h"
29#include "program.h"
30#include "api_control.h"
31
32typedef struct {
33 wchar_t *characters;
34 size_t length;
35} HistoryEntry;
36
37struct ClipboardObjectStruct {
38 struct {
39 wchar_t *characters;
40 size_t size;
41 size_t length;
42 } buffer;
43
44 struct {
45 Queue *queue;
46 } history;
47};
48
49const wchar_t *
50getClipboardHistory (ClipboardObject *cpb, unsigned int index, size_t *length) {
51 Element *element = getStackElement(cpb->history.queue, index);
52 if (!element) return NULL;
53
54 const HistoryEntry *entry = getElementItem(element);
55 *length = entry->length;
56 return entry->characters;
57}
58
59int
60addClipboardHistory (ClipboardObject *cpb, const wchar_t *characters, size_t length) {
61 if (!length) return 1;
62
63 Queue *queue = cpb->history.queue;
64 Element *element = getStackHead(queue);
65
66 if (element) {
67 const HistoryEntry *entry = getElementItem(element);
68
69 if (length == entry->length) {
70 if (wmemcmp(characters, entry->characters, length) == 0) {
71 return 1;
72 }
73 }
74 }
75
76 {
77 HistoryEntry *entry;
78
79 if ((entry = malloc(sizeof(*entry)))) {
80 if ((entry->characters = allocateCharacters(length))) {
81 wmemcpy(entry->characters, characters, length);
82 entry->length = length;
83
84 if (enqueueItem(queue, entry)) {
85 return 1;
86 }
87
88 free(entry->characters);
89 } else {
90 logMallocError();
91 }
92
93 free(entry);
94 } else {
95 logMallocError();
96 }
97 }
98
99 return 0;
100}
101
102const wchar_t *
103getClipboardContent (ClipboardObject *cpb, size_t *length) {
104 *length = cpb->buffer.length;
105 return cpb->buffer.characters;
106}
107
108char *
109getClipboardContentUTF8 (ClipboardObject *cpb) {
110 size_t length;
111 const wchar_t *characters = getClipboardContent(cpb, &length);
112 return getUtf8FromWchars(characters, length, NULL);
113}
114
115size_t
116getClipboardContentLength (ClipboardObject *cpb) {
117 return cpb->buffer.length;
118}
119
120int
121truncateClipboardContent (ClipboardObject *cpb, size_t length) {
122 if (length >= cpb->buffer.length) return 0;
123 cpb->buffer.length = length;
124 return 1;
125}
126
127int
128clearClipboardContent (ClipboardObject *cpb) {
129 size_t length;
130 const wchar_t *characters = getClipboardContent(cpb, &length);
131
132 if (!addClipboardHistory(cpb, characters, length)) return 0;
133 truncateClipboardContent(cpb, 0);
134 return 1;
135}
136
137int
138appendClipboardContent (ClipboardObject *cpb, const wchar_t *characters, size_t length) {
139 size_t newLength = cpb->buffer.length + length;
140
141 if (newLength > cpb->buffer.size) {
142 size_t newSize = newLength | 0XFF;
143 wchar_t *newCharacters = allocateCharacters(newSize);
144
145 if (!newCharacters) {
146 logMallocError();
147 return 0;
148 }
149
150 wmemcpy(newCharacters, cpb->buffer.characters, cpb->buffer.length);
151 if (cpb->buffer.characters) free(cpb->buffer.characters);
152 cpb->buffer.characters = newCharacters;
153 cpb->buffer.size = newSize;
154 }
155
156 wmemcpy(&cpb->buffer.characters[cpb->buffer.length], characters, length);
157 cpb->buffer.length += length;
158 return 1;
159}
160
161int
162setClipboardContent (ClipboardObject *cpb, const wchar_t *characters, size_t length) {
163 int truncated = truncateClipboardContent(cpb, 0);
164 int appended = appendClipboardContent(cpb, characters, length);
165 return truncated || appended;
166}
167
168int
169appendClipboardContentUTF8 (ClipboardObject *cpb, const char *text) {
170 size_t length = strlen(text);
171 wchar_t characters[length + 1];
172 size_t count = makeWcharsFromUtf8(text, characters, ARRAY_COUNT(characters));
173 return appendClipboardContent(cpb, characters, count);
174}
175
176int
177setClipboardContentUTF8 (ClipboardObject *cpb, const char *text) {
178 int truncated = truncateClipboardContent(cpb, 0);
179 int appended = appendClipboardContentUTF8(cpb, text);
180 return truncated || appended;
181}
182
183static void
184deallocateClipboardHistoryEntry (void *item, void *data) {
185 HistoryEntry *entry = item;
186 if (entry->characters) free(entry->characters);
187 free(entry);
188}
189
190ClipboardObject *
191newClipboard (void) {
192 ClipboardObject *cpb;
193
194 if ((cpb = malloc(sizeof(*cpb)))) {
195 memset(cpb, 0, sizeof(*cpb));
196
197 cpb->buffer.characters = NULL;
198 cpb->buffer.size = 0;
199 cpb->buffer.length = 0;
200
201 if ((cpb->history.queue = newQueue(deallocateClipboardHistoryEntry, NULL))) {
202 return cpb;
203 }
204 } else {
205 logMallocError();
206 }
207
208 return NULL;
209}
210
211void
212destroyClipboard (ClipboardObject *cpb) {
213 if (cpb->buffer.characters) free(cpb->buffer.characters);
214 deallocateQueue(cpb->history.queue);
215 free(cpb);
216}
217
218static LockDescriptor *
219getMainClipboardLock (void) {
220 static LockDescriptor *lock = NULL;
221 return getLockDescriptor(&lock, "main-clipboard");
222}
223
224void
225lockMainClipboard (void) {
226 obtainExclusiveLock(getMainClipboardLock());
227}
228
229void
230unlockMainClipboard (void) {
231 releaseLock(getMainClipboardLock());
232}
233
234static void
235exitMainClipboard (void *data) {
236 ClipboardObject **clipboard = data;
237
238 lockMainClipboard();
239 destroyClipboard(*clipboard);
240 *clipboard = NULL;
241 unlockMainClipboard();
242}
243
244ClipboardObject *
245getMainClipboard (void) {
246 static ClipboardObject *clipboard = NULL;
247
248 lockMainClipboard();
249 if (!clipboard) {
250 if ((clipboard = newClipboard())) {
251 onProgramExit("main-clipboard", exitMainClipboard, &clipboard);
252 }
253 }
254 unlockMainClipboard();
255
256 return clipboard;
257}
258
259void
260onMainClipboardUpdated (void) {
261 api.updateParameter(BRLAPI_PARAM_CLIPBOARD_CONTENT, 0);
262}
263
264int
265setMainClipboardContent (const char *content) {
266 ClipboardObject *cpb = getMainClipboard();
267 int updated;
268
269 lockMainClipboard();
270 updated = setClipboardContentUTF8(cpb, content);
271 unlockMainClipboard();
272
273 if (updated) onMainClipboardUpdated();
274 return updated;
275}
276
277char *
278getMainClipboardContent (void) {
279 ClipboardObject *cpb = getMainClipboard();
280 char *content;
281
282 lockMainClipboard();
283 content = getClipboardContentUTF8(cpb);
284 unlockMainClipboard();
285
286 return content;
287}