2002-08-02 19:57:46 +00:00
/*
* synergy - - mouse and keyboard sharing utility
* Copyright ( C ) 2002 Chris Schoeneman
*
* This package is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file .
*
* This package is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2001-11-13 23:34:12 +00:00
# include "CXWindowsClipboard.h"
2002-07-22 17:32:51 +00:00
# include "CXWindowsClipboardTextConverter.h"
# include "CXWindowsClipboardUCS2Converter.h"
# include "CXWindowsClipboardUTF8Converter.h"
2002-05-27 16:22:59 +00:00
# include "CXWindowsUtil.h"
# include "CThread.h"
2002-06-10 22:06:45 +00:00
# include "CLog.h"
2002-06-20 13:35:28 +00:00
# include "CStopwatch.h"
2003-01-04 22:01:32 +00:00
# include "CArch.h"
2002-06-20 16:27:49 +00:00
# include "stdvector.h"
2002-06-10 22:06:45 +00:00
# include <cstdio>
2002-05-27 16:22:59 +00:00
# include <X11/Xatom.h>
2001-11-13 23:34:12 +00:00
//
// CXWindowsClipboard
//
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : CXWindowsClipboard ( Display * display ,
Window window , ClipboardID id ) :
2002-06-10 22:06:45 +00:00
m_display ( display ) ,
m_window ( window ) ,
m_id ( id ) ,
m_open ( false ) ,
m_time ( 0 ) ,
m_owner ( false ) ,
m_timeOwned ( 0 ) ,
m_timeLost ( 0 )
2001-11-13 23:34:12 +00:00
{
2002-05-27 16:22:59 +00:00
// get some atoms
m_atomTargets = XInternAtom ( m_display , " TARGETS " , False ) ;
m_atomMultiple = XInternAtom ( m_display , " MULTIPLE " , False ) ;
m_atomTimestamp = XInternAtom ( m_display , " TIMESTAMP " , False ) ;
2002-07-24 17:07:52 +00:00
m_atomInteger = XInternAtom ( m_display , " INTEGER " , False ) ;
2002-07-24 17:30:32 +00:00
m_atomAtom = XInternAtom ( m_display , " ATOM " , False ) ;
2002-05-27 16:22:59 +00:00
m_atomAtomPair = XInternAtom ( m_display , " ATOM_PAIR " , False ) ;
m_atomData = XInternAtom ( m_display , " CLIP_TEMPORARY " , False ) ;
m_atomINCR = XInternAtom ( m_display , " INCR " , False ) ;
m_atomMotifClipLock = XInternAtom ( m_display , " _MOTIF_CLIP_LOCK " , False ) ;
m_atomMotifClipHeader = XInternAtom ( m_display , " _MOTIF_CLIP_HEADER " , False ) ;
m_atomMotifClipAccess = XInternAtom ( m_display ,
" _MOTIF_CLIP_LOCK_ACCESS_VALID " , False ) ;
m_atomGDKSelection = XInternAtom ( m_display , " GDK_SELECTION " , False ) ;
// set selection atom based on clipboard id
switch ( id ) {
case kClipboardClipboard :
m_selection = XInternAtom ( m_display , " CLIPBOARD " , False ) ;
break ;
case kClipboardSelection :
default :
m_selection = XA_PRIMARY ;
break ;
}
2002-07-22 18:03:44 +00:00
// add converters, most desired first
2002-07-22 17:32:51 +00:00
m_converters . push_back ( new CXWindowsClipboardUTF8Converter ( m_display ,
" text/plain;charset=UTF-8 " ) ) ;
m_converters . push_back ( new CXWindowsClipboardUTF8Converter ( m_display ,
" UTF8_STRING " ) ) ;
m_converters . push_back ( new CXWindowsClipboardUCS2Converter ( m_display ,
" text/plain;charset=ISO-10646-UCS-2 " ) ) ;
m_converters . push_back ( new CXWindowsClipboardUCS2Converter ( m_display ,
" text/unicode " ) ) ;
m_converters . push_back ( new CXWindowsClipboardTextConverter ( m_display ,
" text/plain " ) ) ;
m_converters . push_back ( new CXWindowsClipboardTextConverter ( m_display ,
" STRING " ) ) ;
2002-05-27 16:22:59 +00:00
// we have no data
clearCache ( ) ;
2001-11-13 23:34:12 +00:00
}
CXWindowsClipboard : : ~ CXWindowsClipboard ( )
{
2002-05-27 16:22:59 +00:00
clearReplies ( ) ;
2002-07-22 17:32:51 +00:00
clearConverters ( ) ;
2002-05-27 16:22:59 +00:00
}
2002-06-10 22:06:45 +00:00
void
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : lost ( Time time )
2002-05-27 16:22:59 +00:00
{
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " lost clipboard %d ownership at %d " , m_id , time ) ) ;
2002-05-27 16:22:59 +00:00
if ( m_owner ) {
m_owner = false ;
m_timeLost = time ;
clearCache ( ) ;
}
}
2002-06-10 22:06:45 +00:00
void
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : addRequest ( Window owner , Window requestor ,
Atom target , : : Time time , Atom property )
2002-05-27 16:22:59 +00:00
{
// must be for our window and we must have owned the selection
// at the given time.
bool success = false ;
if ( owner = = m_window ) {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " request for clipboard %d, target %s by 0x%08x (property=%s) " , m_selection , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) , requestor , CXWindowsUtil : : atomToString ( m_display , property ) . c_str ( ) ) ) ;
2002-05-27 16:22:59 +00:00
if ( wasOwnedAtTime ( time ) ) {
if ( target = = m_atomMultiple ) {
// add a multiple request. property may not be None
// according to ICCCM.
if ( property ! = None ) {
success = insertMultipleReply ( requestor , time , property ) ;
}
}
else {
addSimpleRequest ( requestor , target , time , property ) ;
// addSimpleRequest() will have already handled failure
success = true ;
}
}
else {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " failed, not owned at time %d " , time ) ) ;
2002-05-27 16:22:59 +00:00
}
}
if ( ! success ) {
// send failure
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " failed " ) ) ;
2002-05-27 16:22:59 +00:00
insertReply ( new CReply ( requestor , target , time ) ) ;
}
// send notifications that are pending
pushReplies ( ) ;
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : addSimpleRequest ( Window requestor ,
Atom target , : : Time time , Atom property )
2002-05-27 16:22:59 +00:00
{
// obsolete requestors may supply a None property. in
// that case we use the target as the property to store
// the conversion.
if ( property = = None ) {
property = target ;
}
// handle targets
CString data ;
2002-07-22 17:32:51 +00:00
Atom type = None ;
2002-05-27 16:22:59 +00:00
int format = 0 ;
if ( target = = m_atomTargets ) {
type = getTargetsData ( data , & format ) ;
}
else if ( target = = m_atomTimestamp ) {
type = getTimestampData ( data , & format ) ;
}
2002-07-22 17:32:51 +00:00
else {
IXWindowsClipboardConverter * converter = getConverter ( target ) ;
2002-07-22 18:17:21 +00:00
if ( converter ! = NULL ) {
IClipboard : : EFormat clipboardFormat = converter - > getFormat ( ) ;
if ( m_added [ clipboardFormat ] ) {
2002-07-23 09:33:50 +00:00
try {
data = converter - > fromIClipboard ( m_data [ clipboardFormat ] ) ;
format = converter - > getDataSize ( ) ;
type = converter - > getAtom ( ) ;
}
catch ( . . . ) {
// ignore -- cannot convert
}
2002-07-22 18:17:21 +00:00
}
2002-07-22 17:32:51 +00:00
}
2002-05-27 16:22:59 +00:00
}
if ( type ! = None ) {
// success
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " success " ) ) ;
2002-05-27 16:22:59 +00:00
insertReply ( new CReply ( requestor , target , time ,
property , data , type , format ) ) ;
return true ;
}
else {
// failure
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " failed " ) ) ;
2002-05-27 16:22:59 +00:00
insertReply ( new CReply ( requestor , target , time ) ) ;
return false ;
}
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : processRequest ( Window requestor ,
: : Time /*time*/ , Atom property )
2002-05-27 16:22:59 +00:00
{
CReplyMap : : iterator index = m_replies . find ( requestor ) ;
if ( index = = m_replies . end ( ) ) {
// unknown requestor window
return false ;
}
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " received property %s delete from 0x08%x " , CXWindowsUtil : : atomToString ( m_display , property ) . c_str ( ) , requestor ) ) ;
2002-05-27 16:22:59 +00:00
// find the property in the known requests. it should be the
// first property but we'll check 'em all if we have to.
CReplyList & replies = index - > second ;
for ( CReplyList : : iterator index2 = replies . begin ( ) ;
index2 ! = replies . end ( ) ; + + index2 ) {
CReply * reply = * index2 ;
if ( reply - > m_replied & & reply - > m_property = = property ) {
// if reply is complete then remove it and start the
// next one.
pushReplies ( index , replies , index2 ) ;
return true ;
}
}
return false ;
2001-11-13 23:34:12 +00:00
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : destroyRequest ( Window requestor )
2001-11-13 23:34:12 +00:00
{
2002-05-27 16:22:59 +00:00
CReplyMap : : iterator index = m_replies . find ( requestor ) ;
if ( index = = m_replies . end ( ) ) {
// unknown requestor window
return false ;
}
// destroy all replies for this window
clearReplies ( index - > second ) ;
2002-05-27 18:30:13 +00:00
m_replies . erase ( index ) ;
2002-05-27 16:22:59 +00:00
// note -- we don't stop watching the window for events because
// we're called in response to the window being destroyed.
2001-11-25 18:44:13 +00:00
return true ;
2001-11-13 23:34:12 +00:00
}
2002-06-10 22:06:45 +00:00
Window
CXWindowsClipboard : : getWindow ( ) const
2002-05-27 16:22:59 +00:00
{
return m_window ;
}
2002-06-10 22:06:45 +00:00
Atom
CXWindowsClipboard : : getSelection ( ) const
2002-05-27 16:22:59 +00:00
{
return m_selection ;
}
2002-06-10 22:06:45 +00:00
bool
CXWindowsClipboard : : empty ( )
2001-11-13 23:34:12 +00:00
{
2002-05-27 16:22:59 +00:00
assert ( m_open ) ;
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " empty clipboard %d " , m_id ) ) ;
2002-05-27 16:22:59 +00:00
// assert ownership of clipboard
XSetSelectionOwner ( m_display , m_selection , m_window , m_time ) ;
if ( XGetSelectionOwner ( m_display , m_selection ) ! = m_window ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " failed to grab clipboard %d " , m_id ) ) ;
2002-05-27 16:22:59 +00:00
return false ;
}
// clear all data. since we own the data now, the cache is up
// to date.
clearCache ( ) ;
m_cached = true ;
// FIXME -- actually delete motif clipboard items?
// FIXME -- do anything to motif clipboard properties?
// save time
m_timeOwned = m_time ;
m_timeLost = 0 ;
// we're the owner now
m_owner = true ;
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " grabbed clipboard %d " , m_id ) ) ;
2002-05-27 16:22:59 +00:00
return true ;
2001-11-13 23:34:12 +00:00
}
2002-06-10 22:06:45 +00:00
void
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : add ( EFormat format , const CString & data )
2001-11-13 23:34:12 +00:00
{
2002-05-27 16:22:59 +00:00
assert ( m_open ) ;
assert ( m_owner ) ;
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " add %d bytes to clipboard %d format: %d " , data . size ( ) , m_id , format ) ) ;
2002-05-27 16:22:59 +00:00
m_data [ format ] = data ;
m_added [ format ] = true ;
// FIXME -- set motif clipboard item?
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : open ( Time time ) const
2002-05-27 16:22:59 +00:00
{
assert ( ! m_open ) ;
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " open clipboard %d " , m_id ) ) ;
2002-05-27 16:22:59 +00:00
// assume not motif
m_motif = false ;
// lock clipboard
if ( m_id = = kClipboardClipboard ) {
if ( ! motifLockClipboard ( ) ) {
return false ;
}
// check if motif owns the selection. unlock motif clipboard
// if it does not.
m_motif = motifOwnsClipboard ( ) ;
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " motif does %sown clipboard " , m_motif ? " " : " not " ) ) ;
2002-05-27 16:22:59 +00:00
if ( ! m_motif ) {
motifUnlockClipboard ( ) ;
}
}
// now open
m_open = true ;
m_time = time ;
2002-06-20 13:35:28 +00:00
// be sure to flush the cache later if it's dirty
m_checkCache = true ;
2002-05-27 16:22:59 +00:00
return true ;
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : close ( ) const
2002-05-27 16:22:59 +00:00
{
assert ( m_open ) ;
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " close clipboard %d " , m_id ) ) ;
2002-05-27 16:22:59 +00:00
// unlock clipboard
if ( m_motif ) {
motifUnlockClipboard ( ) ;
}
m_motif = false ;
m_open = false ;
}
2002-06-10 22:06:45 +00:00
IClipboard : : Time
CXWindowsClipboard : : getTime ( ) const
2002-05-27 16:22:59 +00:00
{
2002-06-20 13:35:28 +00:00
checkCache ( ) ;
2002-05-27 16:22:59 +00:00
return m_timeOwned ;
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : has ( EFormat format ) const
2002-05-27 16:22:59 +00:00
{
assert ( m_open ) ;
fillCache ( ) ;
return m_added [ format ] ;
}
2002-06-10 22:06:45 +00:00
CString
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : get ( EFormat format ) const
2002-05-27 16:22:59 +00:00
{
assert ( m_open ) ;
fillCache ( ) ;
return m_data [ format ] ;
}
2002-07-22 17:32:51 +00:00
void
CXWindowsClipboard : : clearConverters ( )
2002-05-27 16:22:59 +00:00
{
2002-07-22 17:32:51 +00:00
for ( ConverterList : : iterator index = m_converters . begin ( ) ;
index ! = m_converters . end ( ) ; + + index ) {
delete * index ;
}
m_converters . clear ( ) ;
}
IXWindowsClipboardConverter *
CXWindowsClipboard : : getConverter ( Atom target , bool onlyIfNotAdded ) const
{
IXWindowsClipboardConverter * converter = NULL ;
for ( ConverterList : : const_iterator index = m_converters . begin ( ) ;
index ! = m_converters . end ( ) ; + + index ) {
converter = * index ;
if ( converter - > getAtom ( ) = = target ) {
break ;
}
}
if ( converter = = NULL ) {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " no converter for target %s " , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) ) ) ;
2002-07-22 17:32:51 +00:00
return NULL ;
}
// optionally skip already handled targets
if ( onlyIfNotAdded ) {
if ( m_added [ converter - > getFormat ( ) ] ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " skipping handled format %d " , converter - > getFormat ( ) ) ) ;
2002-07-22 17:32:51 +00:00
return NULL ;
}
}
return converter ;
2002-05-27 16:22:59 +00:00
}
2002-06-20 13:35:28 +00:00
void
CXWindowsClipboard : : checkCache ( ) const
{
if ( ! m_checkCache ) {
return ;
}
m_checkCache = false ;
// get the time the clipboard ownership was taken by the current
// owner.
if ( m_motif ) {
m_timeOwned = motifGetTime ( ) ;
}
else {
m_timeOwned = icccmGetTime ( ) ;
}
// if we can't get the time then use the time passed to us
if ( m_timeOwned = = 0 ) {
m_timeOwned = m_time ;
}
// if the cache is dirty then flush it
if ( m_timeOwned ! = m_cacheTime ) {
clearCache ( ) ;
}
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : clearCache ( ) const
2002-05-27 16:22:59 +00:00
{
const_cast < CXWindowsClipboard * > ( this ) - > doClearCache ( ) ;
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : doClearCache ( )
2002-05-27 16:22:59 +00:00
{
2002-06-20 13:35:28 +00:00
m_checkCache = false ;
m_cached = false ;
2002-05-27 16:22:59 +00:00
for ( SInt32 index = 0 ; index < kNumFormats ; + + index ) {
m_data [ index ] = " " ;
m_added [ index ] = false ;
}
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : fillCache ( ) const
2002-05-27 16:22:59 +00:00
{
// get the selection data if not already cached
2002-06-20 13:35:28 +00:00
checkCache ( ) ;
2002-05-27 16:22:59 +00:00
if ( ! m_cached ) {
const_cast < CXWindowsClipboard * > ( this ) - > doFillCache ( ) ;
}
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : doFillCache ( )
2002-05-27 16:22:59 +00:00
{
if ( m_motif ) {
motifFillCache ( ) ;
}
else {
icccmFillCache ( ) ;
}
2002-06-20 13:35:28 +00:00
m_checkCache = false ;
m_cached = true ;
m_cacheTime = m_timeOwned ;
2002-05-27 16:22:59 +00:00
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : icccmFillCache ( )
2002-05-27 16:22:59 +00:00
{
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " ICCCM fill clipboard %d " , m_id ) ) ;
2002-05-27 16:22:59 +00:00
// see if we can get the list of available formats from the selection.
2003-05-26 09:50:35 +00:00
// if not then use a default list of formats. note that some clipboard
// owners are broken and report TARGETS as the type of the TARGETS data
// instead of the correct type ATOM; allow either.
2002-05-27 16:22:59 +00:00
const Atom atomTargets = m_atomTargets ;
Atom target ;
CString data ;
2002-07-24 17:30:32 +00:00
if ( ! icccmGetSelection ( atomTargets , & target , & data ) | |
2003-05-26 09:50:35 +00:00
( target ! = m_atomAtom & & target ! = m_atomTargets ) ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " selection doesn't support TARGETS " ) ) ;
2002-05-27 16:22:59 +00:00
data = " " ;
target = XA_STRING ;
data . append ( reinterpret_cast < char * > ( & target ) , sizeof ( target ) ) ;
}
const Atom * targets = reinterpret_cast < const Atom * > ( data . data ( ) ) ;
const UInt32 numTargets = data . size ( ) / sizeof ( Atom ) ;
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG " available targets: %s " , CXWindowsUtil : : atomsToString ( m_display , targets , numTargets ) . c_str ( ) ) ) ;
// try each converter in order (because they're in order of
// preference).
2002-07-22 18:03:44 +00:00
for ( ConverterList : : const_iterator index = m_converters . begin ( ) ;
index ! = m_converters . end ( ) ; + + index ) {
IXWindowsClipboardConverter * converter = * index ;
// skip already handled targets
if ( m_added [ converter - > getFormat ( ) ] ) {
continue ;
}
// see if atom is in target list
Atom target = None ;
for ( UInt32 i = 0 ; i < numTargets ; + + i ) {
if ( converter - > getAtom ( ) = = targets [ i ] ) {
target = targets [ i ] ;
break ;
}
}
if ( target = = None ) {
2002-05-27 16:22:59 +00:00
continue ;
}
2002-07-22 17:32:51 +00:00
// get the data
2002-05-27 16:22:59 +00:00
Atom actualTarget ;
CString targetData ;
if ( ! icccmGetSelection ( target , & actualTarget , & targetData ) ) {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " no data for target %s " , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) ) ) ;
2002-05-27 16:22:59 +00:00
continue ;
}
// add to clipboard and note we've done it
2002-07-23 09:33:50 +00:00
IClipboard : : EFormat format = converter - > getFormat ( ) ;
2002-07-23 11:36:18 +00:00
m_data [ format ] = converter - > toIClipboard ( targetData ) ;
m_added [ format ] = true ;
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG " added format %d for target %s (%u %s) " , format , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) , targetData . size ( ) , targetData . size ( ) = = 1 ? " byte " : " bytes " ) ) ;
2002-05-27 16:22:59 +00:00
}
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : icccmGetSelection ( Atom target ,
Atom * actualTarget , CString * data ) const
2002-05-27 16:22:59 +00:00
{
assert ( actualTarget ! = NULL ) ;
assert ( data ! = NULL ) ;
// request data conversion
CICCCMGetClipboard getter ( m_window , m_time , m_atomData ) ;
if ( ! getter . readClipboard ( m_display , m_selection ,
target , actualTarget , data ) ) {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " can't get data for selection target %s " , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) ) ) ;
2002-10-15 21:29:44 +00:00
LOGC ( getter . m_error , ( CLOG_WARN " ICCCM violation by clipboard owner " ) ) ;
2002-05-27 16:22:59 +00:00
return false ;
}
else if ( * actualTarget = = None ) {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " selection conversion failed for target %s " , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) ) ) ;
2002-05-27 16:22:59 +00:00
return false ;
}
return true ;
}
2002-06-10 22:06:45 +00:00
IClipboard : : Time
CXWindowsClipboard : : icccmGetTime ( ) const
2002-05-27 16:22:59 +00:00
{
Atom actualTarget ;
CString data ;
if ( icccmGetSelection ( m_atomTimestamp , & actualTarget , & data ) & &
2002-07-24 17:07:52 +00:00
actualTarget = = m_atomInteger ) {
2002-05-27 16:22:59 +00:00
Time time = * reinterpret_cast < const Time * > ( data . data ( ) ) ;
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " got ICCCM time %d " , time ) ) ;
2002-05-27 16:22:59 +00:00
return time ;
}
else {
// no timestamp
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " can't get ICCCM time " ) ) ;
2002-05-27 16:22:59 +00:00
return 0 ;
}
}
2002-06-10 22:06:45 +00:00
bool
CXWindowsClipboard : : motifLockClipboard ( ) const
2002-05-27 16:22:59 +00:00
{
// fail if anybody owns the lock (even us, so this is non-recursive)
Window lockOwner = XGetSelectionOwner ( m_display , m_atomMotifClipLock ) ;
if ( lockOwner ! = None ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " motif lock owner 0x%08x " , lockOwner ) ) ;
2002-05-27 16:22:59 +00:00
return false ;
}
// try to grab the lock
// FIXME -- is this right? there's a race condition here --
// A grabs successfully, B grabs successfully, A thinks it
// still has the grab until it gets a SelectionClear.
Time time = CXWindowsUtil : : getCurrentTime ( m_display , m_window ) ;
XSetSelectionOwner ( m_display , m_atomMotifClipLock , m_window , time ) ;
lockOwner = XGetSelectionOwner ( m_display , m_atomMotifClipLock ) ;
if ( lockOwner ! = m_window ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " motif lock owner 0x%08x " , lockOwner ) ) ;
2002-05-27 16:22:59 +00:00
return false ;
}
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " locked motif clipboard " ) ) ;
2002-05-27 16:22:59 +00:00
return true ;
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : motifUnlockClipboard ( ) const
2002-05-27 16:22:59 +00:00
{
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " unlocked motif clipboard " ) ) ;
2002-05-27 16:22:59 +00:00
// fail if we don't own the lock
Window lockOwner = XGetSelectionOwner ( m_display , m_atomMotifClipLock ) ;
if ( lockOwner ! = m_window ) {
return ;
}
// release lock
Time time = CXWindowsUtil : : getCurrentTime ( m_display , m_window ) ;
XSetSelectionOwner ( m_display , m_atomMotifClipLock , None , time ) ;
}
2002-06-10 22:06:45 +00:00
bool
CXWindowsClipboard : : motifOwnsClipboard ( ) const
2002-05-27 16:22:59 +00:00
{
// get the current selection owner
// FIXME -- this can't be right. even if the window is destroyed
// Motif will still have a valid clipboard. how can we tell if
// some other client owns CLIPBOARD?
Window owner = XGetSelectionOwner ( m_display , m_selection ) ;
if ( owner = = None ) {
return false ;
}
// get the Motif clipboard header property from the root window
Atom target ;
SInt32 format ;
CString data ;
2002-07-24 17:07:52 +00:00
Window root = RootWindow ( m_display , DefaultScreen ( m_display ) ) ;
2002-05-27 16:22:59 +00:00
if ( ! CXWindowsUtil : : getWindowProperty ( m_display , root ,
m_atomMotifClipHeader ,
& data , & target , & format , False ) ) {
return false ;
}
// check the owner window against the current clipboard owner
const CMotifClipHeader * header =
reinterpret_cast < const CMotifClipHeader * > ( data . data ( ) ) ;
if ( data . size ( ) > = sizeof ( CMotifClipHeader ) & &
header - > m_id = = kMotifClipHeader ) {
if ( header - > m_selectionOwner = = owner ) {
return true ;
}
}
return false ;
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : motifFillCache ( )
2002-05-27 16:22:59 +00:00
{
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG " Motif fill clipboard %d " , m_id ) ) ;
2002-05-27 16:22:59 +00:00
// get the Motif clipboard header property from the root window
Atom target ;
SInt32 format ;
CString data ;
Window root = RootWindow ( m_display , DefaultScreen ( m_display ) ) ;
if ( ! CXWindowsUtil : : getWindowProperty ( m_display , root ,
m_atomMotifClipHeader ,
& data , & target , & format , False ) ) {
return ;
}
// check that the header is okay
const CMotifClipHeader * header =
reinterpret_cast < const CMotifClipHeader * > ( data . data ( ) ) ;
if ( data . size ( ) < sizeof ( CMotifClipHeader ) | |
header - > m_id ! = kMotifClipHeader | |
header - > m_numItems < 1 ) {
return ;
}
// get the Motif item property from the root window
char name [ 18 + 20 ] ;
sprintf ( name , " _MOTIF_CLIP_ITEM_%d " , header - > m_item ) ;
Atom atomItem = XInternAtom ( m_display , name , False ) ;
data = " " ;
if ( ! CXWindowsUtil : : getWindowProperty ( m_display , root ,
atomItem , & data ,
& target , & format , False ) ) {
return ;
}
// check that the item is okay
const CMotifClipItem * item =
reinterpret_cast < const CMotifClipItem * > ( data . data ( ) ) ;
if ( data . size ( ) < sizeof ( CMotifClipItem ) | |
item - > m_id ! = kMotifClipItem | |
2002-07-24 17:07:52 +00:00
item - > m_numFormats - item - > m_numDeletedFormats < 1 ) {
2002-05-27 16:22:59 +00:00
return ;
}
2002-07-24 17:07:52 +00:00
// format list is after static item structure elements
const SInt32 numFormats = item - > m_numFormats - item - > m_numDeletedFormats ;
const SInt32 * formats = reinterpret_cast < const SInt32 * > ( item - > m_size +
reinterpret_cast < const char * > ( data . data ( ) ) ) ;
2002-07-22 18:03:44 +00:00
// get the available formats
2002-07-24 17:07:52 +00:00
typedef std : : map < Atom , CString > CMotifFormatMap ;
CMotifFormatMap motifFormats ;
for ( SInt32 i = 0 ; i < numFormats ; + + i ) {
2002-05-27 16:22:59 +00:00
// get Motif format property from the root window
2002-07-24 17:07:52 +00:00
sprintf ( name , " _MOTIF_CLIP_ITEM_%d " , formats [ i ] ) ;
2002-05-27 16:22:59 +00:00
Atom atomFormat = XInternAtom ( m_display , name , False ) ;
CString data ;
if ( ! CXWindowsUtil : : getWindowProperty ( m_display , root ,
atomFormat , & data ,
& target , & format , False ) ) {
continue ;
}
// check that the format is okay
const CMotifClipFormat * motifFormat =
reinterpret_cast < const CMotifClipFormat * > ( data . data ( ) ) ;
if ( data . size ( ) < sizeof ( CMotifClipFormat ) | |
motifFormat - > m_id ! = kMotifClipFormat | |
motifFormat - > m_length < 0 | |
2002-07-24 17:07:52 +00:00
motifFormat - > m_type = = None | |
motifFormat - > m_deleted ! = 0 ) {
2002-05-27 16:22:59 +00:00
continue ;
}
2002-07-22 18:03:44 +00:00
// save it
2002-07-24 17:07:52 +00:00
motifFormats . insert ( std : : make_pair ( motifFormat - > m_type , data ) ) ;
2002-07-22 18:03:44 +00:00
}
2004-02-28 12:19:49 +00:00
//const UInt32 numMotifFormats = motifFormats.size();
2002-07-22 18:03:44 +00:00
// try each converter in order (because they're in order of
// preference).
for ( ConverterList : : const_iterator index = m_converters . begin ( ) ;
index ! = m_converters . end ( ) ; + + index ) {
IXWindowsClipboardConverter * converter = * index ;
// skip already handled targets
if ( m_added [ converter - > getFormat ( ) ] ) {
continue ;
}
// see if atom is in target list
2003-01-04 22:01:32 +00:00
CMotifFormatMap : : const_iterator index2 =
2002-07-24 17:07:52 +00:00
motifFormats . find ( converter - > getAtom ( ) ) ;
2003-01-04 22:01:32 +00:00
if ( index2 = = motifFormats . end ( ) ) {
2002-05-27 16:22:59 +00:00
continue ;
}
2002-07-24 17:07:52 +00:00
// get format
const CMotifClipFormat * motifFormat =
reinterpret_cast < const CMotifClipFormat * > (
2003-01-04 22:01:32 +00:00
index2 - > second . data ( ) ) ;
2002-07-24 17:07:52 +00:00
const Atom target = motifFormat - > m_type ;
2002-05-27 16:22:59 +00:00
// get the data (finally)
2002-07-24 17:07:52 +00:00
Atom actualTarget ;
2002-07-22 17:32:51 +00:00
CString targetData ;
2002-07-24 17:07:52 +00:00
if ( ! motifGetSelection ( motifFormat , & actualTarget , & targetData ) ) {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " no data for target %s " , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) ) ) ;
2002-05-27 16:22:59 +00:00
continue ;
}
// add to clipboard and note we've done it
2002-07-23 09:33:50 +00:00
IClipboard : : EFormat format = converter - > getFormat ( ) ;
2002-07-23 11:36:18 +00:00
m_data [ format ] = converter - > toIClipboard ( targetData ) ;
m_added [ format ] = true ;
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG " added format %d for target %s " , format , CXWindowsUtil : : atomToString ( m_display , target ) . c_str ( ) ) ) ;
2002-05-27 16:22:59 +00:00
}
}
2002-07-24 17:07:52 +00:00
bool
CXWindowsClipboard : : motifGetSelection ( const CMotifClipFormat * format ,
Atom * actualTarget , CString * data ) const
{
// if the current clipboard owner and the owner indicated by the
// motif clip header are the same then transfer via a property on
// the root window, otherwise transfer as a normal ICCCM client.
if ( ! motifOwnsClipboard ( ) ) {
return icccmGetSelection ( format - > m_type , actualTarget , data ) ;
}
// use motif way
// FIXME -- this isn't right. it'll only work if the data is
// already stored on the root window and only if it fits in a
// property. motif has some scheme for transferring part by
// part that i don't know.
char name [ 18 + 20 ] ;
sprintf ( name , " _MOTIF_CLIP_ITEM_%d " , format - > m_data ) ;
Atom target = XInternAtom ( m_display , name , False ) ;
Window root = RootWindow ( m_display , DefaultScreen ( m_display ) ) ;
return CXWindowsUtil : : getWindowProperty ( m_display , root ,
target , data ,
actualTarget , NULL , False ) ;
}
2002-06-10 22:06:45 +00:00
IClipboard : : Time
CXWindowsClipboard : : motifGetTime ( ) const
2002-05-27 16:22:59 +00:00
{
2002-07-25 09:23:24 +00:00
return icccmGetTime ( ) ;
2002-05-27 16:22:59 +00:00
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : insertMultipleReply ( Window requestor ,
: : Time time , Atom property )
2002-05-27 16:22:59 +00:00
{
// get the requested targets
Atom target ;
SInt32 format ;
CString data ;
if ( ! CXWindowsUtil : : getWindowProperty ( m_display , requestor ,
property , & data , & target , & format , False ) ) {
// can't get the requested targets
return false ;
}
// fail if the requested targets isn't of the correct form
if ( format ! = 32 | |
target ! = m_atomAtomPair ) {
return false ;
}
// data is a list of atom pairs: target, property
const Atom * targets = reinterpret_cast < const Atom * > ( data . data ( ) ) ;
const UInt32 numTargets = data . size ( ) / sizeof ( Atom ) ;
// add replies for each target
bool changed = false ;
for ( UInt32 i = 0 ; i < numTargets ; i + = 2 ) {
const Atom target = targets [ i + 0 ] ;
const Atom property = targets [ i + 1 ] ;
if ( ! addSimpleRequest ( requestor , target , time , property ) ) {
// note that we can't perform the requested conversion
static const Atom none = None ;
data . replace ( i * sizeof ( Atom ) , sizeof ( Atom ) ,
reinterpret_cast < const char * > ( & none ) ,
sizeof ( Atom ) ) ;
changed = true ;
}
}
// update the targets property if we changed it
if ( changed ) {
CXWindowsUtil : : setWindowProperty ( m_display , requestor ,
property , data . data ( ) , data . size ( ) ,
target , format ) ;
}
// add reply for MULTIPLE request
insertReply ( new CReply ( requestor , m_atomMultiple ,
time , property , CString ( ) , None , 32 ) ) ;
return true ;
}
2002-06-10 22:06:45 +00:00
void
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : insertReply ( CReply * reply )
2002-05-27 16:22:59 +00:00
{
assert ( reply ! = NULL ) ;
// note -- we must respond to requests in order if requestor,target,time
// are the same, otherwise we can use whatever order we like with one
// exception: each reply in a MULTIPLE reply must be handled in order
// as well. those replies will almost certainly not share targets so
// we can't simply use requestor,target,time as map index.
//
// instead we'll use just the requestor. that's more restrictive than
// necessary but we're guaranteed to do things in the right order.
// note that we could also include the time in the map index and still
// ensure the right order. but since that'll just make it harder to
// find the right reply when handling property notify events we stick
// to just the requestor.
const bool newWindow = ( m_replies . count ( reply - > m_requestor ) = = 0 ) ;
m_replies [ reply - > m_requestor ] . push_back ( reply ) ;
// adjust requestor's event mask if we haven't done so already. we
// want events in case the window is destroyed or any of its
// properties change.
if ( newWindow ) {
2002-05-27 18:30:13 +00:00
// note errors while we adjust event masks
bool error = false ;
2002-06-22 19:47:27 +00:00
CXWindowsUtil : : CErrorLock lock ( m_display , & error ) ;
2002-05-27 18:30:13 +00:00
// get and save the current event mask
2002-05-27 16:22:59 +00:00
XWindowAttributes attr ;
XGetWindowAttributes ( m_display , reply - > m_requestor , & attr ) ;
2002-05-27 18:30:13 +00:00
m_eventMasks [ reply - > m_requestor ] = attr . your_event_mask ;
// add the events we want
2002-05-27 16:22:59 +00:00
XSelectInput ( m_display , reply - > m_requestor , attr . your_event_mask |
StructureNotifyMask | PropertyChangeMask ) ;
2002-05-27 18:30:13 +00:00
// if we failed then the window has already been destroyed
if ( error ) {
m_replies . erase ( reply - > m_requestor ) ;
delete reply ;
}
2002-05-27 16:22:59 +00:00
}
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : pushReplies ( )
2002-05-27 16:22:59 +00:00
{
// send the first reply for each window if that reply hasn't
// been sent yet.
for ( CReplyMap : : iterator index = m_replies . begin ( ) ;
index ! = m_replies . end ( ) ; + + index ) {
assert ( ! index - > second . empty ( ) ) ;
if ( ! index - > second . front ( ) - > m_replied ) {
pushReplies ( index , index - > second , index - > second . begin ( ) ) ;
}
}
2001-11-13 23:34:12 +00:00
}
2002-06-10 22:06:45 +00:00
void
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : pushReplies ( CReplyMap : : iterator mapIndex ,
CReplyList & replies , CReplyList : : iterator index )
2001-11-13 23:34:12 +00:00
{
2002-05-27 16:22:59 +00:00
CReply * reply = * index ;
while ( sendReply ( reply ) ) {
// reply is complete. discard it and send the next reply,
// if any.
index = replies . erase ( index ) ;
delete reply ;
if ( index = = replies . end ( ) ) {
break ;
}
reply = * index ;
}
// if there are no more replies in the list then remove the list
// and stop watching the requestor for events.
if ( replies . empty ( ) ) {
2002-06-22 19:47:27 +00:00
CXWindowsUtil : : CErrorLock lock ( m_display ) ;
2002-05-27 16:22:59 +00:00
Window requestor = mapIndex - > first ;
XSelectInput ( m_display , requestor , m_eventMasks [ requestor ] ) ;
m_replies . erase ( mapIndex ) ;
m_eventMasks . erase ( requestor ) ;
}
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : sendReply ( CReply * reply )
2002-05-27 16:22:59 +00:00
{
assert ( reply ! = NULL ) ;
// bail out immediately if reply is done
if ( reply - > m_done ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " clipboard: finished reply to 0x%08x,%d,%d " , reply - > m_requestor , reply - > m_target , reply - > m_property ) ) ;
2002-05-27 16:22:59 +00:00
return true ;
}
// start in failed state if property is None
bool failed = ( reply - > m_property = = None ) ;
if ( ! failed ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " clipboard: setting property on 0x%08x,%d,%d " , reply - > m_requestor , reply - > m_target , reply - > m_property ) ) ;
2002-05-27 16:22:59 +00:00
// send using INCR if already sending incrementally or if reply
// is too large, otherwise just send it.
2002-07-25 09:23:24 +00:00
const UInt32 maxRequestSize = 3 * XMaxRequestSize ( m_display ) ;
2002-05-27 16:22:59 +00:00
const bool useINCR = ( reply - > m_data . size ( ) > maxRequestSize ) ;
// send INCR reply if incremental and we haven't replied yet
if ( useINCR & & ! reply - > m_replied ) {
UInt32 size = reply - > m_data . size ( ) ;
if ( ! CXWindowsUtil : : setWindowProperty ( m_display ,
reply - > m_requestor , reply - > m_property ,
& size , 4 , m_atomINCR , 32 ) ) {
failed = true ;
}
}
// send more INCR reply or entire non-incremental reply
else {
// how much more data should we send?
UInt32 size = reply - > m_data . size ( ) - reply - > m_ptr ;
if ( size > maxRequestSize )
size = maxRequestSize ;
// send it
if ( ! CXWindowsUtil : : setWindowProperty ( m_display ,
reply - > m_requestor , reply - > m_property ,
reply - > m_data . data ( ) + reply - > m_ptr ,
size ,
reply - > m_type , reply - > m_format ) ) {
failed = true ;
}
else {
reply - > m_ptr + = size ;
// we've finished the reply if we just sent the zero
// size incremental chunk or if we're not incremental.
reply - > m_done = ( size = = 0 | | ! useINCR ) ;
}
}
}
// if we've failed then delete the property and say we're done.
// if we haven't replied yet then we can send a failure notify,
// otherwise we've failed in the middle of an incremental
// transfer; i don't know how to cancel that so i'll just send
// the final zero-length property.
// FIXME -- how do you gracefully cancel an incremental transfer?
if ( failed ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " clipboard: sending failure to 0x%08x,%d,%d " , reply - > m_requestor , reply - > m_target , reply - > m_property ) ) ;
2002-05-27 16:22:59 +00:00
reply - > m_done = true ;
if ( reply - > m_property ! = None ) {
2002-06-22 19:47:27 +00:00
CXWindowsUtil : : CErrorLock lock ( m_display ) ;
2002-05-27 16:22:59 +00:00
XDeleteProperty ( m_display , reply - > m_requestor , reply - > m_property ) ;
}
if ( ! reply - > m_replied ) {
sendNotify ( reply - > m_requestor , m_selection ,
reply - > m_target , None ,
reply - > m_time ) ;
// don't wait for any reply (because we're not expecting one)
return true ;
}
else {
static const char dummy = 0 ;
CXWindowsUtil : : setWindowProperty ( m_display ,
reply - > m_requestor , reply - > m_property ,
& dummy ,
0 ,
reply - > m_type , reply - > m_format ) ;
// wait for delete notify
return false ;
}
}
// send notification if we haven't yet
if ( ! reply - > m_replied ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " clipboard: sending notify to 0x%08x,%d,%d " , reply - > m_requestor , reply - > m_target , reply - > m_property ) ) ;
2002-05-27 16:22:59 +00:00
reply - > m_replied = true ;
2003-09-02 21:41:00 +00:00
// dump every property on the requestor window to the debug2
// log. we've seen what appears to be a bug in lesstif and
// knowing the properties may help design a workaround, if
// it becomes necessary.
if ( CLOG - > getFilter ( ) > = CLog : : kDEBUG2 ) {
CXWindowsUtil : : CErrorLock lock ( m_display ) ;
int n ;
Atom * props = XListProperties ( m_display , reply - > m_requestor , & n ) ;
LOG ( ( CLOG_DEBUG2 " properties of 0x%08x: " , reply - > m_requestor ) ) ;
for ( int i = 0 ; i < n ; + + i ) {
Atom target ;
CString data ;
char * name = XGetAtomName ( m_display , props [ i ] ) ;
if ( ! CXWindowsUtil : : getWindowProperty ( m_display ,
2002-05-27 16:22:59 +00:00
reply - > m_requestor ,
2003-09-02 21:41:00 +00:00
props [ i ] , & data , & target , NULL , False ) ) {
LOG ( ( CLOG_DEBUG2 " %s: <can't read property> " , name ) ) ;
}
else {
// if there are any non-ascii characters in string
// then print the binary data.
static const char * hex = " 0123456789abcdef " ;
for ( CString : : size_type j = 0 ; j < data . size ( ) ; + + j ) {
if ( data [ j ] < 32 | | data [ j ] > 126 ) {
CString tmp ;
tmp . reserve ( data . size ( ) * 3 ) ;
for ( j = 0 ; j < data . size ( ) ; + + j ) {
unsigned char v = ( unsigned char ) data [ j ] ;
tmp + = hex [ v > > 16 ] ;
tmp + = hex [ v & 15 ] ;
tmp + = ' ' ;
}
data = tmp ;
break ;
}
}
char * type = XGetAtomName ( m_display , target ) ;
LOG ( ( CLOG_DEBUG2 " %s (%s): %s " , name , type , data . c_str ( ) ) ) ;
if ( type ! = NULL ) {
XFree ( type ) ;
}
}
if ( name ! = NULL ) {
XFree ( name ) ;
}
}
if ( props ! = NULL ) {
XFree ( props ) ;
}
}
sendNotify ( reply - > m_requestor , m_selection ,
2002-05-27 16:22:59 +00:00
reply - > m_target , reply - > m_property ,
reply - > m_time ) ;
}
// wait for delete notify
2001-11-13 23:34:12 +00:00
return false ;
}
2002-06-10 22:06:45 +00:00
void
CXWindowsClipboard : : clearReplies ( )
2002-05-27 16:22:59 +00:00
{
for ( CReplyMap : : iterator index = m_replies . begin ( ) ;
index ! = m_replies . end ( ) ; + + index ) {
clearReplies ( index - > second ) ;
}
m_replies . clear ( ) ;
m_eventMasks . clear ( ) ;
}
2002-06-10 22:06:45 +00:00
void
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : clearReplies ( CReplyList & replies )
2002-05-27 16:22:59 +00:00
{
for ( CReplyList : : iterator index = replies . begin ( ) ;
index ! = replies . end ( ) ; + + index ) {
delete * index ;
}
replies . clear ( ) ;
}
2002-06-10 22:06:45 +00:00
void
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : sendNotify ( Window requestor ,
Atom selection , Atom target , Atom property , Time time )
2002-05-27 16:22:59 +00:00
{
XEvent event ;
event . xselection . type = SelectionNotify ;
event . xselection . display = m_display ;
event . xselection . requestor = requestor ;
event . xselection . selection = selection ;
event . xselection . target = target ;
event . xselection . property = property ;
event . xselection . time = time ;
2002-06-22 19:47:27 +00:00
CXWindowsUtil : : CErrorLock lock ( m_display ) ;
2002-05-27 16:22:59 +00:00
XSendEvent ( m_display , requestor , False , 0 , & event ) ;
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : wasOwnedAtTime ( : : Time time ) const
2002-05-27 16:22:59 +00:00
{
// not owned if we've never owned the selection
2002-06-20 13:35:28 +00:00
checkCache ( ) ;
2002-06-10 22:06:45 +00:00
if ( m_timeOwned = = 0 ) {
2002-05-27 16:22:59 +00:00
return false ;
2002-06-10 22:06:45 +00:00
}
2002-05-27 16:22:59 +00:00
// if time is CurrentTime then return true if we still own the
// selection and false if we do not. else if we still own the
// selection then get the current time, otherwise use
// m_timeLost as the end time.
Time lost = m_timeLost ;
2002-06-10 22:06:45 +00:00
if ( m_timeLost = = 0 ) {
if ( time = = CurrentTime ) {
2002-05-27 16:22:59 +00:00
return true ;
2002-06-10 22:06:45 +00:00
}
else {
2002-05-27 16:22:59 +00:00
lost = CXWindowsUtil : : getCurrentTime ( m_display , m_window ) ;
2002-06-10 22:06:45 +00:00
}
}
else {
if ( time = = CurrentTime ) {
2002-05-27 16:22:59 +00:00
return false ;
2002-06-10 22:06:45 +00:00
}
}
2002-05-27 16:22:59 +00:00
// compare time to range
Time duration = lost - m_timeOwned ;
Time when = time - m_timeOwned ;
return ( /*when >= 0 &&*/ when < duration ) ;
}
2002-06-10 22:06:45 +00:00
Atom
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : getTargetsData ( CString & data , int * format ) const
2002-05-27 16:22:59 +00:00
{
assert ( format ! = NULL ) ;
2002-07-22 17:32:51 +00:00
// add standard targets
2002-05-27 16:22:59 +00:00
Atom atom ;
atom = m_atomTargets ;
data . append ( reinterpret_cast < char * > ( & atom ) , sizeof ( Atom ) ) ;
atom = m_atomMultiple ;
data . append ( reinterpret_cast < char * > ( & atom ) , sizeof ( Atom ) ) ;
atom = m_atomTimestamp ;
data . append ( reinterpret_cast < char * > ( & atom ) , sizeof ( Atom ) ) ;
2002-07-22 17:32:51 +00:00
// add targets we can convert to
for ( ConverterList : : const_iterator index = m_converters . begin ( ) ;
index ! = m_converters . end ( ) ; + + index ) {
IXWindowsClipboardConverter * converter = * index ;
// skip formats we don't have
2002-07-22 18:17:21 +00:00
if ( m_added [ converter - > getFormat ( ) ] ) {
2002-07-22 17:32:51 +00:00
atom = converter - > getAtom ( ) ;
data . append ( reinterpret_cast < char * > ( & atom ) , sizeof ( Atom ) ) ;
}
2002-05-27 16:22:59 +00:00
}
* format = 32 ;
2002-07-24 17:30:32 +00:00
return m_atomAtom ;
2002-05-27 16:22:59 +00:00
}
2002-06-10 22:06:45 +00:00
Atom
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : getTimestampData ( CString & data , int * format ) const
2002-05-27 16:22:59 +00:00
{
assert ( format ! = NULL ) ;
assert ( sizeof ( m_timeOwned ) = = 4 ) ;
2002-06-20 13:35:28 +00:00
checkCache ( ) ;
2002-05-27 16:22:59 +00:00
data . append ( reinterpret_cast < const char * > ( & m_timeOwned ) , 4 ) ;
* format = 32 ;
2002-07-24 17:07:52 +00:00
return m_atomInteger ;
2002-05-27 16:22:59 +00:00
}
//
// CXWindowsClipboard::CICCCMGetClipboard
//
CXWindowsClipboard : : CICCCMGetClipboard : : CICCCMGetClipboard (
2002-06-17 13:31:21 +00:00
Window requestor , Time time , Atom property ) :
2002-06-10 22:06:45 +00:00
m_requestor ( requestor ) ,
m_time ( time ) ,
m_property ( property ) ,
m_incr ( false ) ,
m_failed ( false ) ,
m_done ( false ) ,
m_reading ( false ) ,
m_data ( NULL ) ,
m_actualTarget ( NULL ) ,
m_error ( false )
2002-05-27 16:22:59 +00:00
{
// do nothing
}
CXWindowsClipboard : : CICCCMGetClipboard : : ~ CICCCMGetClipboard ( )
{
// do nothing
}
2002-06-10 22:06:45 +00:00
bool
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : CICCCMGetClipboard : : readClipboard ( Display * display ,
Atom selection , Atom target , Atom * actualTarget , CString * data )
2002-05-27 16:22:59 +00:00
{
assert ( actualTarget ! = NULL ) ;
assert ( data ! = NULL ) ;
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " request selection=%s, target=%s, window=%x " , CXWindowsUtil : : atomToString ( display , selection ) . c_str ( ) , CXWindowsUtil : : atomToString ( display , target ) . c_str ( ) , m_requestor ) ) ;
2002-05-27 16:22:59 +00:00
// save output pointers
m_actualTarget = actualTarget ;
m_data = data ;
// assume failure
* m_actualTarget = None ;
* m_data = " " ;
// delete target property
XDeleteProperty ( display , m_requestor , m_property ) ;
// select window for property changes
XWindowAttributes attr ;
XGetWindowAttributes ( display , m_requestor , & attr ) ;
XSelectInput ( display , m_requestor ,
attr . your_event_mask | PropertyChangeMask ) ;
// request data conversion
XConvertSelection ( display , selection , target ,
m_property , m_requestor , m_time ) ;
2002-06-20 14:01:44 +00:00
// synchronize with server before we start following timeout countdown
XSync ( display , False ) ;
2002-06-20 13:35:28 +00:00
// Xlib inexplicably omits the ability to wait for an event with
// a timeout. (it's inexplicable because there's no portable way
// to do it.) we'll poll until we have what we're looking for or
// a timeout expires. we use a timeout so we don't get locked up
// by badly behaved selection owners.
2002-05-27 16:22:59 +00:00
XEvent xevent ;
2002-06-20 16:27:49 +00:00
std : : vector < XEvent > events ;
2002-06-20 13:35:28 +00:00
CStopwatch timeout ( true ) ;
2002-06-20 14:01:44 +00:00
static const double s_timeout = 0.25 ; // FIXME -- is this too short?
2002-07-25 08:57:46 +00:00
bool noWait = false ;
2002-05-27 16:22:59 +00:00
while ( ! m_done & & ! m_failed ) {
2002-06-20 13:35:28 +00:00
// fail if timeout has expired
2002-06-20 14:01:44 +00:00
if ( timeout . getTime ( ) > = s_timeout ) {
2002-06-20 13:35:28 +00:00
m_failed = true ;
break ;
}
2002-06-20 16:27:49 +00:00
// process events if any otherwise sleep
2002-07-25 08:57:46 +00:00
if ( noWait | | XPending ( display ) > 0 ) {
while ( ! m_done & & ! m_failed & & ( noWait | | XPending ( display ) > 0 ) ) {
2002-06-20 16:27:49 +00:00
XNextEvent ( display , & xevent ) ;
if ( ! processEvent ( display , & xevent ) ) {
// not processed so save it
events . push_back ( xevent ) ;
}
2002-07-25 08:57:46 +00:00
else {
// reset timer since we've made some progress
timeout . reset ( ) ;
// don't sleep anymore, just block waiting for events.
// we're assuming here that the clipboard owner will
// complete the protocol correctly. if we continue to
// sleep we'll get very bad performance.
noWait = true ;
}
2002-06-20 14:01:44 +00:00
}
2002-06-20 13:35:28 +00:00
}
else {
2003-01-04 22:01:32 +00:00
ARCH - > sleep ( 0.01 ) ;
2002-06-20 13:35:28 +00:00
}
2002-05-27 16:22:59 +00:00
}
2002-06-20 16:27:49 +00:00
// put unprocessed events back
for ( UInt32 i = events . size ( ) ; i > 0 ; - - i ) {
XPutBackEvent ( display , & events [ i - 1 ] ) ;
}
2002-05-27 16:22:59 +00:00
// restore mask
XSelectInput ( display , m_requestor , attr . your_event_mask ) ;
// return success or failure
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " request %s " , m_failed ? " failed " : " succeeded " ) ) ;
2002-05-27 16:22:59 +00:00
return ! m_failed ;
}
2002-06-10 22:06:45 +00:00
bool
2002-06-20 16:27:49 +00:00
CXWindowsClipboard : : CICCCMGetClipboard : : processEvent (
2002-06-17 13:31:21 +00:00
Display * display , XEvent * xevent )
2002-05-27 16:22:59 +00:00
{
// process event
switch ( xevent - > type ) {
case DestroyNotify :
if ( xevent - > xdestroywindow . window = = m_requestor ) {
m_failed = true ;
return true ;
}
// not interested
return false ;
case SelectionNotify :
if ( xevent - > xselection . requestor = = m_requestor ) {
// done if we can't convert
if ( xevent - > xselection . property = = None ) {
m_done = true ;
return true ;
}
// proceed if conversion successful
else if ( xevent - > xselection . property = = m_property ) {
m_reading = true ;
break ;
}
}
// otherwise not interested
return false ;
case PropertyNotify :
// proceed if conversion successful and we're receiving more data
if ( xevent - > xproperty . window = = m_requestor & &
xevent - > xproperty . atom = = m_property & &
xevent - > xproperty . state = = PropertyNewValue ) {
if ( ! m_reading ) {
2002-06-20 16:27:49 +00:00
// we haven't gotten the SelectionNotify yet
return true ;
2002-05-27 16:22:59 +00:00
}
break ;
}
// otherwise not interested
return false ;
default :
// not interested
return false ;
}
// get the data from the property
Atom target ;
const CString : : size_type oldSize = m_data - > size ( ) ;
if ( ! CXWindowsUtil : : getWindowProperty ( display , m_requestor ,
m_property , m_data , & target , NULL , True ) ) {
// unable to read property
m_failed = true ;
2002-06-02 21:03:38 +00:00
return true ;
2002-05-27 16:22:59 +00:00
}
// note if incremental. if we're already incremental then the
// selection owner is busted. if the INCR property has no size
// then the selection owner is busted.
if ( target = = XInternAtom ( display , " INCR " , False ) ) {
if ( m_incr ) {
m_failed = true ;
m_error = true ;
}
else if ( m_data - > size ( ) = = oldSize ) {
m_failed = true ;
m_error = true ;
}
else {
m_incr = true ;
// discard INCR data
* m_data = " " ;
}
}
// handle incremental chunks
else if ( m_incr ) {
// if first incremental chunk then save target
if ( oldSize = = 0 ) {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " INCR first chunk, target %s " , CXWindowsUtil : : atomToString ( display , target ) . c_str ( ) ) ) ;
2002-05-27 16:22:59 +00:00
* m_actualTarget = target ;
}
// secondary chunks must have the same target
else {
if ( target ! = * m_actualTarget ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_WARN " INCR target mismatch " ) ) ;
2002-05-27 16:22:59 +00:00
m_failed = true ;
m_error = true ;
}
}
// note if this is the final chunk
if ( m_data - > size ( ) = = oldSize ) {
2002-10-15 21:29:44 +00:00
LOG ( ( CLOG_DEBUG1 " INCR final chunk: %d bytes total " , m_data - > size ( ) ) ) ;
2002-05-27 16:22:59 +00:00
m_done = true ;
}
}
// not incremental; save the target.
else {
2004-05-01 16:10:09 +00:00
LOG ( ( CLOG_DEBUG1 " target %s " , CXWindowsUtil : : atomToString ( display , target ) . c_str ( ) ) ) ;
2002-05-27 16:22:59 +00:00
* m_actualTarget = target ;
m_done = true ;
}
2002-06-20 16:27:49 +00:00
// this event has been processed
2002-10-15 21:29:44 +00:00
LOGC ( ! m_incr , ( CLOG_DEBUG1 " got data, %d bytes " , m_data - > size ( ) ) ) ;
2002-06-20 16:27:49 +00:00
return true ;
2002-05-27 16:22:59 +00:00
}
//
// CXWindowsClipboard::CReply
//
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : CReply : : CReply ( Window requestor , Atom target , : : Time time ) :
2002-06-10 22:06:45 +00:00
m_requestor ( requestor ) ,
m_target ( target ) ,
m_time ( time ) ,
m_property ( None ) ,
m_replied ( false ) ,
m_done ( false ) ,
m_data ( ) ,
m_type ( None ) ,
m_format ( 32 ) ,
m_ptr ( 0 )
2002-05-27 16:22:59 +00:00
{
// do nothing
}
2002-06-17 13:31:21 +00:00
CXWindowsClipboard : : CReply : : CReply ( Window requestor , Atom target , : : Time time ,
Atom property , const CString & data , Atom type , int format ) :
2002-06-10 22:06:45 +00:00
m_requestor ( requestor ) ,
m_target ( target ) ,
m_time ( time ) ,
m_property ( property ) ,
m_replied ( false ) ,
m_done ( false ) ,
m_data ( data ) ,
m_type ( type ) ,
m_format ( format ) ,
m_ptr ( 0 )
2001-11-13 23:34:12 +00:00
{
2002-05-27 16:22:59 +00:00
// do nothing
2001-11-13 23:34:12 +00:00
}