56e02f78e3c69f8ea9ed28e76dc1162e4c24b7aa
[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
33 #include "xcb.h"
34 #include "xcbext.h"
35 #include "xcbint.h"
36 #include "extensions/bigreq.h"
37
38 static int write_block(XCBConnection *c, struct iovec *vector, int count)
39 {
40     while(count && c->out.queue_len + vector[0].iov_len <= sizeof(c->out.queue))
41     {
42         memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len);
43         c->out.queue_len += vector[0].iov_len;
44         vector[0].iov_base = (char *) vector[0].iov_base + vector[0].iov_len;
45         vector[0].iov_len = 0;
46         ++vector, --count;
47     }
48     if(!count)
49         return 1;
50
51     --vector, ++count;
52     vector[0].iov_base = c->out.queue;
53     vector[0].iov_len = c->out.queue_len;
54     c->out.queue_len = 0;
55     return _xcb_out_send(c, &vector, &count);
56 }
57
58 /* Public interface */
59
60 CARD32 XCBGetMaximumRequestLength(XCBConnection *c)
61 {
62     pthread_mutex_lock(&c->out.reqlenlock);
63     if(!c->out.maximum_request_length)
64     {
65         const XCBQueryExtensionRep *ext;
66         c->out.maximum_request_length = c->setup->maximum_request_length;
67         ext = XCBGetExtensionData(c, &XCBBigRequestsId);
68         if(ext && ext->present)
69         {
70             XCBBigRequestsEnableRep *r = XCBBigRequestsEnableReply(c, XCBBigRequestsEnable(c), 0);
71             c->out.maximum_request_length = r->maximum_request_length;
72             free(r);
73         }
74     }
75     pthread_mutex_unlock(&c->out.reqlenlock);
76     return c->out.maximum_request_length;
77 }
78
79 unsigned int XCBSendRequest(XCBConnection *c, int flags, struct iovec *vector, const XCBProtocolRequest *req)
80 {
81     static const union {
82         struct {
83             CARD8 major;
84             CARD8 pad;
85             CARD16 len;
86         } fields;
87         CARD32 packet;
88     } sync = { { /* GetInputFocus */ 43, 0, 1 } };
89     unsigned int request;
90     CARD32 prefix[3] = { 0 };
91     int veclen = req->count;
92     enum workarounds workaround = WORKAROUND_NONE;
93
94     assert(c != 0);
95     assert(vector != 0);
96     assert(req->count > 0);
97
98     if(!(flags & XCB_REQUEST_RAW))
99     {
100         static const char pad[3];
101         int i;
102         CARD16 shortlen = 0;
103         size_t longlen = 0;
104         assert(vector[0].iov_len >= 4);
105         /* set the major opcode, and the minor opcode for extensions */
106         if(req->ext)
107         {
108             const XCBQueryExtensionRep *extension = XCBGetExtensionData(c, req->ext);
109             if(!(extension && extension->present))
110                 return 0;
111             ((CARD8 *) vector[0].iov_base)[0] = extension->major_opcode;
112             ((CARD8 *) vector[0].iov_base)[1] = req->opcode;
113         }
114         else
115             ((CARD8 *) vector[0].iov_base)[0] = req->opcode;
116
117         /* put together the length field, possibly using BIGREQUESTS */
118         for(i = 0; i < req->count; ++i)
119         {
120             longlen += vector[i].iov_len;
121             if(!vector[i].iov_base)
122             {
123                 vector[i].iov_base = (char *) pad;
124                 assert(vector[i].iov_len <= sizeof(pad));
125             }
126         }
127         assert((longlen & 3) == 0);
128         longlen >>= 2;
129
130         if(longlen <= c->setup->maximum_request_length)
131         {
132             /* we don't need BIGREQUESTS. */
133             shortlen = longlen;
134             longlen = 0;
135         }
136         else if(longlen > XCBGetMaximumRequestLength(c))
137             return 0; /* server can't take this; maybe need BIGREQUESTS? */
138
139         /* set the length field. */
140         ((CARD16 *) vector[0].iov_base)[1] = shortlen;
141         if(!shortlen)
142             prefix[2] = ++longlen;
143     }
144     flags &= ~XCB_REQUEST_RAW;
145
146     /* do we need to work around the X server bug described in glx.xml? */
147     /* XXX: GetFBConfigs won't use BIG-REQUESTS in any sane
148      * configuration, but that should be handled here anyway. */
149     if(req->ext && !req->isvoid && !strcmp(req->ext->name, "GLX") &&
150             ((req->opcode == 17 && ((CARD32 *) vector[0].iov_base)[1] == 0x10004) ||
151              req->opcode == 21))
152         workaround = WORKAROUND_GLX_GET_FB_CONFIGS_BUG;
153
154     /* get a sequence number and arrange for delivery. */
155     pthread_mutex_lock(&c->iolock);
156     /* wait for other writing threads to get out of my way. */
157     while(c->out.writing)
158         pthread_cond_wait(&c->out.cond, &c->iolock);
159
160     request = ++c->out.request;
161     /* send GetInputFocus (sync) when 64k-2 requests have been sent without
162      * a reply.
163      * Also send sync (could use NoOp) at 32-bit wrap to avoid having
164      * applications see sequence 0 as that is used to indicate
165      * an error in sending the request */
166     while((req->isvoid &&
167         c->out.request == c->in.request_expected + (1 << 16) - 1) ||
168        request == 0)
169     {
170         prefix[0] = sync.packet;
171         _xcb_in_expect_reply(c, request, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY);
172         c->in.request_expected = c->out.request;
173         request = ++c->out.request;
174     }
175
176     if(workaround != WORKAROUND_NONE || flags != 0)
177         _xcb_in_expect_reply(c, request, workaround, flags);
178     if(!req->isvoid)
179         c->in.request_expected = c->out.request;
180
181     if(prefix[0] || prefix[2])
182     {
183         --vector, ++veclen;
184         if(prefix[2])
185         {
186             prefix[1] = ((CARD32 *) vector[1].iov_base)[0];
187             vector[1].iov_base = (CARD32 *) vector[1].iov_base + 1;
188             vector[1].iov_len -= sizeof(CARD32);
189         }
190         vector[0].iov_len = sizeof(CARD32) * (prefix[0] ? 1 : 0 | prefix[2] ? 2 : 0);
191         vector[0].iov_base = prefix + !prefix[0];
192     }
193
194     if(!write_block(c, vector, veclen))
195         request = 0;
196     pthread_mutex_unlock(&c->iolock);
197     return request;
198 }
199
200 int XCBFlush(XCBConnection *c)
201 {
202     int ret;
203     pthread_mutex_lock(&c->iolock);
204     ret = _xcb_out_flush_to(c, c->out.request);
205     pthread_mutex_unlock(&c->iolock);
206     return ret;
207 }
208
209 /* Private interface */
210
211 int _xcb_out_init(_xcb_out *out)
212 {
213     if(pthread_cond_init(&out->cond, 0))
214         return 0;
215     out->writing = 0;
216
217     out->queue_len = 0;
218
219     out->request = 0;
220     out->request_written = 0;
221
222     if(pthread_mutex_init(&out->reqlenlock, 0))
223         return 0;
224     out->maximum_request_length = 0;
225
226     return 1;
227 }
228
229 void _xcb_out_destroy(_xcb_out *out)
230 {
231     pthread_cond_destroy(&out->cond);
232     pthread_mutex_destroy(&out->reqlenlock);
233 }
234
235 int _xcb_out_send(XCBConnection *c, struct iovec **vector, int *count)
236 {
237     int ret = 1;
238     while(ret && *count)
239         ret = _xcb_conn_wait(c, &c->out.cond, vector, count);
240     c->out.request_written = c->out.request;
241     pthread_cond_broadcast(&c->out.cond);
242     return ret;
243 }
244
245 int _xcb_out_flush_to(XCBConnection *c, unsigned int request)
246 {
247     assert(XCB_SEQUENCE_COMPARE(request, <=, c->out.request));
248     if(XCB_SEQUENCE_COMPARE(c->out.request_written, >=, request))
249         return 1;
250     if(c->out.queue_len)
251     {
252         struct iovec vec, *vec_ptr = &vec;
253         int count = 1;
254         vec.iov_base = c->out.queue;
255         vec.iov_len = c->out.queue_len;
256         c->out.queue_len = 0;
257         return _xcb_out_send(c, &vec_ptr, &count);
258     }
259     while(c->out.writing)
260         pthread_cond_wait(&c->out.cond, &c->iolock);
261     assert(XCB_SEQUENCE_COMPARE(c->out.request_written, >=, request));
262     return 1;
263 }