clipboard.cpp

Go to the documentation of this file.
00001 /* $Id: clipboard.cpp 52840 2012-01-31 20:38:59Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /** @file */
00017 
00018 #include "global.hpp"
00019 
00020 #include "clipboard.hpp"
00021 #include <algorithm>
00022 
00023 #if defined(_X11) && !defined(__APPLE__)
00024 
00025 #define CLIPBOARD_FUNCS_DEFINED
00026 
00027 
00028 #include "SDL_syswm.h"
00029 
00030 #include <unistd.h>
00031 
00032 /**
00033  The following are two classes which wrap the SDL's interface to X,
00034  including locking/unlocking, and which manage the atom internment.
00035  They exist mainly to make the actual clipboard code somewhat readable.
00036 */
00037 class XHelper
00038 {
00039 private:
00040     XHelper() :
00041         wmInf_(),
00042         acquireCount_(0)
00043     {
00044         acquire();
00045 
00046         // Intern some atoms;
00047         const char* atoms[] = {
00048             "CLIPBOARD",
00049             "TEXT",
00050             "COMPOUND_TEXT",
00051             "UTF8_STRING",
00052             "WESNOTH_PASTE",
00053             "TARGETS"
00054         };
00055 
00056         XInternAtoms(dpy(), const_cast<char**>(reinterpret_cast<const char**>(atoms)), 6, false, atomTable_);
00057 
00058         release();
00059     }
00060 
00061     static XHelper* s_instance_;
00062 
00063     SDL_SysWMinfo wmInf_;
00064 
00065     Atom          atomTable_[6];
00066     int           acquireCount_;
00067 public:
00068     static XHelper* instance()
00069     {
00070         if (!s_instance_)
00071             s_instance_ = new XHelper;
00072         return s_instance_;
00073     }
00074 
00075 
00076     Atom XA_CLIPBOARD() const
00077     {
00078         return atomTable_[0];
00079     }
00080 
00081     Atom XA_TEXT() const
00082     {
00083         return atomTable_[1];
00084     }
00085 
00086     Atom XA_COMPOUND_TEXT() const
00087     {
00088         return atomTable_[2];
00089     }
00090 
00091     Atom UTF8_STRING() const
00092     {
00093         return atomTable_[3];
00094     }
00095 
00096     Atom WES_PASTE() const
00097     {
00098         return atomTable_[4];
00099     }
00100 
00101     Atom XA_TARGETS() const
00102     {
00103         return atomTable_[5];
00104     }
00105 
00106     Display* dpy() const
00107     {
00108         return wmInf_.info.x11.display;
00109     }
00110 
00111     Window window() const
00112     {
00113         return wmInf_.info.x11.window;
00114     }
00115 
00116     void acquire()
00117     {
00118         ++acquireCount_;
00119         if (acquireCount_ == 1) {
00120             SDL_VERSION  (&wmInf_.version);
00121             SDL_GetWMInfo(&wmInf_);
00122 
00123             wmInf_.info.x11.lock_func();
00124         }
00125     }
00126 
00127     void release()
00128     {
00129         --acquireCount_;
00130         if (acquireCount_ == 0)
00131             wmInf_.info.x11.unlock_func();
00132     }
00133 };
00134 
00135 XHelper* XHelper::s_instance_ = 0;
00136 
00137 class UseX
00138 {
00139 public:
00140     UseX()
00141     {
00142         XHelper::instance()->acquire();
00143     }
00144 
00145     ~UseX()
00146     {
00147         XHelper::instance()->release();
00148     }
00149 
00150     XHelper* operator->()
00151     {
00152         return XHelper::instance();
00153     }
00154 };
00155 
00156 /**
00157  Note: unfortunately, SDL does not keep track of event timestamps.
00158  This means we are forced to use CurrentTime in many spots and
00159  are unable to perform many safety checks.
00160  Hence, the code below is not compliant to the ICCCM, and
00161  may ocassionally suffer from race conditions if an X client
00162  is connected to the server over a slow/high-latency link.
00163  This implementation is also very minimal.
00164  The text is assumed to be reasonably small, as INCR transactions
00165  are not supported.
00166  MULTIPLE is not supported either.
00167 
00168  We provide UTF8_STRING, COMPOUND_TEXT, and TEXT,
00169  and try to grab all of them, plus STRING (which is latin1).
00170 */
00171 
00172 
00173 /**
00174  We primarily. keep a copy of the string to response to data requests,
00175  but it also has an another function: in case we're both the source
00176  and destination, we just copy it across; this is so that we don't
00177  have to handle SelectionRequest events while waiting for SelectionNotify.
00178  To make this work, however, this gets cleared when we loose CLIPBOARD.
00179 */
00180 static std::string clipboard_string;
00181 
00182 /**
00183  The following string is used for the mouse selection aka PRIMARY
00184  Unix behaviour is mouse selection is stored in primary
00185  active selection goes to CLIPBOARD.
00186 */
00187 static std::string primary_string;
00188 
00189 void handle_system_event(const SDL_Event& event)
00190 {
00191     XEvent& xev = event.syswm.msg->event.xevent;
00192     if (xev.type == SelectionRequest) {
00193         UseX x11;
00194 
00195         // Since wesnoth does not notify us of selections,
00196         // we set both selection + clipboard when copying.
00197         if ((xev.xselectionrequest.owner     == x11->window()) &&
00198             ((xev.xselectionrequest.selection == XA_PRIMARY) ||
00199              (xev.xselectionrequest.selection == x11->XA_CLIPBOARD()))) {
00200             XEvent responseEvent;
00201             responseEvent.xselection.type      = SelectionNotify;
00202             responseEvent.xselection.display   = x11->dpy();
00203             responseEvent.xselection.requestor = xev.xselectionrequest.requestor;
00204             responseEvent.xselection.selection = xev.xselectionrequest.selection;
00205             responseEvent.xselection.target    = xev.xselectionrequest.target;
00206             responseEvent.xselection.property  = None; //nothing available, by default
00207             responseEvent.xselection.time      = xev.xselectionrequest.time;
00208 
00209             //std::cout<<"Request for target:"<<XGetAtomName(x11->dpy(), xev.xselectionrequest.target)<<"\n";
00210 
00211             //### presently don't handle XA_STRING as it must be latin1
00212 
00213             if (xev.xselectionrequest.target == x11->XA_TARGETS()) {
00214                 responseEvent.xselection.property = xev.xselectionrequest.property;
00215 
00216                 Atom supported[] = {
00217                     x11->XA_TEXT(),
00218                     x11->XA_COMPOUND_TEXT(),
00219                     x11->UTF8_STRING(),
00220                     x11->XA_TARGETS()
00221                 };
00222 
00223                 XChangeProperty(x11->dpy(), responseEvent.xselection.requestor,
00224                     xev.xselectionrequest.property, XA_ATOM, 32, PropModeReplace,
00225                     const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(supported)), 4);
00226             }
00227 
00228             // The encoding of XA_TEXT and XA_COMPOUND_TEXT is not specified
00229             // by the ICCCM... So we assume wesnoth native/utf-8 for simplicity.
00230             // Modern apps are going to use UTF8_STRING anyway.
00231             if (xev.xselectionrequest.target == x11->XA_TEXT()
00232                     || xev.xselectionrequest.target == x11->XA_COMPOUND_TEXT()
00233                     || xev.xselectionrequest.target == x11->UTF8_STRING()) {
00234 
00235                 responseEvent.xselection.property = xev.xselectionrequest.property;
00236 
00237                 std::string& selection = (xev.xselectionrequest.selection == XA_PRIMARY) ?
00238                     primary_string : clipboard_string;
00239 
00240                 XChangeProperty(x11->dpy(), responseEvent.xselection.requestor,
00241                     xev.xselectionrequest.property,
00242                     xev.xselectionrequest.target, 8, PropModeReplace,
00243                     reinterpret_cast<const unsigned char*>(selection.c_str()), selection.length());
00244             }
00245 
00246             XSendEvent(x11->dpy(), xev.xselectionrequest.requestor, False, NoEventMask,
00247                &responseEvent);
00248         }
00249     }
00250 
00251     if (xev.type == SelectionClear) {
00252         //We no longer own the clipboard, don't try in-process C&P
00253         UseX x11;
00254 
00255         if(xev.xselectionclear.selection == x11->XA_CLIPBOARD()) {
00256             clipboard_string.clear();
00257         } else if(xev.xselectionclear.selection == XA_PRIMARY) {
00258             primary_string.clear();
00259         }
00260     }
00261 }
00262 
00263 void copy_to_clipboard(const std::string& text, const bool mouse)
00264 {
00265     if (text.empty()) {
00266         return;
00267     }
00268 
00269     UseX x11;
00270 
00271     if(mouse) {
00272         primary_string = text;
00273         XSetSelectionOwner(x11->dpy(), XA_PRIMARY, x11->window(), CurrentTime);
00274     } else {
00275         clipboard_string = text;
00276         XSetSelectionOwner(x11->dpy(), x11->XA_CLIPBOARD(), x11->window(), CurrentTime);
00277     }
00278 }
00279 
00280 /**
00281  * Tries to grab a given target.
00282  * Returns true if successful, false otherwise.
00283  */
00284 static bool try_grab_target(Atom source, Atom target, std::string& ret)
00285 {
00286     UseX x11;
00287 
00288     // Cleanup previous data
00289     XDeleteProperty(x11->dpy(), x11->window(), x11->WES_PASTE());
00290     XSync          (x11->dpy(), False);
00291 
00292     //std::cout<<"We request target:"<<XGetAtomName(x11->dpy(), target)<<"\n";
00293 
00294     // Request information
00295     XConvertSelection(x11->dpy(), source, target,
00296                       x11->WES_PASTE(), x11->window(), CurrentTime);
00297 
00298     // Wait (with timeout) for a response SelectionNotify
00299     for (int attempt = 0; attempt < 15; attempt++) {
00300         if (XPending(x11->dpy())) {
00301             XEvent selectNotify;
00302             while (XCheckTypedWindowEvent(x11->dpy(), x11->window(), SelectionNotify, &selectNotify)) {
00303                 if (selectNotify.xselection.property == None)
00304                     //Not supported. Say so.
00305                     return false;
00306                 else if (selectNotify.xselection.property == x11->WES_PASTE() &&
00307                          selectNotify.xselection.target   == target) {
00308                     // The size
00309                     unsigned long length = 0;
00310                     unsigned char* data;
00311 
00312                     // These 3 XGetWindowProperty returns but we don't use
00313                     Atom         typeRet;
00314                     int          formatRet;
00315                     unsigned long remaining;
00316 
00317 //                  std::cout<<"Grab:"<<XGetAtomName(x11->dpy(), target)<<"\n";
00318 
00319                     // Grab the text out of the property
00320                     XGetWindowProperty(x11->dpy(), x11->window(),
00321                                        selectNotify.xselection.property,
00322                                        0, 65535/4, True, target,
00323                                        &typeRet, &formatRet, &length, &remaining, &data);
00324 
00325                     if (data && length) {
00326                         ret = reinterpret_cast<char*>(data);
00327                         XFree(data);
00328                         return true;
00329                     } else {
00330                         return false;
00331                     }
00332                 }
00333             }
00334         }
00335 
00336         usleep(10000);
00337     }
00338 
00339     // Timed out -- return empty string
00340     return false;
00341 }
00342 
00343 std::string copy_from_clipboard(const bool mouse)
00344 {
00345     // in-wesnoth copy-paste
00346     if(mouse && !primary_string.empty()) {
00347         return primary_string;
00348     }
00349     if (!mouse && !clipboard_string.empty()) {
00350         return clipboard_string;
00351     }
00352 
00353     UseX x11;
00354 
00355     std::string ret;
00356     const Atom& source = mouse ?  XA_PRIMARY : x11->XA_CLIPBOARD();
00357 
00358     if (try_grab_target(source, x11->UTF8_STRING(), ret))
00359         return ret;
00360 
00361     if (try_grab_target(source, x11->XA_COMPOUND_TEXT(), ret))
00362         return ret;
00363 
00364     if (try_grab_target(source, x11->XA_TEXT(), ret))
00365         return ret;
00366 
00367     if (try_grab_target(source, XA_STRING, ret))    // acroread only provides this
00368         return ret;
00369 
00370 
00371     return "";
00372 }
00373 
00374 #endif
00375 #ifdef _WIN32
00376 #include <windows.h>
00377 #define CLIPBOARD_FUNCS_DEFINED
00378 
00379 void handle_system_event(const SDL_Event& )
00380 {}
00381 
00382 void copy_to_clipboard(const std::string& text, const bool)
00383 {
00384     if(text.empty())
00385         return;
00386     if(!OpenClipboard(NULL))
00387         return;
00388     EmptyClipboard();
00389 
00390     // Convert newlines
00391     std::string str;
00392     str.reserve(text.size());
00393     std::string::const_iterator last = text.begin();
00394     while(last != text.end()) {
00395         if(*last != '\n') {
00396             str.push_back(*last);
00397         } else {
00398             str.append("\r\n");
00399         }
00400         ++last;
00401     }
00402 
00403     const HGLOBAL hglb = GlobalAlloc(GMEM_MOVEABLE, (str.size() + 1) * sizeof(TCHAR));
00404     if(hglb == NULL) {
00405         CloseClipboard();
00406         return;
00407     }
00408     char* const buffer = reinterpret_cast<char* const>(GlobalLock(hglb));
00409     strcpy(buffer, str.c_str());
00410     GlobalUnlock(hglb);
00411     SetClipboardData(CF_TEXT, hglb);
00412     CloseClipboard();
00413 }
00414 
00415 std::string copy_from_clipboard(const bool)
00416 {
00417     if(!IsClipboardFormatAvailable(CF_TEXT))
00418         return "";
00419     if(!OpenClipboard(NULL))
00420         return "";
00421 
00422     HGLOBAL hglb = GetClipboardData(CF_TEXT);
00423     if(hglb == NULL) {
00424         CloseClipboard();
00425         return "";
00426     }
00427     char const * buffer = reinterpret_cast<char*>(GlobalLock(hglb));
00428     if(buffer == NULL) {
00429         CloseClipboard();
00430         return "";
00431     }
00432 
00433     // Convert newlines
00434     std::string str(buffer);
00435     str.erase(std::remove(str.begin(),str.end(),'\r'),str.end());
00436 
00437     GlobalUnlock(hglb);
00438     CloseClipboard();
00439     return str;
00440 }
00441 
00442 #endif
00443 
00444 #ifdef __BEOS__
00445 #include <Clipboard.h>
00446 #define CLIPBOARD_FUNCS_DEFINED
00447 
00448 void copy_to_clipboard(const std::string& text, const bool)
00449 {
00450     BMessage *clip;
00451     if (be_clipboard->Lock())
00452     {
00453         be_clipboard->Clear();
00454         if ((clip = be_clipboard->Data()))
00455         {
00456             clip->AddData("text/plain", B_MIME_TYPE, text.c_str(), text.size()+1);
00457             be_clipboard->Commit();
00458         }
00459         be_clipboard->Unlock();
00460     }
00461 }
00462 
00463 std::string copy_from_clipboard(const bool)
00464 {
00465     const char* data;
00466     ssize_t size;
00467     BMessage *clip = NULL;
00468     if (be_clipboard->Lock())
00469     {
00470         clip = be_clipboard->Data();
00471         be_clipboard->Unlock();
00472     }
00473     if (clip != NULL && clip->FindData("text/plain", B_MIME_TYPE, (const void**)&data, &size) == B_OK)
00474         return (const char*)data;
00475     else
00476         return "";
00477 }
00478 #endif
00479 
00480 #ifdef __APPLE__
00481 #define CLIPBOARD_FUNCS_DEFINED
00482 
00483 #include <Carbon/Carbon.h>
00484 
00485 void copy_to_clipboard(const std::string& text, const bool)
00486 {
00487     std::string new_str;
00488     new_str.reserve(text.size());
00489     for (unsigned int i = 0; i < text.size(); ++i)
00490     {
00491         if (text[i] == '\n')
00492         {
00493             new_str.push_back('\r');
00494         } else {
00495             new_str.push_back(text[i]);
00496         }
00497     }
00498     OSStatus err = noErr;
00499     PasteboardRef clipboard;
00500     PasteboardSyncFlags syncFlags;
00501     CFDataRef textData = NULL;
00502     err = PasteboardCreate(kPasteboardClipboard, &clipboard);
00503     if (err != noErr) return;
00504     err = PasteboardClear(clipboard);
00505     if (err != noErr) return;
00506     syncFlags = PasteboardSynchronize(clipboard);
00507     if ((syncFlags&kPasteboardModified) && !(syncFlags&kPasteboardClientIsOwner)) return;
00508     textData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)new_str.c_str(), text.size());
00509     PasteboardPutItemFlavor(clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), textData, 0);
00510 }
00511 
00512 std::string copy_from_clipboard(const bool)
00513 {
00514     OSStatus err = noErr;
00515     PasteboardRef clipboard;
00516     PasteboardSyncFlags syncFlags;
00517     ItemCount count;
00518     err = PasteboardCreate(kPasteboardClipboard, &clipboard);
00519     if (err != noErr) return "";
00520     syncFlags = PasteboardSynchronize(clipboard);
00521     if (syncFlags&kPasteboardModified) return "";
00522     err = PasteboardGetItemCount(clipboard, &count);
00523     if (err != noErr) return "";
00524     for (UInt32 k = 1; k <= count; k++) {
00525         PasteboardItemID itemID;
00526         CFArrayRef flavorTypeArray;
00527         CFIndex flavorCount;
00528         err = PasteboardGetItemIdentifier(clipboard, k, &itemID);
00529         if (err != noErr) return "";
00530         err = PasteboardCopyItemFlavors(clipboard, itemID, &flavorTypeArray);
00531         if (err != noErr) return "";
00532         flavorCount = CFArrayGetCount(flavorTypeArray);
00533         for (CFIndex j = 0; j < flavorCount; j++) {
00534             CFStringRef flavorType;
00535             CFDataRef flavorData;
00536             CFIndex flavorDataSize;
00537             flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, j);
00538             if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text"))) {
00539                 err = PasteboardCopyItemFlavorData(clipboard, itemID, flavorType, &flavorData);
00540                 if (err != noErr) {
00541                     CFRelease(flavorTypeArray);
00542                     return "";
00543                 }
00544                 flavorDataSize = CFDataGetLength(flavorData);
00545                 std::string str;
00546                 str.reserve(flavorDataSize);
00547                 str.resize(flavorDataSize);
00548                 CFDataGetBytes(flavorData, CFRangeMake(0, flavorDataSize), (UInt8 *)str.data());
00549                 for (unsigned int i = 0; i < str.size(); ++i) {
00550                     if (str[i] == '\r') str[i] = '\n';
00551                 }
00552                 return str;
00553             }
00554         }
00555     }
00556     return "";
00557 }
00558 
00559 void handle_system_event(const SDL_Event& /*event*/)
00560 {
00561 }
00562 
00563 #endif
00564 
00565 #ifndef CLIPBOARD_FUNCS_DEFINED
00566 
00567 void copy_to_clipboard(const std::string& /*text*/, const bool)
00568 {
00569 }
00570 
00571 std::string copy_from_clipboard(const bool)
00572 {
00573     return "";
00574 }
00575 
00576 void handle_system_event(const SDL_Event& /*event*/)
00577 {
00578 }
00579 
00580 #endif
00581 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:32 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs