1 /*
2 * Copyright © 2022 Red Hat, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23 #include <poll.h>
24 #include <errno.h>
25
26 #include "loader_wayland_helper.h"
27
28 #ifndef HAVE_WL_DISPATCH_QUEUE_TIMEOUT
29 static int
wl_display_poll(struct wl_display * display,short int events,const struct timespec * timeout)30 wl_display_poll(struct wl_display *display,
31 short int events,
32 const struct timespec *timeout)
33 {
34 int ret;
35 struct pollfd pfd[1];
36 struct timespec now;
37 struct timespec deadline = {0};
38 struct timespec result;
39 struct timespec *remaining_timeout = NULL;
40
41 if (timeout) {
42 clock_gettime(CLOCK_MONOTONIC, &now);
43 timespec_add(&deadline, &now, timeout);
44 }
45
46 pfd[0].fd = wl_display_get_fd(display);
47 pfd[0].events = events;
48 do {
49 if (timeout) {
50 clock_gettime(CLOCK_MONOTONIC, &now);
51 timespec_sub_saturate(&result, &deadline, &now);
52 remaining_timeout = &result;
53 }
54 ret = ppoll(pfd, 1, remaining_timeout, NULL);
55 } while (ret == -1 && errno == EINTR);
56
57 return ret;
58 }
59
60 int
wl_display_dispatch_queue_timeout(struct wl_display * display,struct wl_event_queue * queue,const struct timespec * timeout)61 wl_display_dispatch_queue_timeout(struct wl_display *display,
62 struct wl_event_queue *queue,
63 const struct timespec *timeout)
64 {
65 int ret;
66 struct timespec now;
67 struct timespec deadline = {0};
68 struct timespec result;
69 struct timespec *remaining_timeout = NULL;
70
71 if (timeout) {
72 clock_gettime(CLOCK_MONOTONIC, &now);
73 timespec_add(&deadline, &now, timeout);
74 }
75
76 if (wl_display_prepare_read_queue(display, queue) == -1)
77 return wl_display_dispatch_queue_pending(display, queue);
78
79 while (true) {
80 ret = wl_display_flush(display);
81
82 if (ret != -1 || errno != EAGAIN)
83 break;
84
85 if (timeout) {
86 clock_gettime(CLOCK_MONOTONIC, &now);
87 timespec_sub_saturate(&result, &deadline, &now);
88 remaining_timeout = &result;
89 }
90 ret = wl_display_poll(display, POLLOUT, remaining_timeout);
91
92 if (ret <= 0) {
93 wl_display_cancel_read(display);
94 return ret;
95 }
96 }
97
98 /* Don't stop if flushing hits an EPIPE; continue so we can read any
99 * protocol error that may have triggered it. */
100 if (ret < 0 && errno != EPIPE) {
101 wl_display_cancel_read(display);
102 return -1;
103 }
104
105 while (true) {
106 if (timeout) {
107 clock_gettime(CLOCK_MONOTONIC, &now);
108 timespec_sub_saturate(&result, &deadline, &now);
109 remaining_timeout = &result;
110 }
111
112 ret = wl_display_poll(display, POLLIN, remaining_timeout);
113 if (ret <= 0) {
114 wl_display_cancel_read(display);
115 break;
116 }
117
118 ret = wl_display_read_events(display);
119 if (ret == -1)
120 break;
121
122 ret = wl_display_dispatch_queue_pending(display, queue);
123 if (ret != 0)
124 break;
125
126 /* wl_display_dispatch_queue_pending can return 0 if we ended up reading
127 * from WL fd, but there was no complete event to dispatch yet.
128 * Try reading again. */
129 if (wl_display_prepare_read_queue(display, queue) == -1)
130 return wl_display_dispatch_queue_pending(display, queue);
131 }
132
133 return ret;
134 }
135 #endif
136
137 #ifndef HAVE_WL_CREATE_QUEUE_WITH_NAME
138 struct wl_event_queue *
wl_display_create_queue_with_name(struct wl_display * display,const char * name)139 wl_display_create_queue_with_name(struct wl_display *display, const char *name)
140 {
141 return wl_display_create_queue(display);
142 }
143 #endif
144