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