select.c revision f7df2e56
1/*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Keith Packard makes no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_DIX_CONFIG_H
24#include <dix-config.h>
25#endif
26
27#include "xfixesint.h"
28#include "xace.h"
29
30static RESTYPE SelectionClientType, SelectionWindowType;
31static Bool SelectionCallbackRegistered = FALSE;
32
33/*
34 * There is a global list of windows selecting for selection events
35 * on every selection.  This should be plenty efficient for the
36 * expected usage, if it does become a problem, it should be easily
37 * replaced with a hash table of some kind keyed off the selection atom
38 */
39
40typedef struct _SelectionEvent *SelectionEventPtr;
41
42typedef struct _SelectionEvent {
43    SelectionEventPtr next;
44    Atom selection;
45    CARD32 eventMask;
46    ClientPtr pClient;
47    WindowPtr pWindow;
48    XID clientResource;
49} SelectionEventRec;
50
51static SelectionEventPtr selectionEvents;
52
53static void
54XFixesSelectionCallback(CallbackListPtr *callbacks, void *data, void *args)
55{
56    SelectionEventPtr e;
57    SelectionInfoRec *info = (SelectionInfoRec *) args;
58    Selection *selection = info->selection;
59    int subtype;
60    CARD32 eventMask;
61
62    switch (info->kind) {
63    case SelectionSetOwner:
64        subtype = XFixesSetSelectionOwnerNotify;
65        eventMask = XFixesSetSelectionOwnerNotifyMask;
66        break;
67    case SelectionWindowDestroy:
68        subtype = XFixesSelectionWindowDestroyNotify;
69        eventMask = XFixesSelectionWindowDestroyNotifyMask;
70        break;
71    case SelectionClientClose:
72        subtype = XFixesSelectionClientCloseNotify;
73        eventMask = XFixesSelectionClientCloseNotifyMask;
74        break;
75    default:
76        return;
77    }
78    for (e = selectionEvents; e; e = e->next) {
79        if (e->selection == selection->selection && (e->eventMask & eventMask)) {
80            xXFixesSelectionNotifyEvent ev = {
81                .type = XFixesEventBase + XFixesSelectionNotify,
82                .subtype = subtype,
83                .window = e->pWindow->drawable.id,
84                .owner = (subtype == XFixesSetSelectionOwnerNotify) ?
85                            selection->window : 0,
86                .selection = e->selection,
87                .timestamp = currentTime.milliseconds,
88                .selectionTimestamp = selection->lastTimeChanged.milliseconds
89            };
90            WriteEventsToClient(e->pClient, 1, (xEvent *) &ev);
91        }
92    }
93}
94
95static Bool
96CheckSelectionCallback(void)
97{
98    if (selectionEvents) {
99        if (!SelectionCallbackRegistered) {
100            if (!AddCallback(&SelectionCallback, XFixesSelectionCallback, NULL))
101                return FALSE;
102            SelectionCallbackRegistered = TRUE;
103        }
104    }
105    else {
106        if (SelectionCallbackRegistered) {
107            DeleteCallback(&SelectionCallback, XFixesSelectionCallback, NULL);
108            SelectionCallbackRegistered = FALSE;
109        }
110    }
111    return TRUE;
112}
113
114#define SelectionAllEvents (XFixesSetSelectionOwnerNotifyMask |\
115			    XFixesSelectionWindowDestroyNotifyMask |\
116			    XFixesSelectionClientCloseNotifyMask)
117
118static int
119XFixesSelectSelectionInput(ClientPtr pClient,
120                           Atom selection, WindowPtr pWindow, CARD32 eventMask)
121{
122    void *val;
123    int rc;
124    SelectionEventPtr *prev, e;
125
126    rc = XaceHook(XACE_SELECTION_ACCESS, pClient, selection, DixGetAttrAccess);
127    if (rc != Success)
128        return rc;
129
130    for (prev = &selectionEvents; (e = *prev); prev = &e->next) {
131        if (e->selection == selection &&
132            e->pClient == pClient && e->pWindow == pWindow) {
133            break;
134        }
135    }
136    if (!eventMask) {
137        if (e) {
138            FreeResource(e->clientResource, 0);
139        }
140        return Success;
141    }
142    if (!e) {
143        e = (SelectionEventPtr) malloc(sizeof(SelectionEventRec));
144        if (!e)
145            return BadAlloc;
146
147        e->next = 0;
148        e->selection = selection;
149        e->pClient = pClient;
150        e->pWindow = pWindow;
151        e->clientResource = FakeClientID(pClient->index);
152
153        /*
154         * Add a resource hanging from the window to
155         * catch window destroy
156         */
157        rc = dixLookupResourceByType(&val, pWindow->drawable.id,
158                                     SelectionWindowType, serverClient,
159                                     DixGetAttrAccess);
160        if (rc != Success)
161            if (!AddResource(pWindow->drawable.id, SelectionWindowType,
162                             (void *) pWindow)) {
163                free(e);
164                return BadAlloc;
165            }
166
167        if (!AddResource(e->clientResource, SelectionClientType, (void *) e))
168            return BadAlloc;
169
170        *prev = e;
171        if (!CheckSelectionCallback()) {
172            FreeResource(e->clientResource, 0);
173            return BadAlloc;
174        }
175    }
176    e->eventMask = eventMask;
177    return Success;
178}
179
180int
181ProcXFixesSelectSelectionInput(ClientPtr client)
182{
183    REQUEST(xXFixesSelectSelectionInputReq);
184    WindowPtr pWin;
185    int rc;
186
187    REQUEST_SIZE_MATCH(xXFixesSelectSelectionInputReq);
188    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
189    if (rc != Success)
190        return rc;
191    if (stuff->eventMask & ~SelectionAllEvents) {
192        client->errorValue = stuff->eventMask;
193        return BadValue;
194    }
195    return XFixesSelectSelectionInput(client, stuff->selection,
196                                      pWin, stuff->eventMask);
197}
198
199int
200SProcXFixesSelectSelectionInput(ClientPtr client)
201{
202    REQUEST(xXFixesSelectSelectionInputReq);
203
204    REQUEST_SIZE_MATCH(xXFixesSelectSelectionInputReq);
205    swaps(&stuff->length);
206    swapl(&stuff->window);
207    swapl(&stuff->selection);
208    swapl(&stuff->eventMask);
209    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
210}
211
212void
213SXFixesSelectionNotifyEvent(xXFixesSelectionNotifyEvent * from,
214                            xXFixesSelectionNotifyEvent * to)
215{
216    to->type = from->type;
217    cpswaps(from->sequenceNumber, to->sequenceNumber);
218    cpswapl(from->window, to->window);
219    cpswapl(from->owner, to->owner);
220    cpswapl(from->selection, to->selection);
221    cpswapl(from->timestamp, to->timestamp);
222    cpswapl(from->selectionTimestamp, to->selectionTimestamp);
223}
224
225static int
226SelectionFreeClient(void *data, XID id)
227{
228    SelectionEventPtr old = (SelectionEventPtr) data;
229    SelectionEventPtr *prev, e;
230
231    for (prev = &selectionEvents; (e = *prev); prev = &e->next) {
232        if (e == old) {
233            *prev = e->next;
234            free(e);
235            CheckSelectionCallback();
236            break;
237        }
238    }
239    return 1;
240}
241
242static int
243SelectionFreeWindow(void *data, XID id)
244{
245    WindowPtr pWindow = (WindowPtr) data;
246    SelectionEventPtr e, next;
247
248    for (e = selectionEvents; e; e = next) {
249        next = e->next;
250        if (e->pWindow == pWindow) {
251            FreeResource(e->clientResource, 0);
252        }
253    }
254    return 1;
255}
256
257Bool
258XFixesSelectionInit(void)
259{
260    SelectionClientType = CreateNewResourceType(SelectionFreeClient,
261                                                "XFixesSelectionClient");
262    SelectionWindowType = CreateNewResourceType(SelectionFreeWindow,
263                                                "XFixesSelectionWindow");
264    return SelectionClientType && SelectionWindowType;
265}
266