clipboard.cpp

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

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs