Introduction
Reading and writing to the clipboard using low-level APIs can be tricky. There are a bunch of steps required. This tutorial simplifies the process so you can easily read and write to the clipboard using the low-level APIs.
The tutorial is based on RGFW's source code and its usage of the low-level APIs.
Note: the cocoa code is written in Pure-C.
Overview
1) Clipboard Paste
- X11 (init atoms, convert section, get data)
- Win32 (open clipboard, get data, convert data, close clipboard)
- Cocoa (set datatypes, get pasteboard, get data, convert data)
2) Clipboard Copy
- X11 (init atoms, convert section, handle request, send data)
- Win32 (setup global object, convert data, open clipboard, convert string, send data, close clipboard)
- Cocoa (create datatype array, declare types, convert string, send data)
Clipboard Paste
X11
To handle the clipboard, you must create some Atoms via XInternAtom.
X Atoms are used to ask for or send specific data or properties through X11.
You'll need three atoms,
1) UTF8_STRING: Atom for a UTF-8 string.
2) CLIPBOARD: Atom for getting clipboard data.
3) XSEL_DATA: Atom to get selection data.
const Atom UTF8_STRING = XInternAtom(display, "UTF8_STRING", True); const Atom CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0); const Atom XSEL_DATA = XInternAtom(display, "XSEL_DATA", 0);
Now, to get the clipboard data you have to request that the clipboard section be converted to UTF8 using XConvertSelection.
use XSync to send the request to the server.
XConvertSelection(display, CLIPBOARD, UTF8_STRING, XSEL_DATA, window, CurrentTime); XSync(display, 0);
The selection will be converted and sent back to the client as a XSelectionNotify event. You can get the next event, which should be the SelectionNotify event with XNextEvent.
XEvent event; XNextEvent(display, &event);
Check if the event is a SelectionNotify event and use .selection to ensure the type is a CLIPBOARD. Also make sure .property is not 0 and can be retrieved.
if (event.type == SelectionNotify && event.xselection.selection == CLIPBOARD && event.xselection.property != 0) {
You can get the converted data via XGetWindowProperty using the selection property.
int format; unsigned long N, size; char* data, * s = NULL; Atom target; XGetWindowProperty(event.xselection.display, event.xselection.requestor, event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, &format, &size, &N, (unsigned char**) &data);
Make sure the data is in the right format by checking target
if (target == UTF8_STRING || target == XA_STRING) {
The data is stored in data, once you're done with it free it with XFree.
You can also delete the property via XDeleteProperty.
XFree(data); } XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); }
winapi
First, open the clipboard OpenClipboard.
if (OpenClipboard(NULL) == 0) return 0;
Get the clipboard data as a utf16 string via GetClipboardData
If the data is NULL, you should close the clipboard using CloseClipboard
HANDLE hData = GetClipboardData(CF_UNICODETEXT); if (hData == NULL) { CloseClipboard(); return 0; }
Next, you need to convert the utf16 data back to utf8.
Start by locking memory for the utf8 data via GlobalLock.
wchar_t* wstr = (wchar_t*) GlobalLock(hData);
Use setlocale to ensure the data format is utf8.
Get the size of the UTF-8 version with wcstombs.
setlocale(LC_ALL, "en_US.UTF-8"); size_t textLen = wcstombs(NULL, wstr, 0);
If the size is valid, convert the data using wcstombs.
if (textLen) { char* text = (char*) malloc((textLen * sizeof(char)) + 1); wcstombs(text, wstr, (textLen) + 1); text[textLen] = '\0'; free(text); }
Make sure to free leftover global data using GlobalUnlock and close the clipboard with CloseClipboard.
GlobalUnlock(hData); CloseClipboard();
cocoa
Cocoa uses NSPasteboardTypeString to ask for string data. You'll have to define this yourself if you're not using Objective-C.
NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text";
Although the is a c-string and Cocoa uses NSStrings, you can convert the c-string to an NSString via stringWithUTF8String.
NSString* dataType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), (char*)NSPasteboardTypeString);
Now we'll use generalPasteboard to get the default pasteboard object.
NSPasteboard* pasteboard = objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
Then you can get the pasteboard's string data with the dataType using stringForType.
However, it will give you an NSString, which can be converted with UTF8String.
NSString* clip = ((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, sel_registerName("stringForType:"), dataType); const char* str = ((const char* (*)(id, SEL)) objc_msgSend) (clip, sel_registerName("UTF8String"));
Clipboard Copy
X11
To copy to the clipboard you'll need a few more Atoms.
1) SAVE_TARGETS: To request a section to convert to (for copying).
2) TARGETS: To handle one requested target
3) MULTIPLE: When there are multiple request targets
4) ATOM_PAIR: To get the supported data types.
5) CLIPBOARD_MANAGER: To access data from the clipboard manager.
const Atom SAVE_TARGETS = XInternAtom((Display*) display, "SAVE_TARGETS", False); const Atom TARGETS = XInternAtom((Display*) display, "TARGETS", False); const Atom MULTIPLE = XInternAtom((Display*) display, "MULTIPLE", False); const Atom ATOM_PAIR = XInternAtom((Display*) display, "ATOM_PAIR", False); const Atom CLIPBOARD_MANAGER = XInternAtom((Display*) display, "CLIPBOARD_MANAGER", False);
We can request a clipboard section. First, set the owner of the section to be a client window via XSetSelectionOwner. Next request a converted section using XConvertSelection.
XSetSelectionOwner((Display*) display, CLIPBOARD, (Window) window, CurrentTime); XConvertSelection((Display*) display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) window, CurrentTime);
The rest of the code would exist in an event loop. You can create an external event loop from your main event loop if you wish or add this to your main event loop.
We'll be handling SelectionRequest in order to update the clipboard selection to the string data.
if (event.type == SelectionRequest) { const XSelectionRequestEvent* request = &event.xselectionrequest;
At the end of the SelectionNotify event, a response will be sent back to the requester. The structure should be created here and modified depending on the request data.
XEvent reply = { SelectionNotify }; reply.xselection.property = 0;
The first target we will handle is TARGETS when the requestor wants to know which targets are supported.
if (request->target == TARGETS) {
I will create an array of supported targets
const Atom targets[] = { TARGETS, MULTIPLE, UTF8_STRING, XA_STRING };
This array can be passed using XChangeProperty.
I'll also change the selection property so the requestor knows what property we changed.
XChangeProperty(display, request->requestor, request->property, 4, 32, PropModeReplace, (unsigned char*) targets, sizeof(targets) / sizeof(targets[0])); reply.xselection.property = request->property; }
Next, I will handle MULTIPLE targets.
if (request->target == MULTIPLE) {
We'll start by getting the supported targets via XGetWindowProperty
Atom* targets = NULL; Atom actualType = 0; int actualFormat = 0; unsigned long count = 0, bytesAfter = 0; XGetWindowProperty(display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (unsigned char **) &targets);
Now we'll loop through the supported targets. If the supported targets match one of our supported targets, we can pass the data with XChangeProperty.
If the target is not used, the second argument should be set to None, marking it as unused.
unsigned long i; for (i = 0; i requestor, targets[i + 1], targets[i], 8, PropModeReplace, (unsigned char*) text, sizeof(text)); XFlush(display); } else { targets[i + 1] = None; } }
You can pass the final array of supported targets to the requestor using XChangeProperty. This tells the requestor which targets to expect for the original list it sent.
The message will be sent out asap when XFlush is called.
You can free your copy of the target array with XFree.
XChangeProperty((Display*) display, request->requestor, request->property, ATOM_PAIR, 32, PropModeReplace, (unsigned char*) targets, count); XFlush(display); XFree(targets); reply.xselection.property = request->property; }
For the final step of the event, send the selection back to the requestor via XSendEvent.
Then flush the queue with XFlush.
reply.xselection.display = request->display; reply.xselection.requestor = request->requestor; reply.xselection.selection = request->selection; reply.xselection.target = request->target; reply.xselection.time = request->time; XSendEvent((Display*) display, request->requestor, False, 0, &reply); XFlush(display); }
winapi
First allocate global memory for your data and your utf-8 buffer with GlobalAlloc
HANDLE object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR)); WCHAR* buffer = (WCHAR*) GlobalLock(object);
Next, you can use MultiByteToWideChar to convert your string to a wide string.
MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, textLen);
Now unlock the global object and open the clipboard
GlobalUnlock(object); OpenClipboard(NULL);
To update the clipboard data, you start by clearing what's currently on the clipboard via EmptyClipboard you can use SetClipboardData to set the data to the utf8 object.
Finally, close the clipboard with CloseClipboard.
EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, object); CloseClipboard();
cocoa
Start by creating an array of the type of data you want to put on the clipboard and convert it to an NSArray using initWithObjects.
NSPasteboardType ntypes[] = { dataType }; NSArray* array = ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) (NSAlloc(objc_getClass("NSArray")), sel_registerName("initWithObjects:count:"), ntypes, 1);
Use declareTypes to declare the array as the supported data types.
You can also free the NSArray with NSRelease.
((NSInteger(*)(id, SEL, id, void*))objc_msgSend) (pasteboard, sel_registerName("declareTypes:owner:"), array, NULL); NSRelease(array);
You can convert the string to want to copy to an NSString via stringWithUTF8String and set the clipboard string to be that NSString using setString.
NSString* nsstr = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text); ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend) (pasteboard, sel_registerName("setString:forType:"), nsstr, dataType);
Full examples
X11
// compile with: // gcc x11.c -lX11 #include#include #include #include #include #include int main(void) { Display* display = XOpenDisplay(NULL); Window window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 10, 10, 200, 200, 1, BlackPixel(display, DefaultScreen(display)), WhitePixel(display, DefaultScreen(display))); XSelectInput(display, window, ExposureMask | KeyPressMask); const Atom UTF8_STRING = XInternAtom(display, "UTF8_STRING", True); const Atom CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0); const Atom XSEL_DATA = XInternAtom(display, "XSEL_DATA", 0); const Atom SAVE_TARGETS = XInternAtom((Display*) display, "SAVE_TARGETS", False); const Atom TARGETS = XInternAtom((Display*) display, "TARGETS", False); const Atom MULTIPLE = XInternAtom((Display*) display, "MULTIPLE", False); const Atom ATOM_PAIR = XInternAtom((Display*) display, "ATOM_PAIR", False); const Atom CLIPBOARD_MANAGER = XInternAtom((Display*) display, "CLIPBOARD_MANAGER", False); // input XConvertSelection(display, CLIPBOARD, UTF8_STRING, XSEL_DATA, window, CurrentTime); XSync(display, 0); XEvent event; XNextEvent(display, &event); if (event.type == SelectionNotify && event.xselection.selection == CLIPBOARD && event.xselection.property != 0) { int format; unsigned long N, size; char* data, * s = NULL; Atom target; XGetWindowProperty(event.xselection.display, event.xselection.requestor, event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, &format, &size, &N, (unsigned char**) &data); if (target == UTF8_STRING || target == XA_STRING) { printf("paste: %s\n", data); XFree(data); } XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); } // output char text[] = "new string\0"; XSetSelectionOwner((Display*) display, CLIPBOARD, (Window) window, CurrentTime); XConvertSelection((Display*) display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) window, CurrentTime); Bool running = True; while (running) { XNextEvent(display, &event); if (event.type == SelectionRequest) { const XSelectionRequestEvent* request = &event.xselectionrequest; XEvent reply = { SelectionNotify }; reply.xselection.property = 0; if (request->target == TARGETS) { const Atom targets[] = { TARGETS, MULTIPLE, UTF8_STRING, XA_STRING }; XChangeProperty(display, request->requestor, request->property, 4, 32, PropModeReplace, (unsigned char*) targets, sizeof(targets) / sizeof(targets[0])); reply.xselection.property = request->property; } if (request->target == MULTIPLE) { Atom* targets = NULL; Atom actualType = 0; int actualFormat = 0; unsigned long count = 0, bytesAfter = 0; XGetWindowProperty(display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (unsigned char **) &targets); unsigned long i; for (i = 0; i requestor, targets[i + 1], targets[i], 8, PropModeReplace, (unsigned char*) text, sizeof(text)); XFlush(display); running = False; } else { targets[i + 1] = None; } } XChangeProperty((Display*) display, request->requestor, request->property, ATOM_PAIR, 32, PropModeReplace, (unsigned char*) targets, count); XFlush(display); XFree(targets); reply.xselection.property = request->property; } reply.xselection.display = request->display; reply.xselection.requestor = request->requestor; reply.xselection.selection = request->selection; reply.xselection.target = request->target; reply.xselection.time = request->time; XSendEvent((Display*) display, request->requestor, False, 0, &reply); XFlush(display); } } XCloseDisplay(display); }
Winapi
// compile with: // gcc win32.c #include <windows.h> #include <locale.h> #include <stdio.h> int main() { // output if (OpenClipboard(NULL) == 0) return 0; HANDLE hData = GetClipboardData(CF_UNICODETEXT); if (hData == NULL) { CloseClipboard(); return 0; } wchar_t* wstr = (wchar_t*) GlobalLock(hData); setlocale(LC_ALL, "en_US.UTF-8"); size_t textLen = wcstombs(NULL, wstr, 0); if (textLen) { char* text = (char*) malloc((textLen * sizeof(char)) + 1); wcstombs(text, wstr, (textLen) + 1); text[textLen] = '\0'; printf("paste: %s\n", text); free(text); } GlobalUnlock(hData); CloseClipboard(); // input char text[] = "new text\0"; HANDLE object = GlobalAlloc(GMEM_MOVEABLE, (sizeof(text) / sizeof(char)) * sizeof(WCHAR)); WCHAR* buffer = (WCHAR*) GlobalLock(object); if (!buffer) { GlobalFree(object); return 0; } MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, (sizeof(text) / sizeof(char))); GlobalUnlock(object); if (OpenClipboard(NULL) == 0) { GlobalFree(object); return 0; } EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, object); CloseClipboard(); } </stdio.h></locale.h></windows.h>
Cocoa
// compile with: // gcc cocoa.c -framework Foundation -framework AppKit #include#include #include #include #ifdef __arm64__ /* ARM just uses objc_msgSend */ #define abi_objc_msgSend_stret objc_msgSend #define abi_objc_msgSend_fpret objc_msgSend #else /* __i386__ */ /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ #define abi_objc_msgSend_stret objc_msgSend_stret #define abi_objc_msgSend_fpret objc_msgSend_fpret #endif typedef void NSPasteboard; typedef void NSString; typedef void NSArray; typedef void NSApplication; typedef const char* NSPasteboardType; typedef unsigned long NSUInteger; typedef long NSInteger; #define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) #define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) #define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) #define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) #define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) #define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) #define objc_msgSend_void_int ((void (*)(id, SEL, int))objc_msgSend) #define objc_msgSend_bool_void ((BOOL (*)(id, SEL))objc_msgSend) #define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) #define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) #define objc_msgSend_id_id ((id (*)(id, SEL, id))objc_msgSend) #define objc_msgSend_id_bool ((BOOL (*)(id, SEL, id))objc_msgSend) #define objc_msgSend_class_char ((id (*)(Class, SEL, char*))objc_msgSend) void NSRelease(id obj) { objc_msgSend_void(obj, sel_registerName("release")); } int main() { /* input */ NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; NSString* dataType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), (char*)NSPasteboardTypeString); NSPasteboard* pasteboard = objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); NSString* clip = ((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, sel_registerName("stringForType:"), dataType); const char* str = ((const char* (*)(id, SEL)) objc_msgSend) (clip, sel_registerName("UTF8String")); printf("paste: %s\n", str); char text[] = "new string\0"; NSPasteboardType ntypes[] = { dataType }; NSArray* array = ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) (NSAlloc(objc_getClass("NSArray")), sel_registerName("initWithObjects:count:"), ntypes, 1); ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) (pasteboard, sel_registerName("declareTypes:owner:"), array, NULL); NSRelease(array); NSString* nsstr = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), text); ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend) (pasteboard, sel_registerName("setString:forType:"), nsstr, dataType); }
以上是RGFW 底層:剪貼簿複製/貼上的詳細內容。更多資訊請關注PHP中文網其他相關文章!

