Remove XCB_CEIL and use a simpler definition for XCB_PAD.
[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_writev(const int fd, struct iovec *vec, int count)
62 {
63     int n = writev(fd, vec, count);
64     if(n > 0)
65     {
66         int rem = n;
67         for(; count; --count, ++vec)
68         {
69             int cur = vec->iov_len;
70             if(cur > rem)
71                 cur = rem;
72             vec->iov_len -= cur;
73             vec->iov_base = (char *) vec->iov_base + cur;
74             rem -= cur;
75             if(vec->iov_len)
76                 break;
77         }
78         assert(rem == 0);
79     }
80     return n;
81 }
82
83 /* Public interface */
84
85 CARD32 XCBGetMaximumRequestLength(XCBConnection *c)
86 {
87     pthread_mutex_lock(&c->out.reqlenlock);
88     if(!c->out.maximum_request_length)
89     {
90         const XCBQueryExtensionRep *ext;
91         c->out.maximum_request_length = c->setup->maximum_request_length;
92         ext = XCBGetExtensionData(c, &XCBBigRequestsId);
93         if(ext && ext->present)
94         {
95             XCBBigRequestsEnableRep *r = XCBBigRequestsEnableReply(c, XCBBigRequestsEnable(c), 0);
96             c->out.maximum_request_length = r->maximum_request_length;
97             free(r);
98         }
99     }
100     pthread_mutex_unlock(&c->out.reqlenlock);
101     return c->out.maximum_request_length;
102 }
103
104 int XCBSendRequest(XCBConnection *c, unsigned int *request, struct iovec *vector, const XCBProtocolRequest *req)
105 {
106     static const char pad[3];
107     int ret;
108     int i;
109     struct iovec *padded;
110     int padlen = 0;
111     CARD16 shortlen = 0;
112     CARD32 longlen = 0;
113     enum workarounds workaround = WORKAROUND_NONE;
114
115     assert(c != 0);
116     assert(request != 0);
117     assert(vector != 0);
118     assert(req->count > 0);
119
120     /* set the major opcode, and the minor opcode for extensions */
121     if(req->ext)
122     {
123         const XCBQueryExtensionRep *extension = XCBGetExtensionData(c, req->ext);
124         /* TODO: better error handling here, please! */
125         assert(extension && extension->present);
126         ((CARD8 *) vector[0].iov_base)[0] = extension->major_opcode;
127         ((CARD8 *) vector[0].iov_base)[1] = req->opcode;
128
129         /* do we need to work around the X server bug described in glx.xml? */
130         if(strcmp(req->ext->name, "GLX") &&
131                 ((req->opcode == 17 && ((CARD32 *) vector[0].iov_base)[0] == 0x10004) ||
132                  req->opcode == 21))
133             workaround = WORKAROUND_GLX_GET_FB_CONFIGS_BUG;
134     }
135     else
136         ((CARD8 *) vector[0].iov_base)[0] = req->opcode;
137
138     /* put together the length field, possibly using BIGREQUESTS */
139     for(i = 0; i < req->count; ++i)
140         longlen += (vector[i].iov_len + 3) >> 2;
141
142     if(longlen > c->setup->maximum_request_length)
143     {
144         if(longlen > XCBGetMaximumRequestLength(c))
145             return 0; /* server can't take this; maybe need BIGREQUESTS? */
146     }
147     else
148     {
149         /* we don't need BIGREQUESTS. */
150         shortlen = longlen;
151         longlen = 0;
152     }
153
154     padded =
155 #ifdef HAVE_ALLOCA
156         alloca
157 #else
158         malloc
159 #endif
160         ((req->count * 2 + 3) * sizeof(struct iovec));
161     /* set the length field. */
162     ((CARD16 *) vector[0].iov_base)[1] = shortlen;
163     if(!shortlen)
164     {
165         padded[0].iov_base = vector[0].iov_base;
166         padded[0].iov_len = sizeof(CARD32);
167         vector[0].iov_base = ((char *) vector[0].iov_base) + sizeof(CARD32);
168         vector[0].iov_len -= sizeof(CARD32);
169         ++longlen;
170         padded[1].iov_base = &longlen;
171         padded[1].iov_len = sizeof(CARD32);
172         padlen = 2;
173     }
174
175     for(i = 0; i < req->count; ++i)
176     {
177         if(!vector[i].iov_len)
178             continue;
179         padded[padlen].iov_base = vector[i].iov_base;
180         padded[padlen++].iov_len = vector[i].iov_len;
181         if(!XCB_PAD(vector[i].iov_len))
182             continue;
183         padded[padlen].iov_base = (caddr_t) pad;
184         padded[padlen++].iov_len = XCB_PAD(vector[i].iov_len);
185     }
186
187     /* get a sequence number and arrange for delivery. */
188     pthread_mutex_lock(&c->iolock);
189     if(req->isvoid && !force_sequence_wrap(c))
190     {
191         pthread_mutex_unlock(&c->iolock);
192 #ifndef HAVE_ALLOCA
193         free(padded);
194 #endif
195         return -1;
196     }
197
198     *request = ++c->out.request;
199
200     if(!req->isvoid)
201         _xcb_in_expect_reply(c, *request, workaround);
202
203     ret = _xcb_out_write_block(c, padded, padlen);
204     pthread_mutex_unlock(&c->iolock);
205 #ifndef HAVE_ALLOCA
206     free(padded);
207 #endif
208
209     return ret;
210 }
211
212 int XCBFlush(XCBConnection *c)
213 {
214     int ret;
215     pthread_mutex_lock(&c->iolock);
216     ret = _xcb_out_flush(c);
217     pthread_mutex_unlock(&c->iolock);
218     return ret;
219 }
220
221 /* Private interface */
222
223 int _xcb_out_init(_xcb_out *out)
224 {
225     if(pthread_cond_init(&out->cond, 0))
226         return 0;
227     out->writing = 0;
228
229     out->queue_len = 0;
230     out->vec = 0;
231     out->vec_len = 0;
232
233     out->request = 0;
234     out->request_written = 0;
235
236     if(pthread_mutex_init(&out->reqlenlock, 0))
237         return 0;
238     out->maximum_request_length = 0;
239
240     return 1;
241 }
242
243 void _xcb_out_destroy(_xcb_out *out)
244 {
245     pthread_cond_destroy(&out->cond);
246     pthread_mutex_destroy(&out->reqlenlock);
247 }
248
249 /* precondition: there must be something for us to write. */
250 int _xcb_out_write(XCBConnection *c)
251 {
252     int n;
253     assert(!c->out.queue_len);
254     n = _xcb_writev(c->fd, c->out.vec, c->out.vec_len);
255     while(c->out.vec_len && !c->out.vec[0].iov_len)
256         ++c->out.vec, --c->out.vec_len;
257     if(!c->out.vec_len)
258         c->out.vec = 0;
259     return (n > 0) || (n < 0 && errno == EAGAIN);
260 }
261
262 int _xcb_out_write_block(XCBConnection *c, struct iovec *vector, size_t count)
263 {
264     while(c->out.writing)
265         pthread_cond_wait(&c->out.cond, &c->iolock);
266     assert(!c->out.vec && !c->out.vec_len);
267     while(count && c->out.queue_len + vector[0].iov_len < sizeof(c->out.queue))
268     {
269         memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len);
270         c->out.queue_len += vector[0].iov_len;
271         ++vector, --count;
272     }
273     if(!count)
274         return 1;
275
276     memmove(vector + 1, vector, count++ * sizeof(struct iovec));
277     vector[0].iov_base = c->out.queue;
278     vector[0].iov_len = c->out.queue_len;
279     c->out.queue_len = 0;
280
281     c->out.vec_len = count;
282     c->out.vec = vector;
283     return _xcb_out_flush(c);
284 }
285
286 int _xcb_out_flush(XCBConnection *c)
287 {
288     int ret = 1;
289     struct iovec vec;
290     if(c->out.queue_len)
291     {
292         assert(!c->out.vec && !c->out.vec_len);
293         vec.iov_base = c->out.queue;
294         vec.iov_len = c->out.queue_len;
295         c->out.vec = &vec;
296         c->out.vec_len = 1;
297         c->out.queue_len = 0;
298     }
299     while(ret && c->out.vec_len)
300         ret = _xcb_conn_wait(c, /*should_write*/ 1, &c->out.cond);
301     c->out.request_written = c->out.request;
302     pthread_cond_broadcast(&c->out.cond);
303     return ret;
304 }