Нашёл пример в код-архиве реагирующий на изменение буфера обмена, решил попробовать соединить с 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