C#使用自動垃圾回收機制,而C 採用手動內存管理。 1.C#的垃圾回收器自動管理內存,減少內存洩漏風險,但可能導致性能下降。 2.C 提供靈活的內存控制,適合需要精細管理的應用,但需謹慎處理以避免內存洩漏。

C 在現代編程中仍然具有重要相關性。 1)高性能和硬件直接操作能力使其在遊戲開發、嵌入式系統和高性能計算等領域佔據首選地位。 2)豐富的編程範式和現代特性如智能指針和模板編程增強了其靈活性和效率,儘管學習曲線陡峭,但其強大功能使其在今天的編程生態中依然重要。

C 學習者和開發者可以從StackOverflow、Reddit的r/cpp社區、Coursera和edX的課程、GitHub上的開源項目、專業諮詢服務以及CppCon等會議中獲得資源和支持。 1.StackOverflow提供技術問題的解答;2.Reddit的r/cpp社區分享最新資訊;3.Coursera和edX提供正式的C 課程;4.GitHub上的開源項目如LLVM和Boost提陞技能;5.專業諮詢服務如JetBrains和Perforce提供技術支持;6.CppCon等會議有助於職業

C#適合需要高開發效率和跨平台支持的項目,而C 適用於需要高性能和底層控制的應用。 1)C#簡化開發,提供垃圾回收和豐富類庫,適合企業級應用。 2)C 允許直接內存操作,適用於遊戲開發和高性能計算。

