File: | wayland-client.c |
Location: | line 835, column 6 |
Description: | Use of memory after it is freed |
1 | /* | |||
2 | * Copyright © 2008-2012 Kristian Høgsberg | |||
3 | * Copyright © 2010-2012 Intel Corporation | |||
4 | * | |||
5 | * Permission to use, copy, modify, distribute, and sell this software and its | |||
6 | * documentation for any purpose is hereby granted without fee, provided that | |||
7 | * the above copyright notice appear in all copies and that both that copyright | |||
8 | * notice and this permission notice appear in supporting documentation, and | |||
9 | * that the name of the copyright holders not be used in advertising or | |||
10 | * publicity pertaining to distribution of the software without specific, | |||
11 | * written prior permission. The copyright holders make no representations | |||
12 | * about the suitability of this software for any purpose. It is provided "as | |||
13 | * is" without express or implied warranty. | |||
14 | * | |||
15 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |||
16 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |||
17 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |||
18 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |||
19 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |||
20 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |||
21 | * OF THIS SOFTWARE. | |||
22 | */ | |||
23 | ||||
24 | #include <stdlib.h> | |||
25 | #include <stdint.h> | |||
26 | #include <stddef.h> | |||
27 | #include <stdio.h> | |||
28 | #include <stdbool.h> | |||
29 | #include <errno(*__errno_location ()).h> | |||
30 | #include <string.h> | |||
31 | #include <unistd.h> | |||
32 | #include <sys/socket.h> | |||
33 | #include <sys/un.h> | |||
34 | #include <ctype.h> | |||
35 | #include <assert.h> | |||
36 | #include <fcntl.h> | |||
37 | #include <sys/poll.h> | |||
38 | #include <pthread.h> | |||
39 | ||||
40 | #include "wayland-util.h" | |||
41 | #include "wayland-os.h" | |||
42 | #include "wayland-client.h" | |||
43 | #include "wayland-private.h" | |||
44 | ||||
45 | ||||
46 | /** \cond */ | |||
47 | ||||
48 | enum wl_proxy_flag { | |||
49 | WL_PROXY_FLAG_ID_DELETED = (1 << 0), | |||
50 | WL_PROXY_FLAG_DESTROYED = (1 << 1) | |||
51 | }; | |||
52 | ||||
53 | struct wl_proxy { | |||
54 | struct wl_object object; | |||
55 | struct wl_display *display; | |||
56 | struct wl_event_queue *queue; | |||
57 | uint32_t flags; | |||
58 | int refcount; | |||
59 | void *user_data; | |||
60 | }; | |||
61 | ||||
62 | struct wl_global { | |||
63 | uint32_t id; | |||
64 | char *interface; | |||
65 | uint32_t version; | |||
66 | struct wl_list link; | |||
67 | }; | |||
68 | ||||
69 | struct wl_event_queue { | |||
70 | struct wl_list link; | |||
71 | struct wl_list event_list; | |||
72 | struct wl_display *display; | |||
73 | pthread_cond_t cond; | |||
74 | }; | |||
75 | ||||
76 | struct wl_display { | |||
77 | struct wl_proxy proxy; | |||
78 | struct wl_connection *connection; | |||
79 | int last_error; | |||
80 | int fd; | |||
81 | pthread_t display_thread; | |||
82 | struct wl_map objects; | |||
83 | struct wl_event_queue queue; | |||
84 | struct wl_list event_queue_list; | |||
85 | pthread_mutex_t mutex; | |||
86 | }; | |||
87 | ||||
88 | /** \endcond */ | |||
89 | ||||
90 | static int wl_debug = 0; | |||
91 | ||||
92 | static void | |||
93 | display_fatal_error(struct wl_display *display, int error) | |||
94 | { | |||
95 | struct wl_event_queue *iter; | |||
96 | ||||
97 | if (display->last_error) | |||
98 | return; | |||
99 | ||||
100 | if (!error) | |||
101 | error = 1; | |||
102 | ||||
103 | display->last_error = error; | |||
104 | close(display->fd); | |||
105 | display->fd = -1; | |||
106 | ||||
107 | wl_list_for_each(iter, &display->event_queue_list, link)for (iter = 0, iter = (__typeof__(iter))((char *)((&display ->event_queue_list)->next) - ((char *)&(iter)->link - (char *)(iter))); &iter->link != (&display-> event_queue_list); iter = (__typeof__(iter))((char *)(iter-> link.next) - ((char *)&(iter)->link - (char *)(iter))) ) | |||
108 | pthread_cond_broadcast(&iter->cond); | |||
109 | } | |||
110 | ||||
111 | static void | |||
112 | wl_display_fatal_error(struct wl_display *display, int error) | |||
113 | { | |||
114 | pthread_mutex_lock(&display->mutex); | |||
115 | display_fatal_error(display, error); | |||
116 | pthread_mutex_unlock(&display->mutex); | |||
117 | } | |||
118 | ||||
119 | static void | |||
120 | wl_event_queue_init(struct wl_event_queue *queue, struct wl_display *display) | |||
121 | { | |||
122 | wl_list_init(&queue->event_list); | |||
123 | pthread_cond_init(&queue->cond, NULL((void*)0)); | |||
124 | queue->display = display; | |||
125 | } | |||
126 | ||||
127 | static void | |||
128 | wl_event_queue_release(struct wl_event_queue *queue) | |||
129 | { | |||
130 | struct wl_closure *closure; | |||
131 | ||||
132 | while (!wl_list_empty(&queue->event_list)) { | |||
133 | closure = container_of(queue->event_list.next,({ const __typeof__( ((struct wl_closure *)0)->link ) *__mptr = (queue->event_list.next); (struct wl_closure *)( (char * )__mptr - __builtin_offsetof(struct wl_closure, link) );}) | |||
134 | struct wl_closure, link)({ const __typeof__( ((struct wl_closure *)0)->link ) *__mptr = (queue->event_list.next); (struct wl_closure *)( (char * )__mptr - __builtin_offsetof(struct wl_closure, link) );}); | |||
135 | wl_list_remove(&closure->link); | |||
136 | wl_closure_destroy(closure); | |||
137 | } | |||
138 | pthread_cond_destroy(&queue->cond); | |||
139 | } | |||
140 | ||||
141 | /** Destroy an event queue | |||
142 | * | |||
143 | * \param queue The event queue to be destroyed | |||
144 | * | |||
145 | * Destroy the given event queue. Any pending event on that queue is | |||
146 | * discarded. | |||
147 | * | |||
148 | * The \ref wl_display object used to create the queue should not be | |||
149 | * destroyed until all event queues created with it are destroyed with | |||
150 | * this function. | |||
151 | * | |||
152 | * \memberof wl_event_queue | |||
153 | */ | |||
154 | WL_EXPORT__attribute__ ((visibility("default"))) void | |||
155 | wl_event_queue_destroy(struct wl_event_queue *queue) | |||
156 | { | |||
157 | struct wl_display *display = queue->display; | |||
158 | ||||
159 | pthread_mutex_lock(&display->mutex); | |||
160 | wl_list_remove(&queue->link); | |||
161 | wl_event_queue_release(queue); | |||
162 | free(queue); | |||
163 | pthread_mutex_unlock(&display->mutex); | |||
164 | } | |||
165 | ||||
166 | /** Create a new event queue for this display | |||
167 | * | |||
168 | * \param display The display context object | |||
169 | * \return A new event queue associated with this display or NULL on | |||
170 | * failure. | |||
171 | * | |||
172 | * \memberof wl_display | |||
173 | */ | |||
174 | WL_EXPORT__attribute__ ((visibility("default"))) struct wl_event_queue * | |||
175 | wl_display_create_queue(struct wl_display *display) | |||
176 | { | |||
177 | struct wl_event_queue *queue; | |||
178 | ||||
179 | queue = malloc(sizeof *queue); | |||
180 | if (queue == NULL((void*)0)) | |||
181 | return NULL((void*)0); | |||
182 | ||||
183 | wl_event_queue_init(queue, display); | |||
184 | ||||
185 | pthread_mutex_lock(&display->mutex); | |||
186 | wl_list_insert(&display->event_queue_list, &queue->link); | |||
187 | pthread_mutex_unlock(&display->mutex); | |||
188 | ||||
189 | return queue; | |||
190 | } | |||
191 | ||||
192 | /** Create a proxy object with a given interface | |||
193 | * | |||
194 | * \param factory Factory proxy object | |||
195 | * \param interface Interface the proxy object should use | |||
196 | * \return A newly allocated proxy object or NULL on failure | |||
197 | * | |||
198 | * This function creates a new proxy object with the supplied interface. The | |||
199 | * proxy object will have an id assigned from the client id space. The id | |||
200 | * should be created on the compositor side by sending an appropriate request | |||
201 | * with \ref wl_proxy_marshal(). | |||
202 | * | |||
203 | * The proxy will inherit the display and event queue of the factory object. | |||
204 | * | |||
205 | * \note This should not normally be used by non-generated code. | |||
206 | * | |||
207 | * \sa wl_display, wl_event_queue, wl_proxy_marshal() | |||
208 | * | |||
209 | * \memberof wl_proxy | |||
210 | */ | |||
211 | WL_EXPORT__attribute__ ((visibility("default"))) struct wl_proxy * | |||
212 | wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface) | |||
213 | { | |||
214 | struct wl_proxy *proxy; | |||
215 | struct wl_display *display = factory->display; | |||
216 | ||||
217 | proxy = malloc(sizeof *proxy); | |||
218 | if (proxy == NULL((void*)0)) | |||
219 | return NULL((void*)0); | |||
220 | ||||
221 | proxy->object.interface = interface; | |||
222 | proxy->object.implementation = NULL((void*)0); | |||
223 | proxy->display = display; | |||
224 | proxy->queue = factory->queue; | |||
225 | proxy->flags = 0; | |||
226 | proxy->refcount = 1; | |||
227 | ||||
228 | pthread_mutex_lock(&display->mutex); | |||
229 | proxy->object.id = wl_map_insert_new(&display->objects, | |||
230 | WL_MAP_CLIENT_SIDE1, proxy); | |||
231 | pthread_mutex_unlock(&display->mutex); | |||
232 | ||||
233 | return proxy; | |||
234 | } | |||
235 | ||||
236 | /* The caller should hold the display lock */ | |||
237 | static struct wl_proxy * | |||
238 | wl_proxy_create_for_id(struct wl_proxy *factory, | |||
239 | uint32_t id, const struct wl_interface *interface) | |||
240 | { | |||
241 | struct wl_proxy *proxy; | |||
242 | struct wl_display *display = factory->display; | |||
243 | ||||
244 | proxy = malloc(sizeof *proxy); | |||
245 | if (proxy == NULL((void*)0)) | |||
246 | return NULL((void*)0); | |||
247 | ||||
248 | proxy->object.interface = interface; | |||
249 | proxy->object.implementation = NULL((void*)0); | |||
250 | proxy->object.id = id; | |||
251 | proxy->display = display; | |||
252 | proxy->queue = factory->queue; | |||
253 | proxy->flags = 0; | |||
254 | proxy->refcount = 1; | |||
255 | ||||
256 | wl_map_insert_at(&display->objects, id, proxy); | |||
257 | ||||
258 | return proxy; | |||
259 | } | |||
260 | ||||
261 | /** Destroy a proxy object | |||
262 | * | |||
263 | * \param proxy The proxy to be destroyed | |||
264 | * | |||
265 | * \memberof wl_proxy | |||
266 | */ | |||
267 | WL_EXPORT__attribute__ ((visibility("default"))) void | |||
268 | wl_proxy_destroy(struct wl_proxy *proxy) | |||
269 | { | |||
270 | struct wl_display *display = proxy->display; | |||
271 | ||||
272 | pthread_mutex_lock(&display->mutex); | |||
273 | ||||
274 | if (proxy->flags & WL_PROXY_FLAG_ID_DELETED) | |||
275 | wl_map_remove(&proxy->display->objects, proxy->object.id); | |||
276 | else if (proxy->object.id < WL_SERVER_ID_START0xff000000) | |||
277 | wl_map_insert_at(&proxy->display->objects, | |||
278 | proxy->object.id, WL_ZOMBIE_OBJECT((void *) 2)); | |||
279 | else | |||
280 | wl_map_insert_at(&proxy->display->objects, | |||
281 | proxy->object.id, NULL((void*)0)); | |||
282 | ||||
283 | ||||
284 | proxy->flags |= WL_PROXY_FLAG_DESTROYED; | |||
285 | ||||
286 | proxy->refcount--; | |||
287 | if (!proxy->refcount) | |||
288 | free(proxy); | |||
289 | ||||
290 | pthread_mutex_unlock(&display->mutex); | |||
291 | } | |||
292 | ||||
293 | /** Set a proxy's listener | |||
294 | * | |||
295 | * \param proxy The proxy object | |||
296 | * \param implementation The listener to be added to proxy | |||
297 | * \param data User data to be associated with the proxy | |||
298 | * \return 0 on success or -1 on failure | |||
299 | * | |||
300 | * Set proxy's listener to \c implementation and its user data to | |||
301 | * \c data. If a listener has already been set, this function | |||
302 | * fails and nothing is changed. | |||
303 | * | |||
304 | * \c implementation is a vector of function pointers. For an opcode | |||
305 | * \c n, \c implementation[n] should point to the handler of \c n for | |||
306 | * the given object. | |||
307 | * | |||
308 | * \memberof wl_proxy | |||
309 | */ | |||
310 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
311 | wl_proxy_add_listener(struct wl_proxy *proxy, | |||
312 | void (**implementation)(void), void *data) | |||
313 | { | |||
314 | if (proxy->object.implementation) { | |||
315 | fprintf(stderrstderr, "proxy already has listener\n"); | |||
316 | return -1; | |||
317 | } | |||
318 | ||||
319 | proxy->object.implementation = implementation; | |||
320 | proxy->user_data = data; | |||
321 | ||||
322 | return 0; | |||
323 | } | |||
324 | ||||
325 | /** Prepare a request to be sent to the compositor | |||
326 | * | |||
327 | * \param proxy The proxy object | |||
328 | * \param opcode Opcode of the request to be sent | |||
329 | * \param ... Extra arguments for the given request | |||
330 | * | |||
331 | * Translates the request given by opcode and the extra arguments into the | |||
332 | * wire format and write it to the connection buffer. | |||
333 | * | |||
334 | * The example below creates a proxy object with the wl_surface_interface | |||
335 | * using a wl_compositor factory interface and sends the | |||
336 | * \c compositor.create_surface request using \ref wl_proxy_marshal(). Note | |||
337 | * the \c id is the extra argument to the request as specified by the | |||
338 | * protocol. | |||
339 | * | |||
340 | * \code | |||
341 | * id = wl_proxy_create((struct wl_proxy *) wl_compositor, | |||
342 | * &wl_surface_interface); | |||
343 | * wl_proxy_marshal((struct wl_proxy *) wl_compositor, | |||
344 | * WL_COMPOSITOR_CREATE_SURFACE, id); | |||
345 | * \endcode | |||
346 | * | |||
347 | * \note This should not normally be used by non-generated code. | |||
348 | * | |||
349 | * \sa wl_proxy_create() | |||
350 | * | |||
351 | * \memberof wl_proxy | |||
352 | */ | |||
353 | WL_EXPORT__attribute__ ((visibility("default"))) void | |||
354 | wl_proxy_marshal(struct wl_proxy *proxy, uint32_t opcode, ...) | |||
355 | { | |||
356 | struct wl_closure *closure; | |||
357 | va_list ap; | |||
358 | ||||
359 | pthread_mutex_lock(&proxy->display->mutex); | |||
360 | ||||
361 | va_start(ap, opcode)__builtin_va_start(ap, opcode); | |||
362 | closure = wl_closure_vmarshal(&proxy->object, opcode, ap, | |||
363 | &proxy->object.interface->methods[opcode]); | |||
364 | va_end(ap)__builtin_va_end(ap); | |||
365 | ||||
366 | if (closure == NULL((void*)0)) { | |||
367 | fprintf(stderrstderr, "Error marshalling request\n"); | |||
368 | abort(); | |||
369 | } | |||
370 | ||||
371 | if (wl_debug) | |||
372 | wl_closure_print(closure, &proxy->object, true1); | |||
373 | ||||
374 | if (wl_closure_send(closure, proxy->display->connection)) { | |||
375 | fprintf(stderrstderr, "Error sending request: %m\n"); | |||
376 | abort(); | |||
377 | } | |||
378 | ||||
379 | wl_closure_destroy(closure); | |||
380 | ||||
381 | pthread_mutex_unlock(&proxy->display->mutex); | |||
382 | } | |||
383 | ||||
384 | static void | |||
385 | display_handle_error(void *data, | |||
386 | struct wl_display *display, struct wl_object *object, | |||
387 | uint32_t code, const char *message) | |||
388 | { | |||
389 | int err; | |||
390 | ||||
391 | wl_log("%s@%u: error %d: %s\n", | |||
392 | object->interface->name, object->id, code, message); | |||
393 | ||||
394 | switch (code) { | |||
395 | case WL_DISPLAY_ERROR_INVALID_OBJECT: | |||
396 | case WL_DISPLAY_ERROR_INVALID_METHOD: | |||
397 | err = -EINVAL22; | |||
398 | break; | |||
399 | case WL_DISPLAY_ERROR_NO_MEMORY: | |||
400 | err = -ENOMEM12; | |||
401 | break; | |||
402 | default: | |||
403 | err = -EFAULT14; | |||
404 | break; | |||
405 | } | |||
406 | ||||
407 | wl_display_fatal_error(display, err); | |||
408 | } | |||
409 | ||||
410 | static void | |||
411 | display_handle_delete_id(void *data, struct wl_display *display, uint32_t id) | |||
412 | { | |||
413 | struct wl_proxy *proxy; | |||
414 | ||||
415 | pthread_mutex_lock(&display->mutex); | |||
416 | ||||
417 | proxy = wl_map_lookup(&display->objects, id); | |||
418 | if (proxy != WL_ZOMBIE_OBJECT((void *) 2)) | |||
419 | proxy->flags |= WL_PROXY_FLAG_ID_DELETED; | |||
420 | else | |||
421 | wl_map_remove(&display->objects, id); | |||
422 | ||||
423 | pthread_mutex_unlock(&display->mutex); | |||
424 | } | |||
425 | ||||
426 | static const struct wl_display_listener display_listener = { | |||
427 | display_handle_error, | |||
428 | display_handle_delete_id | |||
429 | }; | |||
430 | ||||
431 | static int | |||
432 | connect_to_socket(const char *name) | |||
433 | { | |||
434 | struct sockaddr_un addr; | |||
435 | socklen_t size; | |||
436 | const char *runtime_dir; | |||
437 | int name_size, fd; | |||
438 | ||||
439 | runtime_dir = getenv("XDG_RUNTIME_DIR"); | |||
440 | if (!runtime_dir) { | |||
441 | fprintf(stderrstderr, | |||
442 | "error: XDG_RUNTIME_DIR not set in the environment.\n"); | |||
443 | ||||
444 | /* to prevent programs reporting | |||
445 | * "failed to create display: Success" */ | |||
446 | errno(*__errno_location ()) = ENOENT2; | |||
447 | return -1; | |||
448 | } | |||
449 | ||||
450 | if (name == NULL((void*)0)) | |||
451 | name = getenv("WAYLAND_DISPLAY"); | |||
452 | if (name == NULL((void*)0)) | |||
453 | name = "wayland-0"; | |||
454 | ||||
455 | fd = wl_os_socket_cloexec(PF_LOCAL1, SOCK_STREAMSOCK_STREAM, 0); | |||
456 | if (fd < 0) | |||
457 | return -1; | |||
458 | ||||
459 | memset(&addr, 0, sizeof addr); | |||
460 | addr.sun_family = AF_LOCAL1; | |||
461 | name_size = | |||
462 | snprintf(addr.sun_path, sizeof addr.sun_path, | |||
463 | "%s/%s", runtime_dir, name) + 1; | |||
464 | ||||
465 | assert(name_size > 0)((name_size > 0) ? (void) (0) : __assert_fail ("name_size > 0" , "wayland-client.c", 465, __PRETTY_FUNCTION__)); | |||
466 | if (name_size > (int)sizeof addr.sun_path) { | |||
467 | fprintf(stderrstderr, | |||
468 | "error: socket path \"%s/%s\" plus null terminator" | |||
469 | " exceeds 108 bytes\n", runtime_dir, name); | |||
470 | close(fd); | |||
471 | /* to prevent programs reporting | |||
472 | * "failed to add socket: Success" */ | |||
473 | errno(*__errno_location ()) = ENAMETOOLONG36; | |||
474 | return -1; | |||
475 | }; | |||
476 | ||||
477 | size = offsetof (struct sockaddr_un, sun_path)__builtin_offsetof(struct sockaddr_un, sun_path) + name_size; | |||
478 | ||||
479 | if (connect(fd, (struct sockaddr *) &addr, size) < 0) { | |||
480 | close(fd); | |||
481 | return -1; | |||
482 | } | |||
483 | ||||
484 | return fd; | |||
485 | } | |||
486 | ||||
487 | /** Connect to Wayland display on an already open fd | |||
488 | * | |||
489 | * \param fd The fd to use for the connection | |||
490 | * \return A \ref wl_display object or \c NULL on failure | |||
491 | * | |||
492 | * The wl_display takes ownership of the fd and will close it when the | |||
493 | * display is destroyed. The fd will also be closed in case of | |||
494 | * failure. | |||
495 | * | |||
496 | * \memberof wl_display | |||
497 | */ | |||
498 | WL_EXPORT__attribute__ ((visibility("default"))) struct wl_display * | |||
499 | wl_display_connect_to_fd(int fd) | |||
500 | { | |||
501 | struct wl_display *display; | |||
502 | const char *debug; | |||
503 | ||||
504 | debug = getenv("WAYLAND_DEBUG"); | |||
505 | if (debug && (strstr(debug, "client") || strstr(debug, "1"))) | |||
506 | wl_debug = 1; | |||
507 | ||||
508 | display = malloc(sizeof *display); | |||
509 | if (display == NULL((void*)0)) { | |||
510 | close(fd); | |||
511 | return NULL((void*)0); | |||
512 | } | |||
513 | ||||
514 | memset(display, 0, sizeof *display); | |||
515 | ||||
516 | display->fd = fd; | |||
517 | wl_map_init(&display->objects); | |||
518 | wl_event_queue_init(&display->queue, display); | |||
519 | wl_list_init(&display->event_queue_list); | |||
520 | pthread_mutex_init(&display->mutex, NULL((void*)0)); | |||
521 | ||||
522 | wl_map_insert_new(&display->objects, WL_MAP_CLIENT_SIDE1, NULL((void*)0)); | |||
523 | ||||
524 | display->proxy.object.interface = &wl_display_interface; | |||
525 | display->proxy.object.id = | |||
526 | wl_map_insert_new(&display->objects, | |||
527 | WL_MAP_CLIENT_SIDE1, display); | |||
528 | display->proxy.display = display; | |||
529 | display->proxy.object.implementation = (void(**)(void)) &display_listener; | |||
530 | display->proxy.user_data = display; | |||
531 | display->proxy.queue = &display->queue; | |||
532 | display->proxy.flags = 0; | |||
533 | display->proxy.refcount = 1; | |||
534 | ||||
535 | display->connection = wl_connection_create(display->fd); | |||
536 | if (display->connection == NULL((void*)0)) { | |||
537 | wl_map_release(&display->objects); | |||
538 | close(display->fd); | |||
539 | free(display); | |||
540 | return NULL((void*)0); | |||
541 | } | |||
542 | ||||
543 | return display; | |||
544 | } | |||
545 | ||||
546 | /** Connect to a Wayland display | |||
547 | * | |||
548 | * \param name Name of the Wayland display to connect to | |||
549 | * \return A \ref wl_display object or \c NULL on failure | |||
550 | * | |||
551 | * Connect to the Wayland display named \c name. If \c name is \c NULL, | |||
552 | * its value will bee replaced with the WAYLAND_DISPLAY environment | |||
553 | * variable if it is set, otherwise display "wayland-0" will be used. | |||
554 | * | |||
555 | * \memberof wl_display | |||
556 | */ | |||
557 | WL_EXPORT__attribute__ ((visibility("default"))) struct wl_display * | |||
558 | wl_display_connect(const char *name) | |||
559 | { | |||
560 | char *connection, *end; | |||
561 | int flags, fd; | |||
562 | ||||
563 | connection = getenv("WAYLAND_SOCKET"); | |||
564 | if (connection) { | |||
565 | fd = strtol(connection, &end, 0); | |||
566 | if (*end != '\0') | |||
567 | return NULL((void*)0); | |||
568 | ||||
569 | flags = fcntl(fd, F_GETFD1); | |||
570 | if (flags != -1) | |||
571 | fcntl(fd, F_SETFD2, flags | FD_CLOEXEC1); | |||
572 | unsetenv("WAYLAND_SOCKET"); | |||
573 | } else { | |||
574 | fd = connect_to_socket(name); | |||
575 | if (fd < 0) | |||
576 | return NULL((void*)0); | |||
577 | } | |||
578 | ||||
579 | return wl_display_connect_to_fd(fd); | |||
580 | } | |||
581 | ||||
582 | /** Close a connection to a Wayland display | |||
583 | * | |||
584 | * \param display The display context object | |||
585 | * | |||
586 | * Close the connection to \c display and free all resources associated | |||
587 | * with it. | |||
588 | * | |||
589 | * \memberof wl_display | |||
590 | */ | |||
591 | WL_EXPORT__attribute__ ((visibility("default"))) void | |||
592 | wl_display_disconnect(struct wl_display *display) | |||
593 | { | |||
594 | wl_connection_destroy(display->connection); | |||
595 | wl_map_release(&display->objects); | |||
596 | wl_event_queue_release(&display->queue); | |||
597 | pthread_mutex_destroy(&display->mutex); | |||
598 | if (display->fd > 0) | |||
599 | close(display->fd); | |||
600 | ||||
601 | free(display); | |||
602 | } | |||
603 | ||||
604 | /** Get a display context's file descriptor | |||
605 | * | |||
606 | * \param display The display context object | |||
607 | * \return Display object file descriptor | |||
608 | * | |||
609 | * Return the file descriptor associated with a display so it can be | |||
610 | * integrated into the client's main loop. | |||
611 | * | |||
612 | * \memberof wl_display | |||
613 | */ | |||
614 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
615 | wl_display_get_fd(struct wl_display *display) | |||
616 | { | |||
617 | return display->fd; | |||
618 | } | |||
619 | ||||
620 | static void | |||
621 | sync_callback(void *data, struct wl_callback *callback, uint32_t serial) | |||
622 | { | |||
623 | int *done = data; | |||
624 | ||||
625 | *done = 1; | |||
626 | wl_callback_destroy(callback); | |||
627 | } | |||
628 | ||||
629 | static const struct wl_callback_listener sync_listener = { | |||
630 | sync_callback | |||
631 | }; | |||
632 | ||||
633 | /** Block until all pending request are processed by the server | |||
634 | * | |||
635 | * \param display The display context object | |||
636 | * \return The number of dispatched events on success or -1 on failure | |||
637 | * | |||
638 | * Blocks until the server process all currently issued requests and | |||
639 | * sends out pending events on all event queues. | |||
640 | * | |||
641 | * \memberof wl_display | |||
642 | */ | |||
643 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
644 | wl_display_roundtrip(struct wl_display *display) | |||
645 | { | |||
646 | struct wl_callback *callback; | |||
647 | int done, ret = 0; | |||
648 | ||||
649 | done = 0; | |||
650 | callback = wl_display_sync(display); | |||
651 | wl_callback_add_listener(callback, &sync_listener, &done); | |||
652 | while (!done && ret >= 0) | |||
653 | ret = wl_display_dispatch(display); | |||
654 | ||||
655 | if (ret == -1 && !done) | |||
656 | wl_callback_destroy(callback); | |||
657 | ||||
658 | return ret; | |||
659 | } | |||
660 | ||||
661 | static int | |||
662 | create_proxies(struct wl_proxy *sender, struct wl_closure *closure) | |||
663 | { | |||
664 | struct wl_proxy *proxy; | |||
665 | const char *signature; | |||
666 | struct argument_details arg; | |||
667 | uint32_t id; | |||
668 | int i; | |||
669 | int count; | |||
670 | ||||
671 | signature = closure->message->signature; | |||
672 | count = arg_count_for_signature(signature) + 2; | |||
673 | for (i = 2; i < count; i++) { | |||
674 | signature = get_next_argument(signature, &arg); | |||
675 | switch (arg.type) { | |||
676 | case 'n': | |||
677 | id = **(uint32_t **) closure->args[i]; | |||
678 | if (id == 0) { | |||
679 | *(void **) closure->args[i] = NULL((void*)0); | |||
680 | break; | |||
681 | } | |||
682 | proxy = wl_proxy_create_for_id(sender, id, | |||
683 | closure->message->types[i - 2]); | |||
684 | if (proxy == NULL((void*)0)) | |||
685 | return -1; | |||
686 | *(void **) closure->args[i] = proxy; | |||
687 | break; | |||
688 | default: | |||
689 | break; | |||
690 | } | |||
691 | } | |||
692 | ||||
693 | return 0; | |||
694 | } | |||
695 | ||||
696 | static void | |||
697 | increase_closure_args_refcount(struct wl_closure *closure) | |||
698 | { | |||
699 | const char *signature; | |||
700 | struct argument_details arg; | |||
701 | int i, count; | |||
702 | struct wl_proxy *proxy; | |||
703 | ||||
704 | signature = closure->message->signature; | |||
705 | count = arg_count_for_signature(signature) + 2; | |||
706 | for (i = 2; i < count; i++) { | |||
707 | signature = get_next_argument(signature, &arg); | |||
708 | switch (arg.type) { | |||
709 | case 'n': | |||
710 | case 'o': | |||
711 | proxy = *(struct wl_proxy **) closure->args[i]; | |||
712 | if (proxy) | |||
713 | proxy->refcount++; | |||
714 | break; | |||
715 | default: | |||
716 | break; | |||
717 | } | |||
718 | } | |||
719 | } | |||
720 | ||||
721 | static int | |||
722 | queue_event(struct wl_display *display, int len) | |||
723 | { | |||
724 | uint32_t p[2], id; | |||
725 | int opcode, size; | |||
726 | struct wl_proxy *proxy; | |||
727 | struct wl_closure *closure; | |||
728 | const struct wl_message *message; | |||
729 | ||||
730 | wl_connection_copy(display->connection, p, sizeof p); | |||
731 | id = p[0]; | |||
732 | opcode = p[1] & 0xffff; | |||
733 | size = p[1] >> 16; | |||
734 | if (len < size) | |||
735 | return 0; | |||
736 | ||||
737 | proxy = wl_map_lookup(&display->objects, id); | |||
738 | if (proxy == WL_ZOMBIE_OBJECT((void *) 2)) { | |||
739 | wl_connection_consume(display->connection, size); | |||
740 | return size; | |||
741 | } else if (proxy == NULL((void*)0)) { | |||
742 | wl_connection_consume(display->connection, size); | |||
743 | return size; | |||
744 | } | |||
745 | ||||
746 | message = &proxy->object.interface->events[opcode]; | |||
747 | closure = wl_connection_demarshal(display->connection, size, | |||
748 | &display->objects, message); | |||
749 | if (!closure) | |||
750 | return -1; | |||
751 | ||||
752 | if (create_proxies(proxy, closure) < 0) { | |||
753 | wl_closure_destroy(closure); | |||
754 | return -1; | |||
755 | } | |||
756 | ||||
757 | if (wl_closure_lookup_objects(closure, &display->objects) != 0) { | |||
758 | wl_closure_destroy(closure); | |||
759 | return -1; | |||
760 | } | |||
761 | ||||
762 | increase_closure_args_refcount(closure); | |||
763 | proxy->refcount++; | |||
764 | closure->proxy = proxy; | |||
765 | ||||
766 | if (wl_list_empty(&proxy->queue->event_list)) | |||
767 | pthread_cond_signal(&proxy->queue->cond); | |||
768 | wl_list_insert(proxy->queue->event_list.prev, &closure->link); | |||
769 | ||||
770 | return size; | |||
771 | } | |||
772 | ||||
773 | static void | |||
774 | decrease_closure_args_refcount(struct wl_closure *closure) | |||
775 | { | |||
776 | const char *signature; | |||
777 | struct argument_details arg; | |||
778 | int i, count; | |||
779 | struct wl_proxy *proxy; | |||
780 | ||||
781 | signature = closure->message->signature; | |||
782 | count = arg_count_for_signature(signature) + 2; | |||
783 | for (i = 2; i < count; i++) { | |||
784 | signature = get_next_argument(signature, &arg); | |||
785 | switch (arg.type) { | |||
786 | case 'n': | |||
787 | case 'o': | |||
788 | proxy = *(struct wl_proxy **) closure->args[i]; | |||
789 | if (proxy) { | |||
790 | if (proxy->flags & WL_PROXY_FLAG_DESTROYED) | |||
791 | *(void **) closure->args[i] = NULL((void*)0); | |||
792 | ||||
793 | proxy->refcount--; | |||
794 | if (!proxy->refcount) | |||
795 | free(proxy); | |||
796 | } | |||
797 | break; | |||
798 | default: | |||
799 | break; | |||
800 | } | |||
801 | } | |||
802 | } | |||
803 | ||||
804 | static void | |||
805 | dispatch_event(struct wl_display *display, struct wl_event_queue *queue) | |||
806 | { | |||
807 | struct wl_closure *closure; | |||
808 | struct wl_proxy *proxy; | |||
809 | int opcode; | |||
810 | bool_Bool proxy_destroyed; | |||
811 | ||||
812 | closure = container_of(queue->event_list.next,({ const __typeof__( ((struct wl_closure *)0)->link ) *__mptr = (queue->event_list.next); (struct wl_closure *)( (char * )__mptr - __builtin_offsetof(struct wl_closure, link) );}) | |||
813 | struct wl_closure, link)({ const __typeof__( ((struct wl_closure *)0)->link ) *__mptr = (queue->event_list.next); (struct wl_closure *)( (char * )__mptr - __builtin_offsetof(struct wl_closure, link) );}); | |||
814 | wl_list_remove(&closure->link); | |||
815 | opcode = closure->buffer[1] & 0xffff; | |||
816 | ||||
817 | /* Verify that the receiving object is still valid by checking if has | |||
818 | * been destroyed by the application. */ | |||
819 | ||||
820 | decrease_closure_args_refcount(closure); | |||
821 | proxy = closure->proxy; | |||
822 | proxy_destroyed = !!(proxy->flags & WL_PROXY_FLAG_DESTROYED); | |||
823 | ||||
824 | proxy->refcount--; | |||
825 | if (!proxy->refcount) | |||
826 | free(proxy); | |||
827 | ||||
828 | if (proxy_destroyed) { | |||
829 | wl_closure_destroy(closure); | |||
830 | return; | |||
831 | } | |||
832 | ||||
833 | pthread_mutex_unlock(&display->mutex); | |||
834 | ||||
835 | if (proxy->object.implementation) { | |||
| ||||
836 | if (wl_debug) | |||
837 | wl_closure_print(closure, &proxy->object, false0); | |||
838 | ||||
839 | wl_closure_invoke(closure, &proxy->object, | |||
840 | proxy->object.implementation[opcode], | |||
841 | proxy->user_data); | |||
842 | } | |||
843 | ||||
844 | wl_closure_destroy(closure); | |||
845 | ||||
846 | pthread_mutex_lock(&display->mutex); | |||
847 | } | |||
848 | ||||
849 | static int | |||
850 | dispatch_queue(struct wl_display *display, | |||
851 | struct wl_event_queue *queue, int block) | |||
852 | { | |||
853 | int len, size, count, ret; | |||
854 | ||||
855 | pthread_mutex_lock(&display->mutex); | |||
856 | ||||
857 | if (display->last_error) | |||
858 | goto err_unlock; | |||
859 | ||||
860 | ret = wl_connection_flush(display->connection); | |||
861 | if (ret < 0 && errno(*__errno_location ()) != EAGAIN11) { | |||
862 | display_fatal_error(display, errno(*__errno_location ())); | |||
863 | goto err_unlock; | |||
864 | } | |||
865 | ||||
866 | if (block && wl_list_empty(&queue->event_list) && | |||
867 | pthread_equal(display->display_thread, pthread_self())) { | |||
868 | len = wl_connection_read(display->connection); | |||
869 | if (len == -1) { | |||
870 | display_fatal_error(display, errno(*__errno_location ())); | |||
871 | goto err_unlock; | |||
872 | } else if (len == 0) { | |||
873 | display_fatal_error(display, EPIPE32); | |||
874 | goto err_unlock; | |||
875 | } | |||
876 | while (len >= 8) { | |||
877 | size = queue_event(display, len); | |||
878 | if (size == -1) { | |||
879 | display_fatal_error(display, errno(*__errno_location ())); | |||
880 | goto err_unlock; | |||
881 | } else if (size == 0) { | |||
882 | break; | |||
883 | } | |||
884 | len -= size; | |||
885 | } | |||
886 | } else if (block && wl_list_empty(&queue->event_list)) { | |||
887 | pthread_cond_wait(&queue->cond, &display->mutex); | |||
888 | if (display->last_error) | |||
889 | goto err_unlock; | |||
890 | } | |||
891 | ||||
892 | for (count = 0; !wl_list_empty(&queue->event_list); count++) { | |||
893 | dispatch_event(display, queue); | |||
894 | if (display->last_error) | |||
895 | goto err_unlock; | |||
896 | } | |||
897 | ||||
898 | pthread_mutex_unlock(&display->mutex); | |||
899 | ||||
900 | return count; | |||
901 | ||||
902 | err_unlock: | |||
903 | errno(*__errno_location ()) = display->last_error; | |||
904 | pthread_mutex_unlock(&display->mutex); | |||
905 | ||||
906 | return -1; | |||
907 | } | |||
908 | ||||
909 | /** Dispatch events in an event queue | |||
910 | * | |||
911 | * \param display The display context object | |||
912 | * \param queue The event queue to dispatch | |||
913 | * \return The number of dispatched events on success or -1 on failure | |||
914 | * | |||
915 | * Dispatch all incoming events for objects assigned to the given | |||
916 | * event queue. On failure -1 is returned and errno set appropriately. | |||
917 | * | |||
918 | * This function blocks if there are no events to dispatch. If calling from | |||
919 | * the main thread, it will block reading data from the display fd. For other | |||
920 | * threads this will block until the main thread queues events on the queue | |||
921 | * passed as argument. | |||
922 | * | |||
923 | * \memberof wl_display | |||
924 | */ | |||
925 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
926 | wl_display_dispatch_queue(struct wl_display *display, | |||
927 | struct wl_event_queue *queue) | |||
928 | { | |||
929 | return dispatch_queue(display, queue, 1); | |||
930 | } | |||
931 | ||||
932 | /** Dispatch pending events in an event queue | |||
933 | * | |||
934 | * \param display The display context object | |||
935 | * \param queue The event queue to dispatch | |||
936 | * \return The number of dispatched events on success or -1 on failure | |||
937 | * | |||
938 | * Dispatch all incoming events for objects assigned to the given | |||
939 | * event queue. On failure -1 is returned and errno set appropriately. | |||
940 | * If there are no events queued, this functions return immediately. | |||
941 | * | |||
942 | * \memberof wl_display | |||
943 | * \since 1.0.2 | |||
944 | */ | |||
945 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
946 | wl_display_dispatch_queue_pending(struct wl_display *display, | |||
947 | struct wl_event_queue *queue) | |||
948 | { | |||
949 | return dispatch_queue(display, queue, 0); | |||
950 | } | |||
951 | ||||
952 | /** Process incoming events | |||
953 | * | |||
954 | * \param display The display context object | |||
955 | * \return The number of dispatched events on success or -1 on failure | |||
956 | * | |||
957 | * Dispatch the display's main event queue. | |||
958 | * | |||
959 | * If the main event queue is empty, this function blocks until there are | |||
960 | * events to be read from the display fd. Events are read and queued on | |||
961 | * the appropriate event queues. Finally, events on the main event queue | |||
962 | * are dispatched. | |||
963 | * | |||
964 | * \note It is not possible to check if there are events on the main queue | |||
965 | * or not. For dispatching main queue events without blocking, see \ref | |||
966 | * wl_display_dispatch_pending(). | |||
967 | * | |||
968 | * \note Calling this makes the current thread the main one. | |||
969 | * | |||
970 | * \sa wl_display_dispatch_pending(), wl_display_dispatch_queue() | |||
971 | * | |||
972 | * \memberof wl_display | |||
973 | */ | |||
974 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
975 | wl_display_dispatch(struct wl_display *display) | |||
976 | { | |||
977 | display->display_thread = pthread_self(); | |||
978 | ||||
979 | return dispatch_queue(display, &display->queue, 1); | |||
980 | } | |||
981 | ||||
982 | /** Dispatch main queue events without reading from the display fd | |||
983 | * | |||
984 | * \param display The display context object | |||
985 | * \return The number of dispatched events or -1 on failure | |||
986 | * | |||
987 | * This function dispatches events on the main event queue. It does not | |||
988 | * attempt to read the display fd and simply returns zero if the main | |||
989 | * queue is empty, i.e., it doesn't block. | |||
990 | * | |||
991 | * This is necessary when a client's main loop wakes up on some fd other | |||
992 | * than the display fd (network socket, timer fd, etc) and calls \ref | |||
993 | * wl_display_dispatch_queue() from that callback. This may queue up | |||
994 | * events in the main queue while reading all data from the display fd. | |||
995 | * When the main thread returns to the main loop to block, the display fd | |||
996 | * no longer has data, causing a call to \em poll(2) (or similar | |||
997 | * functions) to block indefinitely, even though there are events ready | |||
998 | * to dispatch. | |||
999 | * | |||
1000 | * To proper integrate the wayland display fd into a main loop, the | |||
1001 | * client should always call \ref wl_display_dispatch_pending() and then | |||
1002 | * \ref wl_display_flush() prior to going back to sleep. At that point, | |||
1003 | * the fd typically doesn't have data so attempting I/O could block, but | |||
1004 | * events queued up on the main queue should be dispatched. | |||
1005 | * | |||
1006 | * A real-world example is a main loop that wakes up on a timerfd (or a | |||
1007 | * sound card fd becoming writable, for example in a video player), which | |||
1008 | * then triggers GL rendering and eventually eglSwapBuffers(). | |||
1009 | * eglSwapBuffers() may call wl_display_dispatch_queue() if it didn't | |||
1010 | * receive the frame event for the previous frame, and as such queue | |||
1011 | * events in the main queue. | |||
1012 | * | |||
1013 | * \note Calling this makes the current thread the main one. | |||
1014 | * | |||
1015 | * \sa wl_display_dispatch(), wl_display_dispatch_queue(), | |||
1016 | * wl_display_flush() | |||
1017 | * | |||
1018 | * \memberof wl_display | |||
1019 | */ | |||
1020 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
1021 | wl_display_dispatch_pending(struct wl_display *display) | |||
1022 | { | |||
1023 | display->display_thread = pthread_self(); | |||
1024 | ||||
1025 | return dispatch_queue(display, &display->queue, 0); | |||
| ||||
1026 | } | |||
1027 | ||||
1028 | /** Retrieve the last error occurred on a display | |||
1029 | * | |||
1030 | * \param display The display context object | |||
1031 | * \return The last error occurred on \c display or 0 if no error occurred | |||
1032 | * | |||
1033 | * Return the last error occurred on the display. This may be an error sent | |||
1034 | * by the server or caused by the local client. | |||
1035 | * | |||
1036 | * \note Errors are \b fatal. If this function returns non-zero the display | |||
1037 | * can no longer be used. | |||
1038 | * | |||
1039 | * \memberof wl_display | |||
1040 | */ | |||
1041 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
1042 | wl_display_get_error(struct wl_display *display) | |||
1043 | { | |||
1044 | int ret; | |||
1045 | ||||
1046 | pthread_mutex_lock(&display->mutex); | |||
1047 | ||||
1048 | ret = display->last_error; | |||
1049 | ||||
1050 | pthread_mutex_unlock(&display->mutex); | |||
1051 | ||||
1052 | return ret; | |||
1053 | } | |||
1054 | ||||
1055 | /** Send all buffered request on the display to the server | |||
1056 | * | |||
1057 | * \param display The display context object | |||
1058 | * \return The number of bytes send on success or -1 on failure | |||
1059 | * | |||
1060 | * Send all buffered data on the client side to the server. Clients | |||
1061 | * should call this function before blocking. On success, the number | |||
1062 | * of bytes sent to the server is returned. On failure, this | |||
1063 | * function returns -1 and errno is set appropriately. | |||
1064 | * | |||
1065 | * \memberof wl_display | |||
1066 | */ | |||
1067 | WL_EXPORT__attribute__ ((visibility("default"))) int | |||
1068 | wl_display_flush(struct wl_display *display) | |||
1069 | { | |||
1070 | int ret; | |||
1071 | ||||
1072 | pthread_mutex_lock(&display->mutex); | |||
1073 | ||||
1074 | if (display->last_error) { | |||
1075 | errno(*__errno_location ()) = display->last_error; | |||
1076 | ret = -1; | |||
1077 | } else { | |||
1078 | ret = wl_connection_flush(display->connection); | |||
1079 | if (ret < 0 && errno(*__errno_location ()) != EAGAIN11) | |||
1080 | display_fatal_error(display, errno(*__errno_location ())); | |||
1081 | } | |||
1082 | ||||
1083 | pthread_mutex_unlock(&display->mutex); | |||
1084 | ||||
1085 | return ret; | |||
1086 | } | |||
1087 | ||||
1088 | /** Set the user data associated with a proxy | |||
1089 | * | |||
1090 | * \param proxy The proxy object | |||
1091 | * \param user_data The data to be associated with proxy | |||
1092 | * | |||
1093 | * Set the user data associated with \c proxy. When events for this | |||
1094 | * proxy are received, \c user_data will be supplied to its listener. | |||
1095 | * | |||
1096 | * \memberof wl_proxy | |||
1097 | */ | |||
1098 | WL_EXPORT__attribute__ ((visibility("default"))) void | |||
1099 | wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data) | |||
1100 | { | |||
1101 | proxy->user_data = user_data; | |||
1102 | } | |||
1103 | ||||
1104 | /** Get the user data associated with a proxy | |||
1105 | * | |||
1106 | * \param proxy The proxy object | |||
1107 | * \return The user data associated with proxy | |||
1108 | * | |||
1109 | * \memberof wl_proxy | |||
1110 | */ | |||
1111 | WL_EXPORT__attribute__ ((visibility("default"))) void * | |||
1112 | wl_proxy_get_user_data(struct wl_proxy *proxy) | |||
1113 | { | |||
1114 | return proxy->user_data; | |||
1115 | } | |||
1116 | ||||
1117 | /** Get the id of a proxy object | |||
1118 | * | |||
1119 | * \param proxy The proxy object | |||
1120 | * \return The id the object associated with the proxy | |||
1121 | * | |||
1122 | * \memberof wl_proxy | |||
1123 | */ | |||
1124 | WL_EXPORT__attribute__ ((visibility("default"))) uint32_t | |||
1125 | wl_proxy_get_id(struct wl_proxy *proxy) | |||
1126 | { | |||
1127 | return proxy->object.id; | |||
1128 | } | |||
1129 | ||||
1130 | ||||
1131 | /** Assign a proxy to an event queue | |||
1132 | * | |||
1133 | * \param proxy The proxy object | |||
1134 | * \param queue The event queue that will handle this proxy | |||
1135 | * | |||
1136 | * Assign proxy to event queue. Events coming from \c proxy will be | |||
1137 | * queued in \c queue instead of the display's main queue. | |||
1138 | * | |||
1139 | * \sa wl_display_dispatch_queue() | |||
1140 | * | |||
1141 | * \memberof wl_proxy | |||
1142 | */ | |||
1143 | WL_EXPORT__attribute__ ((visibility("default"))) void | |||
1144 | wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue) | |||
1145 | { | |||
1146 | proxy->queue = queue; | |||
1147 | } | |||
1148 | ||||
1149 | WL_EXPORT__attribute__ ((visibility("default"))) void | |||
1150 | wl_log_set_handler_client(wl_log_func_t handler) | |||
1151 | { | |||
1152 | wl_log_handler = handler; | |||
1153 | } |