/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include "log.h" #include "clipboard.h" #include "utf8.h" #include "queue.h" #include "lock.h" #include "program.h" #include "api_control.h" typedef struct { wchar_t *characters; size_t length; } HistoryEntry; struct ClipboardObjectStruct { struct { wchar_t *characters; size_t size; size_t length; } buffer; struct { Queue *queue; } history; }; const wchar_t * getClipboardHistory (ClipboardObject *cpb, unsigned int index, size_t *length) { Element *element = getStackElement(cpb->history.queue, index); if (!element) return NULL; const HistoryEntry *entry = getElementItem(element); *length = entry->length; return entry->characters; } int addClipboardHistory (ClipboardObject *cpb, const wchar_t *characters, size_t length) { if (!length) return 1; Queue *queue = cpb->history.queue; Element *element = getStackHead(queue); if (element) { const HistoryEntry *entry = getElementItem(element); if (length == entry->length) { if (wmemcmp(characters, entry->characters, length) == 0) { return 1; } } } { HistoryEntry *entry; if ((entry = malloc(sizeof(*entry)))) { if ((entry->characters = allocateCharacters(length))) { wmemcpy(entry->characters, characters, length); entry->length = length; if (enqueueItem(queue, entry)) { return 1; } free(entry->characters); } else { logMallocError(); } free(entry); } else { logMallocError(); } } return 0; } const wchar_t * getClipboardContent (ClipboardObject *cpb, size_t *length) { *length = cpb->buffer.length; return cpb->buffer.characters; } char * getClipboardContentUTF8 (ClipboardObject *cpb) { size_t length; const wchar_t *characters = getClipboardContent(cpb, &length); return getUtf8FromWchars(characters, length, NULL); } size_t getClipboardContentLength (ClipboardObject *cpb) { return cpb->buffer.length; } int truncateClipboardContent (ClipboardObject *cpb, size_t length) { if (length >= cpb->buffer.length) return 0; cpb->buffer.length = length; return 1; } int clearClipboardContent (ClipboardObject *cpb) { size_t length; const wchar_t *characters = getClipboardContent(cpb, &length); if (!addClipboardHistory(cpb, characters, length)) return 0; truncateClipboardContent(cpb, 0); return 1; } int appendClipboardContent (ClipboardObject *cpb, const wchar_t *characters, size_t length) { size_t newLength = cpb->buffer.length + length; if (newLength > cpb->buffer.size) { size_t newSize = newLength | 0XFF; wchar_t *newCharacters = allocateCharacters(newSize); if (!newCharacters) { logMallocError(); return 0; } wmemcpy(newCharacters, cpb->buffer.characters, cpb->buffer.length); if (cpb->buffer.characters) free(cpb->buffer.characters); cpb->buffer.characters = newCharacters; cpb->buffer.size = newSize; } wmemcpy(&cpb->buffer.characters[cpb->buffer.length], characters, length); cpb->buffer.length += length; return 1; } int setClipboardContent (ClipboardObject *cpb, const wchar_t *characters, size_t length) { int truncated = truncateClipboardContent(cpb, 0); int appended = appendClipboardContent(cpb, characters, length); return truncated || appended; } int appendClipboardContentUTF8 (ClipboardObject *cpb, const char *text) { size_t length = strlen(text); wchar_t characters[length + 1]; size_t count = makeWcharsFromUtf8(text, characters, ARRAY_COUNT(characters)); return appendClipboardContent(cpb, characters, count); } int setClipboardContentUTF8 (ClipboardObject *cpb, const char *text) { int truncated = truncateClipboardContent(cpb, 0); int appended = appendClipboardContentUTF8(cpb, text); return truncated || appended; } static void deallocateClipboardHistoryEntry (void *item, void *data) { HistoryEntry *entry = item; if (entry->characters) free(entry->characters); free(entry); } ClipboardObject * newClipboard (void) { ClipboardObject *cpb; if ((cpb = malloc(sizeof(*cpb)))) { memset(cpb, 0, sizeof(*cpb)); cpb->buffer.characters = NULL; cpb->buffer.size = 0; cpb->buffer.length = 0; if ((cpb->history.queue = newQueue(deallocateClipboardHistoryEntry, NULL))) { return cpb; } } else { logMallocError(); } return NULL; } void destroyClipboard (ClipboardObject *cpb) { if (cpb->buffer.characters) free(cpb->buffer.characters); deallocateQueue(cpb->history.queue); free(cpb); } static LockDescriptor * getMainClipboardLock (void) { static LockDescriptor *lock = NULL; return getLockDescriptor(&lock, "main-clipboard"); } void lockMainClipboard (void) { obtainExclusiveLock(getMainClipboardLock()); } void unlockMainClipboard (void) { releaseLock(getMainClipboardLock()); } static void exitMainClipboard (void *data) { ClipboardObject **clipboard = data; lockMainClipboard(); destroyClipboard(*clipboard); *clipboard = NULL; unlockMainClipboard(); } ClipboardObject * getMainClipboard (void) { static ClipboardObject *clipboard = NULL; lockMainClipboard(); if (!clipboard) { if ((clipboard = newClipboard())) { onProgramExit("main-clipboard", exitMainClipboard, &clipboard); } } unlockMainClipboard(); return clipboard; } void onMainClipboardUpdated (void) { api.updateParameter(BRLAPI_PARAM_CLIPBOARD_CONTENT, 0); } int setMainClipboardContent (const char *content) { ClipboardObject *cpb = getMainClipboard(); int updated; lockMainClipboard(); updated = setClipboardContentUTF8(cpb, content); unlockMainClipboard(); if (updated) onMainClipboardUpdated(); return updated; } char * getMainClipboardContent (void) { ClipboardObject *cpb = getMainClipboard(); char *content; lockMainClipboard(); content = getClipboardContentUTF8(cpb); unlockMainClipboard(); return content; }