1 /*
2 * Copyright © 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <sys/un.h>
32 #include <time.h>
33 #include <unistd.h>
34
35 #include "wayland-client.h"
36 #include "wayland-server.h"
37 #include "wayland-util.h"
38 #include "test-runner.h"
39
40 /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */
41 static const char *
require_xdg_runtime_dir(void)42 require_xdg_runtime_dir(void)
43 {
44 char *val = getenv("XDG_RUNTIME_DIR");
45 assert(val && "set $XDG_RUNTIME_DIR to run this test");
46
47 return val;
48 }
49
50 struct compositor {
51 struct wl_display *display;
52 struct wl_event_loop *loop;
53 int message;
54 struct wl_client *client;
55 };
56
57 struct client {
58 struct wl_display *display;
59 struct wl_callback *cb;
60 int message;
61 };
62
63 struct message {
64 enum wl_protocol_logger_type type;
65 const char *class;
66 int opcode;
67 const char *message_name;
68 int args_count;
69 } messages[] = {
70 {
71 .type = WL_PROTOCOL_LOGGER_REQUEST,
72 .class = "wl_display",
73 .opcode = 0,
74 .message_name = "sync",
75 .args_count = 1,
76 },
77 {
78 .type = WL_PROTOCOL_LOGGER_EVENT,
79 .class = "wl_callback",
80 .opcode = 0,
81 .message_name = "done",
82 .args_count = 1,
83 },
84 {
85 .type = WL_PROTOCOL_LOGGER_EVENT,
86 .class = "wl_display",
87 .opcode = 1,
88 .message_name = "delete_id",
89 .args_count = 1,
90 },
91 };
92
93 struct client_message {
94 enum wl_protocol_logger_client_type type;
95 const char *class;
96 int opcode;
97 const char *message_name;
98 int args_count;
99 } client_messages[] = {
100 {
101 .type = WL_PROTOCOL_LOGGER_CLIENT_REQUEST,
102 .class = "wl_display",
103 .opcode = 0,
104 .message_name = "sync",
105 .args_count = 1,
106 },
107 {
108 .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
109 .class = "wl_display",
110 .opcode = 1,
111 .message_name = "delete_id",
112 .args_count = 1,
113 },
114 {
115 .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
116 .class = "wl_callback",
117 .opcode = 0,
118 .message_name = "done",
119 .args_count = 1,
120 },
121 };
122
123 static void
logger_func(void * user_data,enum wl_protocol_logger_type type,const struct wl_protocol_logger_message * message)124 logger_func(void *user_data, enum wl_protocol_logger_type type,
125 const struct wl_protocol_logger_message *message)
126 {
127 struct compositor *c = user_data;
128 struct message *msg = &messages[c->message++];
129
130 assert(msg->type == type);
131 assert(strcmp(msg->class, wl_resource_get_class(message->resource)) == 0);
132 assert(msg->opcode == message->message_opcode);
133 assert(strcmp(msg->message_name, message->message->name) == 0);
134 assert(msg->args_count == message->arguments_count);
135
136 c->client = wl_resource_get_client(message->resource);
137 }
138
139 static void
client_logger_func(void * user_data,enum wl_protocol_logger_client_type type,const struct wl_protocol_logger_client_message * message)140 client_logger_func(void *user_data, enum wl_protocol_logger_client_type type,
141 const struct wl_protocol_logger_client_message *message)
142 {
143 struct client *c = user_data;
144 struct client_message *msg = &client_messages[c->message++];
145
146 assert(msg->type == type);
147 assert(strcmp(msg->class, wl_proxy_get_class(message->proxy)) == 0);
148 assert(msg->opcode == message->message_opcode);
149 assert(strcmp(msg->message_name, message->message->name) == 0);
150 assert(msg->args_count == message->arguments_count);
151 }
152
153 // A slightly simplified version of get_next_argument() from src/connection.c
154 static const char*
get_next_argument_type(const char * signature,char * type)155 get_next_argument_type(const char *signature, char* type)
156 {
157 for (; *signature; ++signature) {
158 assert(strchr("iufsonah?", *signature) != NULL);
159 switch (*signature) {
160 case 'i':
161 case 'u':
162 case 'f':
163 case 's':
164 case 'o':
165 case 'n':
166 case 'a':
167 case 'h':
168 *type = *signature;
169 return signature + 1;
170 case '?':
171 break;
172
173 }
174 }
175 *type = 0;
176 return signature;
177 }
178
179 // This duplicates what the internal wl_closure_print function does, and can be
180 // used as a starting point for a client or server that wants to log messages.
181 static void
client_log_to_stderr_demo(void * user_data,enum wl_protocol_logger_client_type type,const struct wl_protocol_logger_client_message * message)182 client_log_to_stderr_demo(void *user_data,
183 enum wl_protocol_logger_client_type type,
184 const struct wl_protocol_logger_client_message *message) {
185 int i;
186 char arg_type;
187 const char *signature = message->message->signature;
188 const union wl_argument* args = message->arguments;
189 struct wl_proxy* arg_proxy;
190 const char* arg_class;
191 struct timespec tp;
192 unsigned int time;
193
194 clock_gettime(CLOCK_REALTIME, &tp);
195 time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
196
197 // Note: server logger will be given message->resource, and should
198 // use wl_resource_get_class and wl_resolurce_get_id.
199 fprintf(stderr, "[%10.3f] %s%s@%u.%s(",
200 time / 1000.0,
201 (type == WL_PROTOCOL_LOGGER_CLIENT_REQUEST) ? " -> " : "",
202 wl_proxy_get_class(message->proxy), wl_proxy_get_id(message->proxy),
203 message->message->name);
204
205 for (i = 0; i < message->arguments_count; i++) {
206 signature = get_next_argument_type(signature, &arg_type);
207 if (i > 0)
208 fprintf(stderr, ", ");
209
210 switch (arg_type) {
211 case 'u':
212 fprintf(stderr, "%u", args[i].u);
213 break;
214 case 'i':
215 fprintf(stderr, "%d", args[i].i);
216 break;
217 case 'f':
218 fprintf(stderr, "%f", wl_fixed_to_double(args[i].f));
219 break;
220 case 's':
221 if (args[i].s)
222 fprintf(stderr, "\"%s\"", args[i].s);
223 else
224 fprintf(stderr, "nil");
225 break;
226 case 'o':
227 if (args[i].o) {
228 // Note: server logger should instead use
229 // wl_resource_from_object, and then
230 // wl_resource_get_class and wl_resource_get_id.
231 arg_proxy = wl_proxy_from_object(args[i].o);
232 arg_class = wl_proxy_get_class(arg_proxy);
233
234 fprintf(stderr, "%s@%u",
235 arg_class ? arg_class : "[unknown]",
236 wl_proxy_get_id(arg_proxy));
237 } else {
238 fprintf(stderr, "nil");
239 }
240 break;
241 case 'n':
242 fprintf(stderr, "new id %s@",
243 (message->message->types[i]) ?
244 message->message->types[i]->name :
245 "[unknown]");
246 if (args[i].n != 0)
247 fprintf(stderr, "%u", args[i].n);
248 else
249 fprintf(stderr, "nil");
250 break;
251 case 'a':
252 fprintf(stderr, "array");
253 break;
254 case 'h':
255 fprintf(stderr, "fd %d", args[i].h);
256 break;
257 }
258 }
259
260 fprintf(stderr, ")\n");
261 }
262
263 static void
callback_done(void * data,struct wl_callback * cb,uint32_t time)264 callback_done(void *data, struct wl_callback *cb, uint32_t time)
265 {
266 wl_callback_destroy(cb);
267 }
268
269 static const struct wl_callback_listener callback_listener = {
270 callback_done,
271 };
272
TEST(logger)273 TEST(logger)
274 {
275 test_set_timeout(1);
276
277 const char *socket;
278 struct compositor compositor = { 0 };
279 struct client client = { 0 };
280 struct wl_protocol_logger *logger;
281 struct wl_protocol_logger_client *logger_client;
282 struct wl_protocol_logger_client *logger_client_demo;
283
284 require_xdg_runtime_dir();
285
286 compositor.display = wl_display_create();
287 compositor.loop = wl_display_get_event_loop(compositor.display);
288 socket = wl_display_add_socket_auto(compositor.display);
289
290 logger = wl_display_add_protocol_logger(compositor.display,
291 logger_func, &compositor);
292
293 client.display = wl_display_connect(socket);
294 logger_client = wl_display_add_protocol_logger_client(
295 client.display, client_logger_func, &client);
296 logger_client_demo = wl_display_add_protocol_logger_client(
297 client.display, client_log_to_stderr_demo, &client);
298 client.cb = wl_display_sync(client.display);
299 wl_callback_add_listener(client.cb, &callback_listener, NULL);
300 wl_display_flush(client.display);
301
302 while (compositor.message < 3) {
303 wl_event_loop_dispatch(compositor.loop, -1);
304 wl_display_flush_clients(compositor.display);
305 }
306
307 wl_display_dispatch(client.display);
308 wl_display_disconnect(client.display);
309
310 wl_protocol_logger_client_destroy(logger_client);
311 wl_protocol_logger_client_destroy(logger_client_demo);
312 wl_client_destroy(compositor.client);
313 wl_protocol_logger_destroy(logger);
314 wl_display_destroy(compositor.display);
315 }
316