Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright 2007-2008 Peter Hutterer | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice (including the next | |
12 | * paragraph) shall be included in all copies or substantial portions of the | |
13 | * Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
21 | * DEALINGS IN THE SOFTWARE. | |
22 | * | |
23 | * Author: Peter Hutterer, University of South Australia, NICTA | |
24 | */ | |
25 | ||
26 | #ifdef HAVE_DIX_CONFIG_H | |
27 | #include <dix-config.h> | |
28 | #endif | |
29 | #include "windowstr.h" | |
30 | #include <X11/extensions/ge.h> | |
31 | ||
32 | #include "geint.h" | |
33 | #include "geext.h" | |
34 | #include "protocol-versions.h" | |
35 | #include "extinit.h" | |
36 | ||
37 | DevPrivateKeyRec GEClientPrivateKeyRec; | |
38 | ||
39 | GEExtension GEExtensions[MAXEXTENSIONS]; | |
40 | ||
41 | /* Major available requests */ | |
42 | static const int version_requests[] = { | |
43 | X_GEQueryVersion, /* before client sends QueryVersion */ | |
44 | X_GEQueryVersion, /* must be set to last request in version 1 */ | |
45 | }; | |
46 | ||
47 | /* Forward declarations */ | |
48 | static void SGEGenericEvent(xEvent *from, xEvent *to); | |
49 | ||
50 | #define NUM_VERSION_REQUESTS (sizeof (version_requests) / sizeof (version_requests[0])) | |
51 | #define EXT_MASK(ext) ((ext) & 0x7F) | |
52 | ||
53 | /************************************************************/ | |
54 | /* request handlers */ | |
55 | /************************************************************/ | |
56 | ||
57 | static int | |
58 | ProcGEQueryVersion(ClientPtr client) | |
59 | { | |
60 | GEClientInfoPtr pGEClient = GEGetClient(client); | |
61 | xGEQueryVersionReply rep; | |
62 | ||
63 | REQUEST(xGEQueryVersionReq); | |
64 | ||
65 | REQUEST_SIZE_MATCH(xGEQueryVersionReq); | |
66 | ||
67 | rep = (xGEQueryVersionReply) { | |
68 | .repType = X_Reply, | |
69 | .RepType = X_GEQueryVersion, | |
70 | .sequenceNumber = client->sequence, | |
71 | .length = 0, | |
72 | ||
73 | /* return the supported version by the server */ | |
74 | .majorVersion = SERVER_GE_MAJOR_VERSION, | |
75 | .minorVersion = SERVER_GE_MINOR_VERSION | |
76 | }; | |
77 | ||
78 | /* Remember version the client requested */ | |
79 | pGEClient->major_version = stuff->majorVersion; | |
80 | pGEClient->minor_version = stuff->minorVersion; | |
81 | ||
82 | if (client->swapped) { | |
83 | swaps(&rep.sequenceNumber); | |
84 | swapl(&rep.length); | |
85 | swaps(&rep.majorVersion); | |
86 | swaps(&rep.minorVersion); | |
87 | } | |
88 | ||
89 | WriteToClient(client, sizeof(xGEQueryVersionReply), &rep); | |
90 | return Success; | |
91 | } | |
92 | ||
93 | int (*ProcGEVector[GENumberRequests]) (ClientPtr) = { | |
94 | /* Version 1.0 */ | |
95 | ProcGEQueryVersion}; | |
96 | ||
97 | /************************************************************/ | |
98 | /* swapped request handlers */ | |
99 | /************************************************************/ | |
100 | static int | |
101 | SProcGEQueryVersion(ClientPtr client) | |
102 | { | |
103 | REQUEST(xGEQueryVersionReq); | |
104 | ||
105 | swaps(&stuff->length); | |
106 | REQUEST_SIZE_MATCH(xGEQueryVersionReq); | |
107 | swaps(&stuff->majorVersion); | |
108 | swaps(&stuff->minorVersion); | |
109 | return (*ProcGEVector[stuff->ReqType]) (client); | |
110 | } | |
111 | ||
112 | int (*SProcGEVector[GENumberRequests]) (ClientPtr) = { | |
113 | /* Version 1.0 */ | |
114 | SProcGEQueryVersion}; | |
115 | ||
116 | /************************************************************/ | |
117 | /* callbacks */ | |
118 | /************************************************************/ | |
119 | ||
120 | /* dispatch requests */ | |
121 | static int | |
122 | ProcGEDispatch(ClientPtr client) | |
123 | { | |
124 | GEClientInfoPtr pGEClient = GEGetClient(client); | |
125 | ||
126 | REQUEST(xGEReq); | |
127 | ||
128 | if (pGEClient->major_version >= NUM_VERSION_REQUESTS) | |
129 | return BadRequest; | |
130 | if (stuff->ReqType > version_requests[pGEClient->major_version]) | |
131 | return BadRequest; | |
132 | ||
133 | return (ProcGEVector[stuff->ReqType]) (client); | |
134 | } | |
135 | ||
136 | /* dispatch swapped requests */ | |
137 | static int | |
138 | SProcGEDispatch(ClientPtr client) | |
139 | { | |
140 | REQUEST(xGEReq); | |
141 | if (stuff->ReqType >= GENumberRequests) | |
142 | return BadRequest; | |
143 | return (*SProcGEVector[stuff->ReqType]) (client); | |
144 | } | |
145 | ||
146 | /** | |
147 | * Called when a new client inits a connection to the X server. | |
148 | * | |
149 | * We alloc a simple struct to store the client's major/minor version. Can be | |
150 | * used in the furture for versioning support. | |
151 | */ | |
152 | static void | |
153 | GEClientCallback(CallbackListPtr *list, pointer closure, pointer data) | |
154 | { | |
155 | NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; | |
156 | ClientPtr pClient = clientinfo->client; | |
157 | GEClientInfoPtr pGEClient = GEGetClient(pClient); | |
158 | ||
159 | pGEClient->major_version = 0; | |
160 | pGEClient->minor_version = 0; | |
161 | } | |
162 | ||
163 | /* Reset extension. Called on server shutdown. */ | |
164 | static void | |
165 | GEResetProc(ExtensionEntry * extEntry) | |
166 | { | |
167 | DeleteCallback(&ClientStateCallback, GEClientCallback, 0); | |
168 | EventSwapVector[GenericEvent] = NotImplemented; | |
169 | } | |
170 | ||
171 | /* Calls the registered event swap function for the extension. | |
172 | * | |
173 | * Each extension can register a swap function to handle GenericEvents being | |
174 | * swapped properly. The server calls SGEGenericEvent() before the event is | |
175 | * written on the wire, this one calls the registered swap function to do the | |
176 | * work. | |
177 | */ | |
178 | static void | |
179 | SGEGenericEvent(xEvent *from, xEvent *to) | |
180 | { | |
181 | xGenericEvent *gefrom = (xGenericEvent *) from; | |
182 | xGenericEvent *geto = (xGenericEvent *) to; | |
183 | ||
184 | if ((gefrom->extension & 0x7f) > MAXEXTENSIONS) { | |
185 | ErrorF("GE: Invalid extension offset for event.\n"); | |
186 | return; | |
187 | } | |
188 | ||
189 | if (GEExtensions[EXT_MASK(gefrom->extension)].evswap) | |
190 | GEExtensions[EXT_MASK(gefrom->extension)].evswap(gefrom, geto); | |
191 | } | |
192 | ||
193 | /* Init extension, register at server. | |
194 | * Since other extensions may rely on XGE (XInput does already), it is a good | |
195 | * idea to init XGE first, before any other extension. | |
196 | */ | |
197 | void | |
198 | GEExtensionInit(void) | |
199 | { | |
200 | ExtensionEntry *extEntry; | |
201 | ||
202 | if (!dixRegisterPrivateKey | |
203 | (&GEClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(GEClientInfoRec))) | |
204 | FatalError("GEExtensionInit: GE private request failed.\n"); | |
205 | ||
206 | if (!AddCallback(&ClientStateCallback, GEClientCallback, 0)) { | |
207 | FatalError("GEExtensionInit: register client callback failed.\n"); | |
208 | } | |
209 | ||
210 | if ((extEntry = AddExtension(GE_NAME, | |
211 | 0, GENumberErrors, | |
212 | ProcGEDispatch, SProcGEDispatch, | |
213 | GEResetProc, StandardMinorOpcode)) != 0) { | |
214 | memset(GEExtensions, 0, sizeof(GEExtensions)); | |
215 | ||
216 | EventSwapVector[GenericEvent] = (EventSwapPtr) SGEGenericEvent; | |
217 | } | |
218 | else { | |
219 | FatalError("GEInit: AddExtensions failed.\n"); | |
220 | } | |
221 | ||
222 | } | |
223 | ||
224 | /************************************************************/ | |
225 | /* interface for extensions */ | |
226 | /************************************************************/ | |
227 | ||
228 | /* Register an extension with GE. The given swap function will be called each | |
229 | * time an event is sent to a client with different byte order. | |
230 | * @param extension The extensions major opcode | |
231 | * @param ev_swap The event swap function. | |
232 | * @param ev_fill Called for an event before delivery. The extension now has | |
233 | * the chance to fill in necessary fields for the event. | |
234 | */ | |
235 | void | |
236 | GERegisterExtension(int extension, | |
237 | void (*ev_swap) (xGenericEvent *from, xGenericEvent *to)) | |
238 | { | |
239 | if (EXT_MASK(extension) >= MAXEXTENSIONS) | |
240 | FatalError("GE: extension > MAXEXTENSIONS. This should not happen.\n"); | |
241 | ||
242 | /* extension opcodes are > 128, might as well save some space here */ | |
243 | GEExtensions[EXT_MASK(extension)].evswap = ev_swap; | |
244 | } | |
245 | ||
246 | /* Sets type and extension field for a generic event. This is just an | |
247 | * auxiliary function, extensions could do it manually too. | |
248 | */ | |
249 | void | |
250 | GEInitEvent(xGenericEvent *ev, int extension) | |
251 | { | |
252 | ev->type = GenericEvent; | |
253 | ev->extension = extension; | |
254 | ev->length = 0; | |
255 | } |