selection.c revision 1b5d61b8
1/************************************************************
2
3Copyright 1987, 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
26
27                        All Rights Reserved
28
29Permission to use, copy, modify, and distribute this software and its
30documentation for any purpose and without fee is hereby granted,
31provided that the above copyright notice appear in all copies and that
32both that copyright notice and this permission notice appear in
33supporting documentation, and that the name of Digital not be
34used in advertising or publicity pertaining to distribution of the
35software without specific, written prior permission.
36
37DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43SOFTWARE.
44
45********************************************************/
46
47#ifdef HAVE_DIX_CONFIG_H
48#include <dix-config.h>
49#endif
50
51#include "windowstr.h"
52#include "dixstruct.h"
53#include "dispatch.h"
54#include "selection.h"
55#include "xace.h"
56
57/*****************************************************************
58 * Selection Stuff
59 *
60 *    dixLookupSelection
61 *
62 *   Selections are global to the server.  The list of selections should
63 *   not be traversed directly.  Instead, use the functions listed above.
64 *
65 *****************************************************************/
66
67Selection *CurrentSelections;
68CallbackListPtr SelectionCallback;
69
70int
71dixLookupSelection(Selection ** result, Atom selectionName,
72                   ClientPtr client, Mask access_mode)
73{
74    Selection *pSel;
75    int rc = BadMatch;
76
77    client->errorValue = selectionName;
78
79    for (pSel = CurrentSelections; pSel; pSel = pSel->next)
80        if (pSel->selection == selectionName)
81            break;
82
83    if (pSel)
84        rc = XaceHookSelectionAccess(client, &pSel, access_mode);
85    *result = pSel;
86    return rc;
87}
88
89void
90InitSelections(void)
91{
92    Selection *pSel, *pNextSel;
93
94    pSel = CurrentSelections;
95    while (pSel) {
96        pNextSel = pSel->next;
97        dixFreeObjectWithPrivates(pSel, PRIVATE_SELECTION);
98        pSel = pNextSel;
99    }
100
101    CurrentSelections = NULL;
102}
103
104static _X_INLINE void
105CallSelectionCallback(Selection * pSel, ClientPtr client,
106                      SelectionCallbackKind kind)
107{
108    SelectionInfoRec info = { pSel, client, kind };
109    CallCallbacks(&SelectionCallback, &info);
110}
111
112void
113DeleteWindowFromAnySelections(WindowPtr pWin)
114{
115    Selection *pSel;
116
117    for (pSel = CurrentSelections; pSel; pSel = pSel->next)
118        if (pSel->pWin == pWin) {
119            CallSelectionCallback(pSel, NULL, SelectionWindowDestroy);
120
121            pSel->pWin = (WindowPtr) NULL;
122            pSel->window = None;
123            pSel->client = NullClient;
124        }
125}
126
127void
128DeleteClientFromAnySelections(ClientPtr client)
129{
130    Selection *pSel;
131
132    for (pSel = CurrentSelections; pSel; pSel = pSel->next)
133        if (pSel->client == client) {
134            CallSelectionCallback(pSel, NULL, SelectionClientClose);
135
136            pSel->pWin = (WindowPtr) NULL;
137            pSel->window = None;
138            pSel->client = NullClient;
139        }
140}
141
142int
143ProcSetSelectionOwner(ClientPtr client)
144{
145    WindowPtr pWin = NULL;
146    TimeStamp time;
147    Selection *pSel;
148    int rc;
149
150    REQUEST(xSetSelectionOwnerReq);
151    REQUEST_SIZE_MATCH(xSetSelectionOwnerReq);
152
153    UpdateCurrentTime();
154    time = ClientTimeToServerTime(stuff->time);
155
156    /* If the client's time stamp is in the future relative to the server's
157       time stamp, do not set the selection, just return success. */
158    if (CompareTimeStamps(time, currentTime) == LATER)
159        return Success;
160
161    if (stuff->window != None) {
162        rc = dixLookupWindow(&pWin, stuff->window, client, DixSetAttrAccess);
163        if (rc != Success)
164            return rc;
165    }
166    if (!ValidAtom(stuff->selection)) {
167        client->errorValue = stuff->selection;
168        return BadAtom;
169    }
170
171    /*
172     * First, see if the selection is already set...
173     */
174    rc = dixLookupSelection(&pSel, stuff->selection, client, DixSetAttrAccess);
175
176    if (rc == Success) {
177        /* If the timestamp in client's request is in the past relative
178           to the time stamp indicating the last time the owner of the
179           selection was set, do not set the selection, just return
180           success. */
181        if (CompareTimeStamps(time, pSel->lastTimeChanged) == EARLIER)
182            return Success;
183        if (pSel->client && (!pWin || (pSel->client != client))) {
184            xEvent event = {
185                .u.selectionClear.time = time.milliseconds,
186                .u.selectionClear.window = pSel->window,
187                .u.selectionClear.atom = pSel->selection
188            };
189            event.u.u.type = SelectionClear;
190            WriteEventsToClient(pSel->client, 1, &event);
191        }
192    }
193    else if (rc == BadMatch) {
194        /*
195         * It doesn't exist, so add it...
196         */
197        pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION);
198        if (!pSel)
199            return BadAlloc;
200
201        pSel->selection = stuff->selection;
202
203        /* security creation/labeling check */
204        rc = XaceHookSelectionAccess(client, &pSel,
205                                     DixCreateAccess | DixSetAttrAccess);
206        if (rc != Success) {
207            free(pSel);
208            return rc;
209        }
210
211        pSel->next = CurrentSelections;
212        CurrentSelections = pSel;
213    }
214    else
215        return rc;
216
217    pSel->lastTimeChanged = time;
218    pSel->window = stuff->window;
219    pSel->pWin = pWin;
220    pSel->client = (pWin ? client : NullClient);
221
222    CallSelectionCallback(pSel, client, SelectionSetOwner);
223    return Success;
224}
225
226int
227ProcGetSelectionOwner(ClientPtr client)
228{
229    int rc;
230    Selection *pSel;
231    xGetSelectionOwnerReply reply;
232
233    REQUEST(xResourceReq);
234    REQUEST_SIZE_MATCH(xResourceReq);
235
236    if (!ValidAtom(stuff->id)) {
237        client->errorValue = stuff->id;
238        return BadAtom;
239    }
240
241    reply = (xGetSelectionOwnerReply) {
242        .type = X_Reply,
243        .sequenceNumber = client->sequence,
244        .length = 0,
245    };
246
247    rc = dixLookupSelection(&pSel, stuff->id, client, DixGetAttrAccess);
248    if (rc == Success)
249        reply.owner = pSel->window;
250    else if (rc == BadMatch)
251        reply.owner = None;
252    else
253        return rc;
254
255    WriteReplyToClient(client, sizeof(xGetSelectionOwnerReply), &reply);
256    return Success;
257}
258
259int
260ProcConvertSelection(ClientPtr client)
261{
262    Bool paramsOkay;
263    xEvent event;
264    WindowPtr pWin;
265    Selection *pSel;
266    int rc;
267
268    REQUEST(xConvertSelectionReq);
269    REQUEST_SIZE_MATCH(xConvertSelectionReq);
270
271    rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess);
272    if (rc != Success)
273        return rc;
274
275    paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target);
276    paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property);
277    if (!paramsOkay) {
278        client->errorValue = stuff->property;
279        return BadAtom;
280    }
281
282    if (stuff->time == CurrentTime)
283        UpdateCurrentTime();
284
285    rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess);
286
287    memset(&event, 0, sizeof(xEvent));
288    if (rc != Success && rc != BadMatch)
289        return rc;
290    else if (rc == Success && pSel->window != None) {
291        event.u.u.type = SelectionRequest;
292        event.u.selectionRequest.owner = pSel->window;
293        event.u.selectionRequest.time = stuff->time;
294        event.u.selectionRequest.requestor = stuff->requestor;
295        event.u.selectionRequest.selection = stuff->selection;
296        event.u.selectionRequest.target = stuff->target;
297        event.u.selectionRequest.property = stuff->property;
298        if (pSel->client && pSel->client != serverClient &&
299            !pSel->client->clientGone) {
300            WriteEventsToClient(pSel->client, 1, &event);
301            return Success;
302        }
303    }
304
305    event.u.u.type = SelectionNotify;
306    event.u.selectionNotify.time = stuff->time;
307    event.u.selectionNotify.requestor = stuff->requestor;
308    event.u.selectionNotify.selection = stuff->selection;
309    event.u.selectionNotify.target = stuff->target;
310    event.u.selectionNotify.property = None;
311    WriteEventsToClient(client, 1, &event);
312    return Success;
313}
314