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.