select.c revision 05b261ec
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
29static RESTYPE		SelectionClientType, SelectionWindowType;
30static Bool		SelectionCallbackRegistered = FALSE;
31
32/*
33 * There is a global list of windows selecting for selection events
34 * on every selection.  This should be plenty efficient for the
35 * expected usage, if it does become a problem, it should be easily
36 * replaced with a hash table of some kind keyed off the selection atom
37 */
38
39typedef struct _SelectionEvent *SelectionEventPtr;
40
41typedef struct _SelectionEvent {
42    SelectionEventPtr	next;
43    Atom		selection;
44    CARD32		eventMask;
45    ClientPtr		pClient;
46    WindowPtr		pWindow;
47    XID			clientResource;
48} SelectionEventRec;
49
50static SelectionEventPtr	selectionEvents;
51
52static void
53XFixesSelectionCallback (CallbackListPtr *callbacks, pointer data, pointer args)
54{
55    SelectionEventPtr	e;
56    SelectionInfoRec	*info = (SelectionInfoRec *) args;
57    Selection		*selection = info->selection;
58    int			subtype;
59    CARD32		eventMask;
60
61    switch (info->kind) {
62    case SelectionSetOwner:
63	subtype = XFixesSetSelectionOwnerNotify;
64	eventMask = XFixesSetSelectionOwnerNotifyMask;
65	break;
66    case SelectionWindowDestroy:
67	subtype = XFixesSelectionWindowDestroyNotify;
68	eventMask = XFixesSelectionWindowDestroyNotifyMask;
69	break;
70    case SelectionClientClose:
71	subtype = XFixesSelectionClientCloseNotify;
72	eventMask = XFixesSelectionClientCloseNotifyMask;
73	break;
74    default:
75	return;
76    }
77    for (e = selectionEvents; e; e = e->next)
78    {
79	if (e->selection == selection->selection &&
80	    (e->eventMask & eventMask) &&
81	    !e->pClient->clientGone)
82	{
83	    xXFixesSelectionNotifyEvent	ev;
84
85	    ev.type = XFixesEventBase + XFixesSelectionNotify;
86	    ev.subtype = subtype;
87	    ev.sequenceNumber = e->pClient->sequence;
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    SelectionEventPtr	*prev, e;
135
136    for (prev = &selectionEvents; (e = *prev); prev = &e->next)
137    {
138	if (e->selection == selection &&
139	    e->pClient == pClient &&
140	    e->pWindow == pWindow)
141	{
142	    break;
143	}
144    }
145    if (!eventMask)
146    {
147	if (e)
148	{
149	    FreeResource (e->clientResource, 0);
150	}
151	return Success;
152    }
153    if (!e)
154    {
155	e = (SelectionEventPtr) xalloc (sizeof (SelectionEventRec));
156	if (!e)
157	    return BadAlloc;
158
159	e->next = 0;
160	e->selection = selection;
161	e->pClient = pClient;
162	e->pWindow = pWindow;
163	e->clientResource = FakeClientID(pClient->index);
164
165	/*
166	 * Add a resource hanging from the window to
167	 * catch window destroy
168	 */
169	if (!LookupIDByType(pWindow->drawable.id, SelectionWindowType))
170	    if (!AddResource (pWindow->drawable.id, SelectionWindowType,
171			      (pointer) pWindow))
172	    {
173		xfree (e);
174		return BadAlloc;
175	    }
176
177	if (!AddResource (e->clientResource, SelectionClientType, (pointer) e))
178	    return BadAlloc;
179
180	*prev = e;
181	if (!CheckSelectionCallback ())
182	{
183	    FreeResource (e->clientResource, 0);
184	    return BadAlloc;
185	}
186    }
187    e->eventMask = eventMask;
188    return Success;
189}
190
191int
192ProcXFixesSelectSelectionInput (ClientPtr client)
193{
194    REQUEST (xXFixesSelectSelectionInputReq);
195    WindowPtr	pWin;
196    int		rc;
197
198    REQUEST_SIZE_MATCH (xXFixesSelectSelectionInputReq);
199    rc = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
200    if (rc != Success)
201        return rc;
202    if (stuff->eventMask & ~SelectionAllEvents)
203    {
204	client->errorValue = stuff->eventMask;
205	return( BadValue );
206    }
207    return XFixesSelectSelectionInput (client, stuff->selection,
208				       pWin, stuff->eventMask);
209}
210
211int
212SProcXFixesSelectSelectionInput (ClientPtr client)
213{
214    register int n;
215    REQUEST(xXFixesSelectSelectionInputReq);
216
217    swaps(&stuff->length, n);
218    swapl(&stuff->window, n);
219    swapl(&stuff->selection, n);
220    swapl(&stuff->eventMask, n);
221    return ProcXFixesSelectSelectionInput(client);
222}
223
224void
225SXFixesSelectionNotifyEvent (xXFixesSelectionNotifyEvent *from,
226			     xXFixesSelectionNotifyEvent *to)
227{
228    to->type = from->type;
229    cpswaps (from->sequenceNumber, to->sequenceNumber);
230    cpswapl (from->window, to->window);
231    cpswapl (from->owner, to->owner);
232    cpswapl (from->selection, to->selection);
233    cpswapl (from->timestamp, to->timestamp);
234    cpswapl (from->selectionTimestamp, to->selectionTimestamp);
235}
236
237static int
238SelectionFreeClient (pointer data, XID id)
239{
240    SelectionEventPtr	old = (SelectionEventPtr) data;
241    SelectionEventPtr	*prev, e;
242
243    for (prev = &selectionEvents; (e = *prev); prev = &e->next)
244    {
245	if (e == old)
246	{
247	    *prev = e->next;
248	    xfree (e);
249	    CheckSelectionCallback ();
250	    break;
251	}
252    }
253    return 1;
254}
255
256static int
257SelectionFreeWindow (pointer data, XID id)
258{
259    WindowPtr		pWindow = (WindowPtr) data;
260    SelectionEventPtr	e, next;
261
262    for (e = selectionEvents; e; e = next)
263    {
264	next = e->next;
265	if (e->pWindow == pWindow)
266	{
267	    FreeResource (e->clientResource, 0);
268	}
269    }
270    return 1;
271}
272
273Bool
274XFixesSelectionInit (void)
275{
276    SelectionClientType = CreateNewResourceType(SelectionFreeClient);
277    SelectionWindowType = CreateNewResourceType(SelectionFreeWindow);
278    return SelectionClientType && SelectionWindowType;
279}
280