deskflow/src/lib/platform/XWindowsClipboard.h
sithlord48 1085df54ed refactor: Reuse compliance
build files and documentation: MIT
  images / sources: GPL-2.0-only
  breeze icons: LGPL-2.0-only
  Microsoft MSM file, vc redist license
2025-01-27 17:05:47 -05:00

375 lines
9.7 KiB
C++

/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "common/stdlist.h"
#include "common/stdmap.h"
#include "common/stdvector.h"
#include "deskflow/IClipboard.h"
#include "deskflow/clipboard_types.h"
#if X_DISPLAY_MISSING
#error X11 is required to build deskflow
#else
#include <X11/Xlib.h>
#endif
class IXWindowsClipboardConverter;
//! X11 clipboard implementation
class XWindowsClipboard : public IClipboard
{
public:
/*!
Use \c window as the window that owns or interacts with the
clipboard identified by \c id.
*/
XWindowsClipboard(Display *, Window window, ClipboardID id);
XWindowsClipboard(XWindowsClipboard const &) = delete;
XWindowsClipboard(XWindowsClipboard &&) = delete;
virtual ~XWindowsClipboard();
XWindowsClipboard &operator=(XWindowsClipboard const &) = delete;
XWindowsClipboard &operator=(XWindowsClipboard &&) = delete;
//! Notify clipboard was lost
/*!
Tells clipboard it lost ownership at the given time.
*/
void lost(Time);
//! Add clipboard request
/*!
Adds a selection request to the request list. If the given
owner window isn't this clipboard's window then this simply
sends a failure event to the requestor.
*/
void addRequest(Window owner, Window requestor, Atom target, ::Time time, Atom property);
//! Process clipboard request
/*!
Continues processing a selection request. Returns true if the
request was handled, false if the request was unknown.
*/
bool processRequest(Window requestor, ::Time time, Atom property);
//! Cancel clipboard request
/*!
Terminate a selection request. Returns true iff the request
was known and handled.
*/
bool destroyRequest(Window requestor);
//! Get window
/*!
Returns the clipboard's window (passed the c'tor).
*/
Window getWindow() const;
//! Get selection atom
/*!
Returns the selection atom that identifies the clipboard to X11
(e.g. XA_PRIMARY).
*/
Atom getSelection() const;
// IClipboard overrides
virtual bool empty();
virtual void add(EFormat, const std::string &data);
virtual bool open(Time) const;
virtual void close() const;
virtual Time getTime() const;
virtual bool has(EFormat) const;
virtual std::string get(EFormat) const;
private:
// remove all converters from our list
void clearConverters();
// get the converter for a clipboard format. returns NULL if no
// suitable converter. iff onlyIfNotAdded is true then also
// return NULL if a suitable converter was found but we already
// have data of the converter's clipboard format.
IXWindowsClipboardConverter *getConverter(Atom target, bool onlyIfNotAdded = false) const;
// convert target atom to clipboard format
EFormat getFormat(Atom target) const;
// add a non-MULTIPLE request. does not verify that the selection
// was owned at the given time. returns true if the conversion
// could be performed, false otherwise. in either case, the
// reply is inserted.
bool addSimpleRequest(Window requestor, Atom target, ::Time time, Atom property);
// if not already checked then see if the cache is stale and, if so,
// clear it. this has the side effect of updating m_timeOwned.
void checkCache() const;
// clear the cache, resetting the cached flag and the added flag for
// each format.
void clearCache() const;
void doClearCache();
// cache all formats of the selection
void fillCache() const;
void doFillCache();
protected:
//
// helper classes
//
// read an ICCCM conforming selection
class CICCCMGetClipboard
{
public:
CICCCMGetClipboard(Window requestor, Time time, Atom property);
~CICCCMGetClipboard() = default;
// convert the given selection to the given type. returns
// true iff the conversion was successful or the conversion
// cannot be performed (in which case *actualTarget == None).
bool readClipboard(Display *display, Atom selection, Atom target, Atom *actualTarget, std::string *data);
private:
bool processEvent(Display *display, XEvent *event);
private:
Window m_requestor;
Time m_time;
Atom m_property;
bool m_incr = false;
bool m_failed = false;
bool m_done = false;
// atoms needed for the protocol
Atom m_atomNone = None;
Atom m_atomIncr = None;
// true iff we've received the selection notify
bool m_reading = false;
// the converted selection data
std::string *m_data = nullptr;
// the actual type of the data. if this is None then the
// selection owner cannot convert to the requested type.
Atom *m_actualTarget = nullptr;
// true iff the selection owner didn't follow ICCCM conventions
bool m_error = false;
public:
bool error() const
{
return m_error;
}
};
// Motif structure IDs
enum class MotifClip : int32_t
{
Format = 1,
Item = 2,
Header = 3
};
// _MOTIF_CLIP_HEADER structure
class MotifClipHeader
{
public:
MotifClip m_id;
int32_t m_pad1[3];
int32_t m_item;
int32_t m_pad2[4];
int32_t m_numItems;
int32_t m_pad3[3];
int32_t m_selectionOwner; // a Window
int32_t m_pad4[2];
};
// Motif clip item structure
class MotifClipItem
{
public:
MotifClip m_id;
int32_t m_pad1[5];
int32_t m_size;
int32_t m_numFormats;
int32_t m_numDeletedFormats;
int32_t m_pad2[6];
};
// Motif clip format structure
class MotifClipFormat
{
public:
MotifClip m_id;
int32_t m_pad1[6];
int32_t m_length;
int32_t m_data;
int32_t m_type; // an Atom
int32_t m_pad2[1];
int32_t m_deleted;
int32_t m_pad3[4];
};
// stores data needed to respond to a selection request
class Reply
{
public:
Reply(Window, Atom target, ::Time);
Reply(Window, Atom target, ::Time, Atom property, const std::string &data, Atom type, int format);
public:
// information about the request
Window m_requestor;
Atom m_target;
::Time m_time;
Atom m_property;
// true iff we've sent the notification for this reply
bool m_replied;
// true iff the reply has sent its last message
bool m_done;
// the data to send and its type and format
std::string m_data;
Atom m_type;
int m_format;
// index of next byte in m_data to send
uint32_t m_ptr;
};
using ReplyList = std::list<Reply *>;
using ReplyMap = std::map<Window, ReplyList>;
using ReplyEventMask = std::map<Window, long>;
// ICCCM interoperability methods
void icccmFillCache();
bool icccmGetSelection(Atom target, Atom *actualTarget, std::string *data) const;
Time icccmGetTime() const;
// motif interoperability methods
bool motifLockClipboard() const;
void motifUnlockClipboard() const;
bool motifOwnsClipboard() const;
void motifFillCache();
bool motifGetSelection(const MotifClipFormat *, Atom *actualTarget, std::string *data) const;
Time motifGetTime() const;
// reply methods
bool insertMultipleReply(Window, ::Time, Atom);
void insertReply(Reply *);
void pushReplies();
void pushReplies(ReplyMap::iterator &, ReplyList &, ReplyList::iterator);
bool sendReply(Reply *);
void clearReplies();
void clearReplies(ReplyList &);
void sendNotify(Window requestor, Atom selection, Atom target, Atom property, Time time);
bool wasOwnedAtTime(::Time) const;
// data conversion methods
Atom getTargetsData(std::string &, int *format) const;
Atom getTimestampData(std::string &, int *format) const;
private:
using ConverterList = std::vector<IXWindowsClipboardConverter *>;
Display *m_display;
Window m_window;
ClipboardID m_id;
Atom m_selection;
mutable bool m_open;
mutable Time m_time;
bool m_owner;
mutable Time m_timeOwned;
Time m_timeLost;
// true iff open and clipboard owned by a motif app
mutable bool m_motif;
// the added/cached clipboard data
mutable bool m_checkCache;
bool m_cached;
Time m_cacheTime;
bool m_added[kNumFormats];
std::string m_data[kNumFormats];
// conversion request replies
ReplyMap m_replies;
ReplyEventMask m_eventMasks;
// clipboard format converters
ConverterList m_converters;
// atoms we'll need
Atom m_atomTargets;
Atom m_atomMultiple;
Atom m_atomTimestamp;
Atom m_atomInteger;
Atom m_atomAtom;
Atom m_atomAtomPair;
Atom m_atomData;
Atom m_atomINCR;
Atom m_atomMotifClipLock;
Atom m_atomMotifClipHeader;
Atom m_atomMotifClipAccess;
Atom m_atomGDKSelection;
};
//! Clipboard format converter interface
/*!
This interface defines the methods common to all X11 clipboard format
converters.
*/
class IXWindowsClipboardConverter : public IInterface
{
public:
//! @name accessors
//@{
//! Get clipboard format
/*!
Return the clipboard format this object converts from/to.
*/
virtual IClipboard::EFormat getFormat() const = 0;
//! Get X11 format atom
/*!
Return the atom representing the X selection format that
this object converts from/to.
*/
virtual Atom getAtom() const = 0;
//! Get X11 property datum size
/*!
Return the size (in bits) of data elements returned by
toIClipboard().
*/
virtual int getDataSize() const = 0;
//! Convert from IClipboard format
/*!
Convert from the IClipboard format to the X selection format.
The input data must be in the IClipboard format returned by
getFormat(). The return data will be in the X selection
format returned by getAtom().
*/
virtual std::string fromIClipboard(const std::string &) const = 0;
//! Convert to IClipboard format
/*!
Convert from the X selection format to the IClipboard format
(i.e., the reverse of fromIClipboard()).
*/
virtual std::string toIClipboard(const std::string &) const = 0;
//@}
};