Factor padding out of _xcb_out_write_block and into its callers, XCBSendRequest and...
[free-sw/xcb/libxcb] / src / xcb_out.c
1 /* Copyright (C) 2001-2004 Bart Massey and Jamey Sharp.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a
4  * copy of this software and associated documentation files (the "Software"),
5  * to deal in the Software without restriction, including without limitation
6  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7  * and/or sell copies of the Software, and to permit persons to whom the
8  * Software is furnished to do so, subject to the following conditions:
9  * 
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  * 
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  * 
20  * Except as contained in this notice, the names of the authors or their
21  * institutions shall not be used in advertising or otherwise to promote the
22  * sale, use or other dealings in this Software without prior written
23  * authorization from the authors.
24  */
25
26 /* Stuff that sends stuff to the server. */
27
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #ifndef __GNUC__
35 # if HAVE_ALLOCA_H
36 #  include <alloca.h>
37 # else
38 #  ifdef _AIX
39  #pragma alloca
40 #  endif
41 # endif
42 #endif
43
44 #include "xcb.h"
45 #include "xcbext.h"
46 #include "xcbint.h"
47 #include "extensions/bigreq.h"
48
49 static int force_sequence_wrap(XCBConnection *c)
50 {
51     int ret = 1;
52     if((c->out.request - c->in.request_read) > 65530)
53     {
54         pthread_mutex_unlock(&c->iolock);
55         ret = XCBSync(c, 0);
56         pthread_mutex_lock(&c->iolock);
57     }
58     return ret;
59 }
60
61 static int _xcb_write(const int fd, char (*buf)[], int *count)
62 {
63     int n = write(fd, *buf, *count);
64     if(n > 0)
65     {
66         *count -= n;
67         if(*count)
68             memmove(*buf, *buf + n, *count);
69     }
70     return n;
71 }
72
73 static int _xcb_writev(const int fd, struct iovec *vec, int count)
74 {
75     int n = writev(fd, vec, count);
76     if(n > 0)
77     {
78         int rem = n;
79         for(; count; --count, ++vec)
80         {
81             int cur = vec->iov_len;
82             if(cur > rem)
83                 cur = rem;
84             vec->iov_len -= cur;
85             vec->iov_base = (char *) vec->iov_base + cur;
86             rem -= cur;
87             if(vec->iov_len)
88                 break;
89         }
90         assert(rem == 0);
91     }
92     return n;
93 }
94
95 /* Public interface */
96
97 CARD32 XCBGetMaximumRequestLength(XCBConnection *c)
98 {
99     pthread_mutex_lock(&c->out.reqlenlock);
100     if(!c->out.maximum_request_length)
101     {
102         const XCBQueryExtensionRep *ext;
103         c->out.maximum_request_length = c->setup->maximum_request_length;
104         ext = XCBGetExtensionData(c, &XCBBigRequestsId);
105         if(ext && ext->present)
106         {
107             XCBBigRequestsEnableRep *r = XCBBigRequestsEnableReply(c, XCBBigRequestsEnable(c), 0);
108             c->out.maximum_request_length = r->maximum_request_length;
109             free(r);
110         }
111     }
112     pthread_mutex_unlock(&c->out.reqlenlock);
113     return c->out.maximum_request_length;
114 }
115
116 int XCBSendRequest(XCBConnection *c, unsigned int *request, struct iovec *vector, const XCBProtocolRequest *req)
117 {
118     static const char pad[3];
119     int ret;
120     int i;
121     struct iovec *padded;
122     int padlen = 0;
123     CARD16 shortlen = 0;
124     CARD32 longlen = 0;
125     enum workarounds workaround = WORKAROUND_NONE;
126
127     assert(c != 0);
128     assert(request != 0);
129     assert(vector != 0);
130     assert(req->count > 0);
131
132     /* set the major opcode, and the minor opcode for extensions */
133     if(req->ext)
134     {
135         const XCBQueryExtensionRep *extension = XCBGetExtensionData(c, req->ext);
136         /* TODO: better error handling here, please! */
137         assert(extension && extension->present);
138         ((CARD8 *) vector[0].iov_base)[0] = extension->major_opcode;
139         ((CARD8 *) vector[0].iov_base)[1] = req->opcode;
140
141         /* do we need to work around the X server bug described in glx.xml? */
142         if(strcmp(req->ext->name, "GLX") &&
143                 ((req->opcode == 17 && ((CARD32 *) vector[0].iov_base)[0] == 0x10004) ||
144                  req->opcode == 21))
145             workaround = WORKAROUND_GLX_GET_FB_CONFIGS_BUG;
146     }
147     else
148         ((CARD8 *) vector[0].iov_base)[0] = req->opcode;
149
150     /* put together the length field, possibly using BIGREQUESTS */
151     for(i = 0; i < req->count; ++i)
152         longlen += XCB_CEIL(vector[i].iov_len) >> 2;
153
154     if(longlen > c->setup->maximum_request_length)
155     {
156         if(longlen > XCBGetMaximumRequestLength(c))
157             return 0; /* server can't take this; maybe need BIGREQUESTS? */
158     }
159     else
160     {
161         /* we don't need BIGREQUESTS. */
162         shortlen = longlen;
163         longlen = 0;
164     }
165
166     padded =
167 #ifdef HAVE_ALLOCA
168         alloca
169 #else
170         malloc
171 #endif
172         ((req->count * 2 + 3) * sizeof(struct iovec));
173     /* set the length field. */
174     ((CARD16 *) vector[0].iov_base)[1] = shortlen;
175     if(!shortlen)
176     {
177         padded[0].iov_base = vector[0].iov_base;
178         padded[0].iov_len = sizeof(CARD32);
179         vector[0].iov_base = ((char *) vector[0].iov_base) + sizeof(CARD32);
180         vector[0].iov_len -= sizeof(CARD32);
181         ++longlen;
182         padded[1].iov_base = &longlen;
183         padded[1].iov_len = sizeof(CARD32);
184         padlen = 2;
185     }
186
187     for(i = 0; i < req->count; ++i)
188     {
189         if(!vector[i].iov_len)
190             continue;
191         padded[padlen].iov_base = vector[i].iov_base;
192         padded[padlen++].iov_len = vector[i].iov_len;
193         if(!XCB_PAD(vector[i].iov_len))
194             continue;
195         padded[padlen].iov_base = (caddr_t) pad;
196         padded[padlen++].iov_len = XCB_PAD(vector[i].iov_len);
197     }
198
199     /* get a sequence number and arrange for delivery. */
200     pthread_mutex_lock(&c->iolock);
201     if(req->isvoid && !force_sequence_wrap(c))
202     {
203         pthread_mutex_unlock(&c->iolock);
204 #ifndef HAVE_ALLOCA
205         free(padded);
206 #endif
207         return -1;
208     }
209
210     *request = ++c->out.request;
211
212     if(!req->isvoid)
213         _xcb_in_expect_reply(c, *request, workaround);
214
215     ret = _xcb_out_write_block(c, padded, padlen);
216     pthread_mutex_unlock(&c->iolock);
217 #ifndef HAVE_ALLOCA
218     free(padded);
219 #endif
220
221     return ret;
222 }
223
224 int XCBFlush(XCBConnection *c)
225 {
226     int ret;
227     pthread_mutex_lock(&c->iolock);
228     ret = _xcb_out_flush(c);
229     pthread_mutex_unlock(&c->iolock);
230     return ret;
231 }
232
233 /* Private interface */
234
235 int _xcb_out_init(_xcb_out *out)
236 {
237     if(pthread_cond_init(&out->cond, 0))
238         return 0;
239     out->writing = 0;
240
241     out->queue_len = 0;
242     out->vec = 0;
243     out->vec_len = 0;
244
245     out->request = 0;
246     out->request_written = 0;
247
248     if(pthread_mutex_init(&out->reqlenlock, 0))
249         return 0;
250     out->maximum_request_length = 0;
251
252     return 1;
253 }
254
255 void _xcb_out_destroy(_xcb_out *out)
256 {
257     pthread_cond_destroy(&out->cond);
258     pthread_mutex_destroy(&out->reqlenlock);
259     free(out->vec);
260 }
261
262 int _xcb_out_write(XCBConnection *c)
263 {
264     int n;
265     if(c->out.vec_len)
266         n = _xcb_writev(c->fd, c->out.vec, c->out.vec_len);
267     else
268         n = _xcb_write(c->fd, &c->out.queue, &c->out.queue_len);
269
270     /* XXX: should "nothing was written" be considered failure or
271      * success for this function? it's not an I/O error, but... */
272     n = (n > 0) || (n < 0 && errno == EAGAIN);
273
274     if(c->out.vec_len)
275     {
276         int i;
277         for(i = 0; i < c->out.vec_len; ++i)
278             if(c->out.vec[i].iov_len)
279                 return n;
280         c->out.vec = 0;
281         c->out.vec_len = 0;
282     }
283     return n;
284 }
285
286 int _xcb_out_write_block(XCBConnection *c, struct iovec *vector, size_t count)
287 {
288     while(count && c->out.queue_len + vector[0].iov_len < sizeof(c->out.queue))
289     {
290         memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len);
291         c->out.queue_len += vector[0].iov_len;
292         ++vector, --count;
293     }
294     if(!count)
295         return 1;
296
297     memmove(vector + 1, vector, count++ * sizeof(struct iovec));
298     vector[0].iov_base = c->out.queue;
299     vector[0].iov_len = c->out.queue_len;
300     c->out.queue_len = 0;
301
302     assert(!c->out.vec_len);
303     assert(!c->out.vec);
304     c->out.vec_len = count;
305     c->out.vec = vector;
306     return _xcb_out_flush(c);
307 }
308
309 int _xcb_out_flush(XCBConnection *c)
310 {
311     int ret = 1;
312     while(ret && (c->out.queue_len || c->out.vec_len))
313         ret = _xcb_conn_wait(c, /*should_write*/ 1, &c->out.cond);
314     c->out.request_written = c->out.request;
315     pthread_cond_broadcast(&c->out.cond);
316     return ret;
317 }