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.
5 #include "ui/base/x/selection_owner.h"
10 #include "base/logging.h"
11 #include "ui/base/x/selection_utils.h"
12 #include "ui/base/x/x11_util.h"
18 const char kAtomPair[] = "ATOM_PAIR";
19 const char kMultiple[] = "MULTIPLE";
20 const char kSaveTargets[] = "SAVE_TARGETS";
21 const char kTargets[] = "TARGETS";
23 const char* kAtomsToCache[] = {
31 // Gets the value of an atom pair array property. On success, true is returned
32 // and the value is stored in |value|.
33 bool GetAtomPairArrayProperty(XID window,
35 std::vector<std::pair<Atom,Atom> >* value) {
37 int format = 0; // size in bits of each item in 'property'
38 unsigned long num_items = 0;
39 unsigned char* properties = NULL;
40 unsigned long remaining_bytes = 0;
42 int result = XGetWindowProperty(gfx::GetXDisplay(),
45 0, // offset into property data to
47 (~0L), // entire array
56 if (result != Success)
59 // GTK does not require |type| to be kAtomPair.
60 if (format != 32 || num_items % 2 != 0) {
65 Atom* atom_properties = reinterpret_cast<Atom*>(properties);
67 for (size_t i = 0; i < num_items; i+=2)
68 value->push_back(std::make_pair(atom_properties[i], atom_properties[i+1]));
75 SelectionOwner::SelectionOwner(Display* x_display,
78 : x_display_(x_display),
80 selection_name_(selection_name),
81 atom_cache_(x_display_, kAtomsToCache) {
84 SelectionOwner::~SelectionOwner() {
85 // If we are the selection owner, we need to release the selection so we
86 // don't receive further events. However, we don't call ClearSelectionOwner()
87 // because we don't want to do this indiscriminately.
88 if (XGetSelectionOwner(x_display_, selection_name_) == x_window_)
89 XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
92 void SelectionOwner::RetrieveTargets(std::vector<Atom>* targets) {
93 for (SelectionFormatMap::const_iterator it = format_map_.begin();
94 it != format_map_.end(); ++it) {
95 targets->push_back(it->first);
99 void SelectionOwner::TakeOwnershipOfSelection(
100 const SelectionFormatMap& data) {
101 XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime);
103 if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) {
104 // The X server agrees that we are the selection owner. Commit our data.
109 void SelectionOwner::ClearSelectionOwner() {
110 XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
111 format_map_ = SelectionFormatMap();
114 void SelectionOwner::OnSelectionRequest(const XSelectionRequestEvent& event) {
115 // Incrementally build our selection. By default this is a refusal, and we'll
116 // override the parts indicating success in the different cases.
118 reply.xselection.type = SelectionNotify;
119 reply.xselection.requestor = event.requestor;
120 reply.xselection.selection = event.selection;
121 reply.xselection.target = event.target;
122 reply.xselection.property = None; // Indicates failure
123 reply.xselection.time = event.time;
125 if (event.target == atom_cache_.GetAtom(kMultiple)) {
126 // The contents of |event.property| should be a list of
127 // <target,property> pairs.
128 std::vector<std::pair<Atom,Atom> > conversions;
129 if (GetAtomPairArrayProperty(event.requestor,
132 std::vector<Atom> conversion_results;
133 for (size_t i = 0; i < conversions.size(); ++i) {
134 bool conversion_successful = ProcessTarget(conversions[i].first,
136 conversions[i].second);
137 conversion_results.push_back(conversions[i].first);
138 conversion_results.push_back(
139 conversion_successful ? conversions[i].second : None);
142 // Set the property to indicate which conversions succeeded. This matches
148 atom_cache_.GetAtom(kAtomPair),
151 reinterpret_cast<const unsigned char*>(&conversion_results.front()),
152 conversion_results.size());
154 reply.xselection.property = event.property;
157 if (ProcessTarget(event.target, event.requestor, event.property))
158 reply.xselection.property = event.property;
161 // Send off the reply.
162 XSendEvent(x_display_, event.requestor, False, 0, &reply);
165 void SelectionOwner::OnSelectionClear(const XSelectionClearEvent& event) {
166 DLOG(ERROR) << "SelectionClear";
168 // TODO(erg): If we receive a SelectionClear event while we're handling data,
169 // we need to delay clearing.
172 bool SelectionOwner::ProcessTarget(Atom target,
175 Atom multiple_atom = atom_cache_.GetAtom(kMultiple);
176 Atom save_targets_atom = atom_cache_.GetAtom(kSaveTargets);
177 Atom targets_atom = atom_cache_.GetAtom(kTargets);
179 if (target == multiple_atom || target == save_targets_atom)
182 if (target == targets_atom) {
183 // We have been asked for TARGETS. Send an atom array back with the data
185 std::vector<Atom> targets;
186 targets.push_back(targets_atom);
187 targets.push_back(save_targets_atom);
188 targets.push_back(multiple_atom);
189 RetrieveTargets(&targets);
191 XChangeProperty(x_display_, requestor, property, XA_ATOM, 32,
193 reinterpret_cast<unsigned char*>(&targets.front()),
197 // Try to find the data type in map.
198 SelectionFormatMap::const_iterator it = format_map_.find(target);
199 if (it != format_map_.end()) {
200 XChangeProperty(x_display_, requestor, property, target, 8,
202 const_cast<unsigned char*>(
203 reinterpret_cast<const unsigned char*>(
204 it->second->front())),
208 // I would put error logging here, but GTK ignores TARGETS and spams us
209 // looking for its own internal types.