API/ABI change: XCBSendRequest callers must pad to 4-byte boundaries now. When not...
[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 #include "xcb.h"
35 #include "xcbext.h"
36 #include "xcbint.h"
37 #include "extensions/bigreq.h"
38
39 static int force_sequence_wrap(XCBConnection *c)
40 {
41     int ret = 1;
42     if((c->out.request - c->in.request_read) > 65530)
43     {
44         pthread_mutex_unlock(&c->iolock);
45         ret = XCBSync(c, 0);
46         pthread_mutex_lock(&c->iolock);
47     }
48     return ret;
49 }
50
51 /* Public interface */
52
53 CARD32 XCBGetMaximumRequestLength(XCBConnection *c)
54 {
55     pthread_mutex_lock(&c->out.reqlenlock);
56     if(!c->out.maximum_request_length)
57     {
58         const XCBQueryExtensionRep *ext;
59         c->out.maximum_request_length = c->setup->maximum_request_length;
60         ext = XCBGetExtensionData(c, &XCBBigRequestsId);
61         if(ext && ext->present)
62         {
63             XCBBigRequestsEnableRep *r = XCBBigRequestsEnableReply(c, XCBBigRequestsEnable(c), 0);
64             c->out.maximum_request_length = r->maximum_request_length;
65             free(r);
66         }
67     }
68     pthread_mutex_unlock(&c->out.reqlenlock);
69     return c->out.maximum_request_length;
70 }
71
72 int XCBSendRequest(XCBConnection *c, unsigned int *request, int flags, struct iovec *vector, const XCBProtocolRequest *req)
73 {
74     int ret;
75     CARD32 prefix[2];
76     int veclen = req->count;
77     enum workarounds workaround = WORKAROUND_NONE;
78
79     assert(c != 0);
80     assert(request != 0);
81     assert(vector != 0);
82     assert(req->count > 0);
83
84     if(!(flags & XCB_REQUEST_RAW))
85     {
86         static const char pad[3];
87         int i;
88         CARD16 shortlen = 0;
89         size_t longlen = 0;
90         /* set the major opcode, and the minor opcode for extensions */
91         if(req->ext)
92         {
93             const XCBQueryExtensionRep *extension = XCBGetExtensionData(c, req->ext);
94             /* TODO: better error handling here, please! */
95             assert(extension && extension->present);
96             ((CARD8 *) vector[0].iov_base)[0] = extension->major_opcode;
97             ((CARD8 *) vector[0].iov_base)[1] = req->opcode;
98         }
99         else
100             ((CARD8 *) vector[0].iov_base)[0] = req->opcode;
101
102         /* put together the length field, possibly using BIGREQUESTS */
103         for(i = 0; i < req->count; ++i)
104         {
105             longlen += vector[i].iov_len;
106             if(!vector[i].iov_base)
107             {
108                 vector[i].iov_base = (caddr_t) pad;
109                 assert(vector[i].iov_len <= sizeof(pad));
110             }
111         }
112         assert((longlen & 3) == 0);
113         longlen >>= 2;
114
115         if(longlen <= c->setup->maximum_request_length)
116         {
117             /* we don't need BIGREQUESTS. */
118             shortlen = longlen;
119             longlen = 0;
120         }
121         else if(longlen > XCBGetMaximumRequestLength(c))
122             return 0; /* server can't take this; maybe need BIGREQUESTS? */
123
124         /* set the length field. */
125         ((CARD16 *) vector[0].iov_base)[1] = shortlen;
126         if(!shortlen)
127         {
128             memmove(vector + 1, vector, veclen++ * sizeof(*vector));
129             ++veclen;
130             vector[0].iov_base = prefix;
131             vector[0].iov_len = sizeof(prefix);
132             prefix[0] = ((CARD32 *) vector[0].iov_base)[0];
133             prefix[1] = ++longlen;
134             vector[1].iov_base = ((char *) vector[1].iov_base) + sizeof(CARD32);
135             vector[1].iov_len -= sizeof(CARD32);
136         }
137     }
138     flags &= ~XCB_REQUEST_RAW;
139
140     /* do we need to work around the X server bug described in glx.xml? */
141     if(req->ext && !req->isvoid && strcmp(req->ext->name, "GLX") &&
142             ((req->opcode == 17 && ((CARD32 *) vector[0].iov_base)[0] == 0x10004) ||
143              req->opcode == 21))
144         workaround = WORKAROUND_GLX_GET_FB_CONFIGS_BUG;
145
146     /* get a sequence number and arrange for delivery. */
147     pthread_mutex_lock(&c->iolock);
148     if(req->isvoid && !force_sequence_wrap(c))
149     {
150         pthread_mutex_unlock(&c->iolock);
151         return -1;
152     }
153
154     /* wait for other writing threads to get out of my way. */
155     while(c->out.writing)
156         pthread_cond_wait(&c->out.cond, &c->iolock);
157
158     *request = ++c->out.request;
159
160     _xcb_in_expect_reply(c, *request, workaround, flags);
161
162     ret = _xcb_out_write_block(c, vector, veclen);
163     pthread_mutex_unlock(&c->iolock);
164     return ret;
165 }
166
167 int XCBFlush(XCBConnection *c)
168 {
169     int ret;
170     pthread_mutex_lock(&c->iolock);
171     ret = _xcb_out_flush(c);
172     pthread_mutex_unlock(&c->iolock);
173     return ret;
174 }
175
176 /* Private interface */
177
178 int _xcb_out_init(_xcb_out *out)
179 {
180     if(pthread_cond_init(&out->cond, 0))
181         return 0;
182     out->writing = 0;
183
184     out->queue_len = 0;
185     out->vec = 0;
186     out->vec_len = 0;
187
188     out->request = 0;
189     out->request_written = 0;
190
191     if(pthread_mutex_init(&out->reqlenlock, 0))
192         return 0;
193     out->maximum_request_length = 0;
194
195     return 1;
196 }
197
198 void _xcb_out_destroy(_xcb_out *out)
199 {
200     pthread_cond_destroy(&out->cond);
201     pthread_mutex_destroy(&out->reqlenlock);
202 }
203
204 /* precondition: there must be something for us to write. */
205 int _xcb_out_write(XCBConnection *c)
206 {
207     int n;
208     assert(!c->out.queue_len);
209     n = writev(c->fd, c->out.vec, c->out.vec_len);
210     if(n < 0 && errno == EAGAIN)
211         return 1;
212     if(n <= 0)
213         return 0;
214
215     for(; c->out.vec_len; --c->out.vec_len, ++c->out.vec)
216     {
217         int cur = c->out.vec->iov_len;
218         if(cur > n)
219             cur = n;
220         c->out.vec->iov_len -= cur;
221         c->out.vec->iov_base = (char *) c->out.vec->iov_base + cur;
222         n -= cur;
223         if(c->out.vec->iov_len)
224             break;
225     }
226     if(!c->out.vec_len)
227         c->out.vec = 0;
228     assert(n == 0);
229     return 1;
230 }
231
232 int _xcb_out_write_block(XCBConnection *c, struct iovec *vector, size_t count)
233 {
234     assert(!c->out.vec && !c->out.vec_len);
235     while(count && c->out.queue_len + vector[0].iov_len < sizeof(c->out.queue))
236     {
237         memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len);
238         c->out.queue_len += vector[0].iov_len;
239         ++vector, --count;
240     }
241     if(!count)
242         return 1;
243
244     memmove(vector + 1, vector, count++ * sizeof(struct iovec));
245     vector[0].iov_base = c->out.queue;
246     vector[0].iov_len = c->out.queue_len;
247     c->out.queue_len = 0;
248
249     c->out.vec_len = count;
250     c->out.vec = vector;
251     return _xcb_out_flush(c);
252 }
253
254 int _xcb_out_flush(XCBConnection *c)
255 {
256     int ret = 1;
257     struct iovec vec;
258     if(c->out.queue_len)
259     {
260         assert(!c->out.vec && !c->out.vec_len);
261         vec.iov_base = c->out.queue;
262         vec.iov_len = c->out.queue_len;
263         c->out.vec = &vec;
264         c->out.vec_len = 1;
265         c->out.queue_len = 0;
266     }
267     while(ret && c->out.vec_len)
268         ret = _xcb_conn_wait(c, /*should_write*/ 1, &c->out.cond);
269     c->out.request_written = c->out.request;
270     pthread_cond_broadcast(&c->out.cond);
271     return ret;
272 }