C 持續使用的理由包括其高性能、廣泛應用和不斷演進的特性。 1)高效性能:通過直接操作內存和硬件,C 在系統編程和高性能計算中表現出色。 2)廣泛應用:在遊戲開發、嵌入式系統等領域大放異彩。 3)不斷演進:自1983年發布以來,C 持續增加新特性,保持其競爭力。

C 和XML的未來發展趨勢分別為:1)C 將通過C 20和C 23標準引入模塊、概念和協程等新特性,提升編程效率和安全性;2)XML將繼續在數據交換和配置文件中佔據重要地位,但會面臨JSON和YAML的挑戰,並朝著更簡潔和易解析的方向發展,如XMLSchema1.1和XPath3.1的改進。

現代C 設計模式利用C 11及以後的新特性實現,幫助構建更靈活、高效的軟件。 1)使用lambda表達式和std::function簡化觀察者模式。 2)通過移動語義和完美轉發優化性能。 3)智能指針確保類型安全和資源管理。

C 多線程和並發編程的核心概念包括線程的創建與管理、同步與互斥、條件變量、線程池、異步編程、常見錯誤與調試技巧以及性能優化與最佳實踐。 1)創建線程使用std::thread類,示例展示瞭如何創建並等待線程完成。 2)同步與互斥使用std::mutex和std::lock_guard保護共享資源,避免數據競爭。 3)條件變量通過std::condition_variable實現線程間的通信和同步。 4)線程池示例展示瞭如何使用ThreadPool類並行處理任務,提高效率。 5)異步編程使用std::as


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

記事本++7.3.1
好用且免費的程式碼編輯器

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

WebStorm Mac版
好用的JavaScript開發工具