Home | History | Annotate | Download | only in x
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/base/x/selection_owner.h"
      6 
      7 #include <X11/Xlib.h>
      8 #include <X11/Xatom.h>
      9 
     10 #include "base/logging.h"
     11 #include "ui/base/x/selection_utils.h"
     12 
     13 namespace ui {
     14 
     15 namespace {
     16 
     17 const char kMultiple[] = "MULTIPLE";
     18 const char kTargets[] = "TARGETS";
     19 
     20 const char* kAtomsToCache[] = {
     21   kMultiple,
     22   kTargets,
     23   NULL
     24 };
     25 
     26 }  // namespace
     27 
     28 SelectionOwner::SelectionOwner(Display* x_display,
     29                                Window x_window,
     30                                Atom selection_name)
     31     : x_display_(x_display),
     32       x_window_(x_window),
     33       selection_name_(selection_name),
     34       atom_cache_(x_display_, kAtomsToCache) {
     35 }
     36 
     37 SelectionOwner::~SelectionOwner() {
     38   Clear();
     39 }
     40 
     41 void SelectionOwner::RetrieveTargets(std::vector<Atom>* targets) {
     42   targets->clear();
     43   for (SelectionFormatMap::const_iterator it = format_map_.begin();
     44        it != format_map_.end(); ++it) {
     45     targets->push_back(it->first);
     46   }
     47 }
     48 
     49 void SelectionOwner::TakeOwnershipOfSelection(
     50     const SelectionFormatMap& data) {
     51   XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime);
     52 
     53   if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) {
     54     // The X server agrees that we are the selection owner. Commit our data.
     55     format_map_ = data;
     56   }
     57 }
     58 
     59 void SelectionOwner::Clear() {
     60   if (XGetSelectionOwner(x_display_, selection_name_) == x_window_)
     61     XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
     62 
     63   format_map_ = SelectionFormatMap();
     64 }
     65 
     66 void SelectionOwner::OnSelectionRequest(const XSelectionRequestEvent& event) {
     67   // Incrementally build our selection. By default this is a refusal, and we'll
     68   // override the parts indicating success in the different cases.
     69   XEvent reply;
     70   reply.xselection.type = SelectionNotify;
     71   reply.xselection.requestor = event.requestor;
     72   reply.xselection.selection = event.selection;
     73   reply.xselection.target = event.target;
     74   reply.xselection.property = None;  // Indicates failure
     75   reply.xselection.time = event.time;
     76 
     77   // Get the proper selection.
     78   Atom targets_atom = atom_cache_.GetAtom(kTargets);
     79   if (event.target == targets_atom) {
     80     // We have been asked for TARGETS. Send an atom array back with the data
     81     // types we support.
     82     std::vector<Atom> targets;
     83     targets.push_back(targets_atom);
     84     RetrieveTargets(&targets);
     85 
     86     XChangeProperty(x_display_, event.requestor, event.property, XA_ATOM, 32,
     87                     PropModeReplace,
     88                     reinterpret_cast<unsigned char*>(&targets.front()),
     89                     targets.size());
     90     reply.xselection.property = event.property;
     91   } else if (event.target == atom_cache_.GetAtom(kMultiple)) {
     92     // TODO(erg): Theoretically, the spec claims I'm supposed to handle the
     93     // MULTIPLE case, but I haven't seen it in the wild yet.
     94     NOTIMPLEMENTED();
     95   } else {
     96     // Try to find the data type in map.
     97     SelectionFormatMap::const_iterator it =
     98         format_map_.find(event.target);
     99     if (it != format_map_.end()) {
    100       XChangeProperty(x_display_, event.requestor, event.property,
    101                       event.target, 8,
    102                       PropModeReplace,
    103                       const_cast<unsigned char*>(
    104                           reinterpret_cast<const unsigned char*>(
    105                               it->second->front())),
    106                       it->second->size());
    107       reply.xselection.property = event.property;
    108     }
    109     // I would put error logging here, but GTK ignores TARGETS and spams us
    110     // looking for its own internal types.
    111   }
    112 
    113   // Send off the reply.
    114   XSendEvent(x_display_, event.requestor, False, 0, &reply);
    115 }
    116 
    117 void SelectionOwner::OnSelectionClear(const XSelectionClearEvent& event) {
    118   DLOG(ERROR) << "SelectionClear";
    119 
    120   // TODO(erg): If we receive a SelectionClear event while we're handling data,
    121   // we need to delay clearing.
    122 }
    123 
    124 }  // namespace ui
    125 
    126