Опенсорсная утилита CopyQ показывает содержимое буфера обмена на всех ОС и сохраняет историю

Буфер обмена — один из основных элементов GUI, но он сложен в реализации. Подводных камней настолько много, что вы больше никогда не сможете произнести слово «копипаст» с презрительным выражением. Есть тысячи приложений и форматов данных. Невозможно обеспечить полную конвертацию всего во всё.

В некоторых случаях данные вообще нигде не сохраняются по нажатию Ctrl+C. И по нажатию Ctrl+V будет возвращён NULL. Shit happens, как говорится…

В одних программах возможен копипаст только текста, в других можно выделить графику, где-то поддерживается копирование/вставка файлов и т. д. Нюансов очень много. Попробуем выделить основные моменты для Windows и Linux.

Если идти от простого к сложному и в примерно хронологическом порядке, то лучше начать с X11.

▍ X11


Графический стек Linux состоит из множества разных элементов, которые объединены в монолитную архитектуру и каким-то чудом работают вместе без постоянных глюков.

Так или иначе, клипборд в X11 работает не так, как в других ОС. Здесь создаётся впечатление двух независимых буферов обмена. Один по горячим клавишам типа Ctrl+C/V (или Ctrl/Shift+Ins), а второй — по нажатию мыши. Если скопировать данные в буфер горячей клавишей с клавиатуры, то и вставить можно только с клавиатуры. А если скопировал мышью — то вставить средним колёсиком. Но иногда работают оба способа, то есть можно скопировать текст в буфер обмена мышью, а вставить — сочетанием клавиш.

Документация подтверждает, что в X11 действительно несколько независимых буферов обмена (здесь они называются selections). Их даже не два, а может быть сколько угодно много. По схеме эти selections представляют способ коммуникации между клиентами. Сами клиенты должны договориться между собой, какой буфер они будут использовать. Например, если вы пишете какой-то клиент под Linux, то как владелец буфера (SelectionOwner) можете ввести свой буфер обмена и надеяться, что остальные его поймут.

То есть работа с буфером обмена в общем виде выглядит так:

  1. Клиент 1 заявляет X-серверу о владении определённым буфером как SelectionOwner (буфер 1).
  2. В какой-то момент времени клиент 2 может попросить у сервера содержимое буфера 1 в определённом формате.
  3. X Server получает запрос и перенаправляет его соответствующему владельцу.
  4. Владелец буфера (клиент) напрямую отправляет запрошенные данные в указанном формате клиенту 2, когда (и если) сможет это сделать.
  5. Клиент 2 возвращает клиенту 1 подтверждение получения.

Всё кажется просто и логично, при минимальном взаимодействии с системным ядром. Но в реальности тут возможны нестыковки и заминки. Самое главное, что нет абсолютно никакой гарантии, что клиент 1 когда-нибудь отправит запрошенные данные, а уж тем более в указанном формате.

Как же получается, что для пользователя вся эта система выглядит словно два изолированных буфера, один для горячих клавиш, а второй для мыши? Ну, здесь дело в идентификации буферов. Внутренне это произвольные номера, а внешне (для клиентов) три варианта:

  • PRIMARY: буфер «средней кнопки мыши» (условно говоря);
  • SECONDARY: практически не используется в наше время;
  • CLIPBOARD: буфер Ctrl+C (условно говоря).

Часто работающие программы в X11 создают невидимые окна исключительно с целью владения (захвата) того или иного системного буфера. Теоретически, это можно сделать и с помощью видимого окна, но с помощью невидимого проще.

В реальности содержимое буфера передаётся через окна, а именно через запись в свойства окна (туда помещается ограниченный объём данных). В этом смысле работа буферов аналогична работе окон — в X11 нет требований, чтобы эти окна работали на одном хосте или по одному протоколу. Например, теоретически копипаст должен работать в разных программах, даже если клиенты находятся на разных компьютерах и подключены к одному X-серверу через интернет.

Вот так всё работает в X11: это просто брокер между клиентами, который принимает минимальное участие в передаче данных из буфера обмена. В X11 даже нет встроенного менеджера для таких данных.

▍ Wayland


Как известно, на замену X11 разработан протокол Wayland, где всё организовано несколько иначе.


Работа буфера обмена в спецификациях Wayland описана с применением той же специфической терминологии (selection — буфер обмена, clipboard — один из буферов обмена, уникальный идентификатор atom и т. д.).

Но если разобраться, всё организовано довольно просто и логично, как в X11, даже с некоторыми усовершенствованиями.

Здесь примерно так же организовано владение буфером определённого типа. Когда вы нажимаете Ctrl+C, программа просто захватывает буфер обмена определённого типа и заявляет о наличии данных в определённом формате (например, image/png, text/x-moz-url1 или text/plain). На этом этапе никакие данные никуда не передаются и нигде не сохраняются. Так что в отсутствие специального менеджера для буфера обмена данные будут потеряны при закрытии окна или при их удалении в окне.

Захват буфера разрешён только основному окну. Когда новое окно захватывает буфер, оно получает сообщение от предыдущего владельца буфера о наличии данных и их формате. Затем наступает этап вставки данных «из буфера» (в кавычках, потому что мы уже поняли, что буфер — абстрактное понятие), то есть помеченных для копирования данных. Так вот, второй клиент делает запрос (создаёт дескриптор файла) с указанием формата, в котором хочет получить данные. Этот формат может не полностью совпадать с указанными выше, и тогда он получит лишь часть данных или вовсе ничего.

Несмотря на долгую эволюцию, API для буфера обмена в Wayland далеко не совершенен и этой подсистеме по-прежнему нужен внешний менеджер (библиотека) для нормальной работы.

▍ Как это работает в Windows


В Windows применяется та же концепция «владельца буфера». Владельцем становится любое приложение, которое отправило информацию в буфер стандартным способом. Поддерживаются следующие системные вызовы:

  • Вызов Open­Clipboard(hwnd) отправляет информацию об окне, которое должно стать новым владельцем буфера.
  • Вызов Empty­Clipboard() стирает из буфера предыдущее содержимое.
  • Вызов Set­Clipboard­Data() для каждого фрагмента данных, которые вы хотите поместить в буфер (по историческим причинам этот вызов могут использовать даже программы, не владеющие буфером).
  • Вызов Close­Clipboard() сообщает об окончании работы с данными.
  • Поздравляем, вы новый владелец буфера.

При попытке стороннего окна получить доступ к буферу его владелец получает несколько сообщений. Среди них могут быть WM_RENDER­FORMAT (отложенный рендеринг данных для буфера до того момента, когда они будут непосредственно запрошены — о нём ниже), WM_RENDER­ALL­FORMATS (часть последовательности уничтожения окна, если он ещё является владельцем буфера обмена на момент его уничтожения) или WM_DESTROYCLIPBOARD (опустошение содержимого буфера).

Предполагаемая схема использования для чтения данных из буфера обмена следующая:

  • Вызов Open­Clipboard(hwnd) отправляет информацию об окне, которое читает из буфера.
  • Вызов Get­Clipboard­Data() для получения данных из буфера.
  • Вызов Close­Clipboard() для индикации окончания чтения данных.

По словам сотрудника Microsoft с 25-летним стажем Реймонда Чена, если бы все использовали рекомендуемую схему, то Windows бы работала как положено. К сожалению, сторонние программы постоянно норовят нарушить конвенцию, а API допускает запись в чужой буфер. Из-за этого возникают проблемы.

Примерно так же работает буфер в macOS (собственно, изобретателями самой концепции буфера обмена считаются создатели компьютера Mac/Lisa, под Mac есть отличные менеджеры с поиском по многомесячной истории типа ClipBuddy, Alfred и Raycast).

Кроме того, в Windows есть Linux-подобный способ работы с буфером, когда данные нигде не сохраняются, а просто помечаются как доступные (для экономии памяти). Это упомянутый выше отложенный рендеринг WM_RENDER­FORMAT. Поэтому если скопировать в буфер большой диапазон ячеек Excel, а потом попробовать их вставить в другой программе в текстовом формате, то операция может завершиться неудачно. В Windows действует ограничение 30 секунд на отложенный рендеринг (на июнь 2022 года). Если в течение этого времени данные не удалось переконвертировать в новый формат для вставки, то буфер вернёт NULL.


▍ Вывод


Таким образом, буфер обмена — это высокоуровневая абстракция, которая описывает человеку механизм передачи данных между программами (клиентами, окнами) на понятном для него языке. При этом в Linux вообще никакого буфера нет, ничего туда не копируется, данные только помечаются для передачи. Это очень грамотный подход — расход памяти сводится к нулю.

Недостаток тоже понятен — при закрытии исходного окна информация «из буфера» тоже будет потеряна и уже никуда не вставится (хотя эту проблему решают специальные менеджеры буфера).

В других операционных системах решили пожертвовать производительностью (памятью) ради удобства пользователей, поскольку копипаст — одна из самых востребованных функций у обычных людей, и потеря данных недопустима.

Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх ????️

Комментарии (19)


  1. foxyrus
    00.00.0000 00:00

    На рабочем компьютер впервые столкнулся с тем, что буфер обмена windows становится полностью недоступен. Во всех приложениях просто ничего не вставляется, если вставить из недоступного буфера в .NET приложениях, возникает ошибка - win32exception openClipboard error_access_denied.

    Лечится только перезагрузкой.


    1. kekekeks
      00.00.0000 00:00
      +5

      У вас какое-то приложение OpenClipboard вызвало, а CloseClipboard нет. И всё, буфер обмена становится недоступен вообще для всех.


      1. MonkAlex
        00.00.0000 00:00
        +1

        Звучит как анекдот.

        Закрытие приложения решит проблему (винда почистит всё) или нет?


        1. remzalp
          00.00.0000 00:00
          -1

          Ну приложение при закрытии может и не закрыть клипборд :)
          Так что с гарантией только перезагрузка


        1. kekekeks
          00.00.0000 00:00
          +2

          При закрытии отпускает, да


      1. foxyrus
        00.00.0000 00:00

        У меня подозрение на антивирус или другое приложение связанное с безопасностью.


  1. Biga
    00.00.0000 00:00
    +1

    Про X11 как-то не очень понятно.
    Как сохраняются данные в буфере обмена при закрытии программы? Почему для одних программ, если скопировать текст и закрыть окно, то скопированное потеряется, а для других программ такой проблемы нет?


    Также что-то мутное про сохранение ограниченного объема данных в свойствах окна: на практике сотни мегабайт данных очень бодро копипастятся. Ни разу не сталкивался с ограничением на объём буфера обмена.


    1. kekekeks
      00.00.0000 00:00
      +3

      В свойство окна влезает то ли 32, то ли 64 кб данных. Чтобы передать больше используется совершенно проклятый протокол INCR, где через уведомления об изменениях свойств окон организуют что-то типа пайпа.


  1. mpa4b
    00.00.0000 00:00

    Так и не понял, в вяленом что, буфер по выделению/средней кнопке отменили что ли?


    1. romancelover
      00.00.0000 00:00
      +1

      Проверил (рядом открыто окно Konsole, сессия wayland) - работает по средней кнопке. И в браузере тоже.


    1. bigcrush
      00.00.0000 00:00
      +1

      Кстати, вставка (и копирование в) по средней кнопке мыши не работает в программах на базе SDL2. Мне приходилось компилить свою версию libsdl2 с поддержкой доступа к PRIMARY буферу.


  1. Dart55
    00.00.0000 00:00
    +1

    Я был очень удивлён когда узнал сколько информации браузер кладёт в буфер обмена при простом копировании текста.
    nirsoft InsideClipboard
    nirsoft InsideClipboard


    1. Revertis
      00.00.0000 00:00

      Размер в байтах?


      1. Dart55
        00.00.0000 00:00

        Скорее всего да, но обозначений нет, nirsoft InsideClipboard.


  1. Psychosynthesis
    00.00.0000 00:00

    Сталкивался с тем, что в винде в некоторых случаях почему-то сбрасывается буфер при закрытии окна. Не могу сходу вспомнить кто так работал, кажется какой-то из редакторов.


    1. Alexeyslav
      00.00.0000 00:00
      +2

      Это в случае использования буфера с отложенным рендерингом - в этом случае нет приложения и логично данные недоступны. Некоторые приложения при закрытии спрашивают что делать с содержимым буффера - оставить его доступным или удалить - это тот самый случай, когда выбираешь оставить на самом деле приложение его пересохраняет но уже в привычном для всех формате, при этом специфические возможности вставки могут отсутствовать - например есл и редактор векторный то он может выдать по запросу в векторе или bitmap, приложение клиент при вставке может предпочитать вектор и всё будет красиво, а потом мы закрыли приложение а после себя в буфере оно оставило только bitmap и последующая вставка будет уже не в векторе с соответствующими ньюансами и артефактами.


  1. unreal_undead2
    00.00.0000 00:00

    теоретически копипаст должен работать в разных программах, даже если клиенты находятся на разных компьютерах и подключены к одному X-серверу через интернет

    Написано, как будто это какая экзотика. Нормально штатно работает.


  1. Ogoun
    00.00.0000 00:00

    В Windows никак не могут нормально доделать RDPCLIP, начиная с самых первых версий. Если начать копировать данные через RDP, и что то поместить в буфер обмена локально, копирование вылетает с ошибкой. При этом вообще непонятно что мешает исправить настолько простой баг, почему бв при вставке не запомнить список который копируется, и освободить буфер.


  1. Timofeuz
    00.00.0000 00:00

    У visual студии есть (или была) неприятная фича, если скопировать текст из редактора, а потом удалить или изменить скопированный участок, то вставляется пустое место.