select.c revision 0b0d8713
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, pointer data, pointer 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    {
80	if (e->selection == selection->selection &&
81	    (e->eventMask & eventMask))
82	{
83	    xXFixesSelectionNotifyEvent	ev;
84
85	    memset(&ev, 0, sizeof(xXFixesSelectionNotifyEvent));
86	    ev.type = XFixesEventBase + XFixesSelectionNotify;
87	    ev.subtype = subtype;
88	    ev.window = e->pWindow->drawable.id;
89	    if (subtype == XFixesSetSelectionOwnerNotify)
90		ev.owner = selection->window;
91	    else
92		ev.owner = 0;
93	    ev.selection = e->selection;
94	    ev.timestamp = currentTime.milliseconds;
95	    ev.selectionTimestamp = selection->lastTimeChanged.milliseconds;
96	    WriteEventsToClient (e->pClient, 1, (xEvent *) &ev);
97	}
98    }
99}
100
101static Bool
102CheckSelectionCallback (void)
103{
104    if (selectionEvents)
105    {
106	if (!SelectionCallbackRegistered)
107	{
108	    if (!AddCallback (&SelectionCallback, XFixesSelectionCallback, NULL))
109		return FALSE;
110	    SelectionCallbackRegistered = TRUE;
111	}
112    }
113    else
114    {
115	if (SelectionCallbackRegistered)
116	{
117	    DeleteCallback (&SelectionCallback, XFixesSelectionCallback, NULL);
118	    SelectionCallbackRegistered = FALSE;
119	}
120    }
121    return TRUE;
122}
123
124#define SelectionAllEvents (XFixesSetSelectionOwnerNotifyMask |\
125			    XFixesSelectionWindowDestroyNotifyMask |\
126			    XFixesSelectionClientCloseNotifyMask)
127
128static int
129XFixesSelectSelectionInput (ClientPtr	pClient,
130			    Atom	selection,
131			    WindowPtr	pWindow,
132			    CARD32	eventMask)
133{
134    pointer val;
135    int rc;
136    SelectionEventPtr	*prev, e;
137
138    rc = XaceHook(XACE_SELECTION_ACCESS, pClient, selection, DixGetAttrAccess);
139    if (rc != Success)
140	return rc;
141
142    for (prev = &selectionEvents; (e = *prev); prev = &e->next)
143    {
144	if (e->selection == selection &&
145	    e->pClient == pClient &&
146	    e->pWindow == pWindow)
147	{
148	    break;
149	}
150    }
151    if (!eventMask)
152    {
153	if (e)
154	{
155	    FreeResource (e->clientResource, 0);
156	}
157	return Success;
158    }
159    if (!e)
160    {
161	e = (SelectionEventPtr) malloc(sizeof (SelectionEventRec));
162	if (!e)
163	    return BadAlloc;
164
165	e->next = 0;
166	e->selection = selection;
167	e->pClient = pClient;
168	e->pWindow = pWindow;
169	e->clientResource = FakeClientID(pClient->index);
170
171	/*
172	 * Add a resource hanging from the window to
173	 * catch window destroy
174	 */
175	rc = dixLookupResourceByType (&val, pWindow->drawable.id,
176				      SelectionWindowType, serverClient,
177				      DixGetAttrAccess);
178	if (rc != Success)
179	    if (!AddResource (pWindow->drawable.id, SelectionWindowType,
180			      (pointer) pWindow))
181	    {
182		free(e);
183		return BadAlloc;
184	    }
185
186	if (!AddResource (e->clientResource, SelectionClientType, (pointer) e))
187	    return BadAlloc;
188
189	*prev = e;
190	if (!CheckSelectionCallback ())
191	{
192	    FreeResource (e->clientResource, 0);
193	    return BadAlloc;
194	}
195    }
196    e->eventMask = eventMask;
197    return Success;
198}
199
200int
201ProcXFixesSelectSelectionInput (ClientPtr client)
202{
203    REQUEST (xXFixesSelectSelectionInputReq);
204    WindowPtr	pWin;
205    int		rc;
206
207    REQUEST_SIZE_MATCH (xXFixesSelectSelectionInputReq);
208    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
209    if (rc != Success)
210        return rc;
211    if (stuff->eventMask & ~SelectionAllEvents)
212    {
213	client->errorValue = stuff->eventMask;
214	return BadValue;
215    }
216    return XFixesSelectSelectionInput (client, stuff->selection,
217				       pWin, stuff->eventMask);
218}
219
220int
221SProcXFixesSelectSelectionInput (ClientPtr client)
222{
223    register int n;
224    REQUEST(xXFixesSelectSelectionInputReq);
225
226    REQUEST_SIZE_MATCH(xXFixesSelectSelectionInputReq);
227    swaps(&stuff->length, n);
228    swapl(&stuff->window, n);
229    swapl(&stuff->selection, n);
230    swapl(&stuff->eventMask, n);
231    return (*ProcXFixesVector[stuff->xfixesReqType])(client);
232}
233
234void
235SXFixesSelectionNotifyEvent (xXFixesSelectionNotifyEvent *from,
236			     xXFixesSelectionNotifyEvent *to)
237{
238    to->type = from->type;
239    cpswaps (from->sequenceNumber, to->sequenceNumber);
240    cpswapl (from->window, to->window);
241    cpswapl (from->owner, to->owner);
242    cpswapl (from->selection, to->selection);
243    cpswapl (from->timestamp, to->timestamp);
244    cpswapl (from->selectionTimestamp, to->selectionTimestamp);
245}
246
247static int
248SelectionFreeClient (pointer data, XID id)
249{
250    SelectionEventPtr	old = (SelectionEventPtr) data;
251    SelectionEventPtr	*prev, e;
252
253    for (prev = &selectionEvents; (e = *prev); prev = &e->next)
254    {
255	if (e == old)
256	{
257	    *prev = e->next;
258	    free(e);
259	    CheckSelectionCallback ();
260	    break;
261	}
262    }
263    return 1;
264}
265
266static int
267SelectionFreeWindow (pointer data, XID id)
268{
269    WindowPtr		pWindow = (WindowPtr) data;
270    SelectionEventPtr	e, next;
271
272    for (e = selectionEvents; e; e = next)
273    {
274	next = e->next;
275	if (e->pWindow == pWindow)
276	{
277	    FreeResource (e->clientResource, 0);
278	}
279    }
280    return 1;
281}
282
283Bool
284XFixesSelectionInit (void)
285{
286    SelectionClientType = CreateNewResourceType(SelectionFreeClient,
287						"XFixesSelectionClient");
288    SelectionWindowType = CreateNewResourceType(SelectionFreeWindow,
289						"XFixesSelectionWindow");
290    return SelectionClientType && SelectionWindowType;
291}
292