Нашёл пример в код-архиве реагирующий на изменение буфера обмена, решил попробовать соединить с SQLite в плане сохранения в базу данных с большой историей до 1000 и более или по дате за неделю и более, чтобы всегда можно было найти то, что когда-то копировал.
Проба (Linux)
1. База данных имеет проблемы с экранированием вставляемого текста, так как скобки и кавычки являются элементами кода.
2. Сохранение окна, из которого произошло копирование тоже пока не получилось.
; Файл: Clipboard_OwnerChange.pb ; Функционал: калбак при смене владельца буфера обмена - Linux ; Автор: Omi (оригинального примера) ; Дата: Май. 28, 2016 ; Компилятор: PureBasic 5.22/5.31/5.4x ; ОС: Linux: (X/K/L)ubuntu, Mint, 32/64, Ascii/Uni ;-------------------------------------------------------------- EnableExplicit ImportC "" gdk_screen_get_window_stack(*screen.GdkScreen) gdk_x11_window_get_xid(*window) gtk_window_get_title(*window); gdk_atom_intern(atom_name.p-utf8, only_if_exists) gtk_widget_get_window(*widget.GtkWidget) g_signal_connect(*instance, detailed_signal.p-utf8, *c_handler, *pdata, destroy= 0, flags= 0) As "g_signal_connect_data" EndImport Enumeration GdkEventType #GDK_OWNER_CHANGE = 34; 2.6+ #GDK_GRAB_BROKEN = 35; 2.8+ #GDK_DAMAGE = 36; 2.14+ #GDK_TOUCH_BEGIN = 37; 3.4+ #GDK_TOUCH_UPDATE = 38; 3.4+ #GDK_TOUCH_END = 39; 3.4+ #GDK_TOUCH_CANCEL = 40; 3.4+ #GDK_TOUCHPAD_SWIPE= 41; 3.18+ #GDK_TOUCHPAD_PINCH= 42; 3.18+ #GDK_EVENT_LAST = 43; 2.18+ EndEnumeration Enumeration GdkOwnerChange #GDK_OWNER_CHANGE_NEW_OWNER #GDK_OWNER_CHANGE_DESTROY #GDK_OWNER_CHANGE_CLOSE EndEnumeration Structure GdkEventOwnerChange type.i;l *window.GdkWindow send_event.b CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 PB_Alignment1.b[3] CompilerElseIf #PB_Compiler_Processor = #PB_Processor_x64 PB_Alignment1.b[7] CompilerEndIf *owner.GdkWindow reason.i selection.i time.l selection_time.l EndStructure ; Object constants #Win_Main = 0 #btn = 0 #sql = 0 ; #ClipboardTimer= 999 Global.i gEvent ; Global.i gTime= 1000 Global gClipboard= gtk_clipboard_get_(gdk_atom_intern("CLIPBOARD", #True)) ; === мой контент === UseSQLiteDatabase() Declare ForceDirectories(Dir.s) Declare Screen_GetWindowStack(id) Declare.s GetWinTitle(id) Global sqlite$, sqliteTrue = 0 sqlite$ = GetPathPart(ProgramFilename()) + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".sqlite" If FileSize(sqlite$) < 0 ; Если рядом с прогой файла нет, то прога не портабельная и ищем файл-базы в папках конфигов ; Создаём в AppData\Roaming, если в текущей не удалось sqlite$ = GetHomeDirectory() + ".config/ClipboardSQLite/ClipboardSQLite.sqlite" If FileSize(sqlite$) > -1 sqliteTrue = 1 ElseIf ForceDirectories(GetPathPart(sqlite$)) And CreateFile(0, sqlite$) CloseFile(0) sqliteTrue = 1 EndIf EndIf ; MessageRequester("", Str(sqliteTrue) + Str(OpenDatabase(#sql, sqlite$, "", ""))) ; End Define tmp$ Global sqlTbDate$ = "data" Global sqlTbWinID$ = "WinID" Global sqlTbText$ = "Text" ; база: Дата добавления | текст данных | окно? с которого взято If sqliteTrue And OpenDatabase(#sql, sqlite$, "", "") ; если не добавили таблицу то завершаем программу If Not DatabaseUpdate(#sql, "CREATE TABLE IF NOT EXISTS '" + "txt" + "' (" + sqlTbDate$ + " int, '" + sqlTbWinID$ + "' Text, '" + sqlTbText$ + "' Text);") MessageRequester("", "Не удалось добавить таблицу в базу данных") End EndIf Else MessageRequester("", "Не удалось создать базу данных") End EndIf ; IsDatabase(#Database) ProcedureC Callback_Clipboard_OwnerChange(*clipboard, *event.GdkEventAny, user_data) Protected *eventownerchange.GdkEventOwnerChange Static t = 0 ; Protected *window, gpointer If *event\type = #GDK_OWNER_CHANGE ; If t ; t - 1 ; ProcedureReturn ; EndIf ; t+1 *eventownerchange= *event ; gdk_window_get_user_data_(*eventownerchange\window, @gpointer) ; If gpointer ; Debug PeekS(gtk_window_get_title_(gpointer), -1, #PB_UTF8) ; EndIf ; Debug PeekS(gtk_window_get_title(*eventownerchange\window), -1, #PB_UTF8) ; Debug gtk_window_get_title(*eventownerchange\window) If Not DatabaseUpdate(#sql, "BEGIN TRANSACTION;INSERT INTO '" + "txt" + "' (" + sqlTbDate$ + "," + sqlTbWinID$ + "," + sqlTbText$ + ") values ('" + Str(Date()) + "','" + GetWinTitle(*eventownerchange\window) + "','" + GetClipboardText() + "');COMMIT;") ; MessageRequester("", "Не удалось добавить элемент") RunProgram("zenity", "--info --title=Ошибка --text=Не_удалось_добавить_элемент", "") EndIf EndIf EndProcedure Procedure Create_WinMain() If OpenWindow(#Win_Main, 300, 200, 500, 200, "Слежка за буфером обмена", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) TextGadget (#PB_Any, 5, 5, 490, 26, "Перенести части в буфер обмена и с другими программами") ButtonGadget(#btn, 5, 40, 300, 26, "Показать данные") g_signal_connect(gClipboard, "owner-change", @Callback_Clipboard_OwnerChange(), 0) EndIf EndProcedure Create_WinMain() Repeat gEvent= WaitWindowEvent() Select gEvent Case #PB_Event_Gadget Select EventGadget() Case #btn ; SetClipboardText("Тестовый текст PureBasic!") If DatabaseQuery(#sql, "SELECT * FROM txt") ; Получите все записи в таблице 'txt' tmp$ = "" While NextDatabaseRow(#sql) ; Цикл для каждой записи tmp$ + FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", Val(GetDatabaseString(#sql, 0))) + #TAB$ + GetDatabaseString(#sql, 1) + #TAB$ + GetDatabaseString(#sql, 2) + #CRLF$ ; Отображение содержимого первого поля Wend FinishDatabaseQuery(#sql) EndIf MessageRequester("", tmp$) EndSelect Case #PB_Event_CloseWindow CloseDatabase(#sql) End EndSelect ForEver ;================================================================== ; ; Author: ts-soft ; Date: March 5th, 2010 ; Explain: ; modified version from IBSoftware (CodeArchiv) ; on vista and above check the Request for "User mode" or "Administrator mode" in compileroptions ; (no virtualisation!) ;================================================================== Procedure ForceDirectories(Dir.s) Static tmpDir.s, Init, delim$ Protected result CompilerSelect #PB_Compiler_OS CompilerCase #PB_OS_Windows delim$ = "\" CompilerCase #PB_OS_Linux delim$ = "/" CompilerEndSelect If Len(Dir) = 0 ProcedureReturn #False Else If Not Init tmpDir = Dir Init = #True EndIf If (Right(Dir, 1) = delim$) Dir = Left(Dir, Len(Dir) - 1) EndIf If (Len(Dir) < 3) Or FileSize(Dir) = -2 Or GetPathPart(Dir) = Dir If FileSize(tmpDir) = -2 result = #True EndIf tmpDir = "" Init = #False ProcedureReturn result EndIf ForceDirectories(GetPathPart(Dir)) ProcedureReturn CreateDirectory(Dir) EndIf EndProcedure Procedure.s GetWinTitle(id) Protected ProgOutput.s, Pos, Pos2, ProgrammId id = Screen_GetWindowStack(id) ProgrammId = RunProgram("wmctrl", "-l", "", #PB_Program_Open | #PB_Program_Read) If ProgrammId While ProgramRunning(ProgrammId) If AvailableProgramOutput(ProgrammId) ProgOutput + ReadProgramString(ProgrammId) + #LF$ EndIf Wend CloseProgram(ProgrammId) ProgOutput = ReplaceString(ProgOutput, " ", #TAB$) ; Pos = FindString(ProgOutput, "0x" + Hex(id)) Pos = FindString(ProgOutput, Hex(id)) If Pos Pos2 = FindString(ProgOutput, #LF$, Pos) ; Debug "Найдено 1 " + Hex(id) If Pos2 ; Debug "Найдено 2" ProgOutput = StringField(Mid(ProgOutput, Pos, Pos2 - Pos), 2, #TAB$) Pos = FindString(ProgOutput, " ", 3) If Pos ProgOutput = Mid(ProgOutput, Pos + 1) EndIf EndIf Else ProgOutput = Str(id) ; Debug "Не найдено" EndIf ; Debug "0x" + Hex(id) ; Debug "ProgOutput" +ProgOutput If ProgOutput = "" ProgOutput = "none" EndIf ProcedureReturn ProgOutput Else Debug "wmctrl must be installed !!!" Debug "install: sudo apt-get install wmctrl" EndIf EndProcedure Procedure Screen_GetWindowStack(id) Protected *gList0.GList = gdk_screen_get_window_stack(gdk_display_get_default_screen_(gdk_display_get_default_())) Protected *gList.GList = *gList0 Protected *window, *decorations Protected.i xid If *gList Repeat *window= *glist\data ; Debug Str(*window - 800) + " = " + Str(id) If *window - 800 = id xid= gdk_x11_window_get_xid(*window) Break EndIf *gList= *gList\next g_object_unref_(*window) Until *gList = 0 EndIf g_list_free_(*gList0); free list ProcedureReturn xid EndProcedure