2 * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
30 #include <wayland-server.h>
31 #include <wayland-util.h>
32 #include <pepper-inotify.h>
33 #include <pepper-keyrouter.h>
34 #include <pepper-xkb.h>
35 #include <headless_server.h>
39 #define STDOUT_REDIR "stdout"
40 #define STDERR_REDIR "stderr"
41 #define PROTOCOL_TRACE_ON "protocol_trace_on"
42 #define PROTOCOL_TRACE_OFF "protocol_trace_off"
43 #define KEYGRAB_STATUS "keygrab_status"
44 #define TOPVWINS "topvwins"
45 #define CONNECTED_CLIENTS "connected_clients"
46 #define CLIENT_RESOURCES "reslist"
47 #define KEYMAP "keymap"
48 #define HELP_MSG "help"
52 pepper_compositor_t *compositor;
53 pepper_inotify_t *inotify;
55 pepper_view_t *top_mapped;
59 typedef void (*headless_debug_action_cb_t)(headless_debug_t *hd, void *data);
64 headless_debug_action_cb_t cb;
65 headless_debug_action_cb_t disable_cb;
66 } headless_debug_action_t;
68 const static int KEY_DEBUG = 0xdeaddeb0;
69 extern void wl_debug_server_enable(int enable);
72 _headless_debug_usage()
74 fprintf(stdout, "Supported commands:\n\n");
75 fprintf(stdout, "\t %s\n", PROTOCOL_TRACE_ON);
76 fprintf(stdout, "\t %s\n", PROTOCOL_TRACE_OFF);
77 fprintf(stdout, "\t %s\n", STDOUT_REDIR);
78 fprintf(stdout, "\t %s\n", STDERR_REDIR);
79 fprintf(stdout, "\t %s\n", KEYGRAB_STATUS);
80 fprintf(stdout, "\t %s\n", TOPVWINS);
81 fprintf(stdout, "\t %s\n", CONNECTED_CLIENTS);
82 fprintf(stdout, "\t %s\n", CLIENT_RESOURCES);
83 fprintf(stdout, "\t %s\n", KEYMAP);
84 fprintf(stdout, "\t %s\n", HELP_MSG);
86 fprintf(stdout, "\nTo execute commands, just create/remove/update a file with the commands above.\n");
87 fprintf(stdout, "Please refer to the following examples.\n\n");
88 fprintf(stdout, "\t # winfo protocol_trace_on\t : enable event trace\n");
89 fprintf(stdout, "\t # winfo event_trace_off\t : disable event trace\n");
90 fprintf(stdout, "\t # winfo stdout\t\t\t : redirect STDOUT\n");
91 fprintf(stdout, "\t # winfo stderr\t\t\t : redirect STDERR\n");
92 fprintf(stdout, "\t # winfo keygrab_status\t\t : display keygrab status\n");
93 fprintf(stdout, "\t # winfo topvwins\t\t : display top/visible window stack\n");
94 fprintf(stdout, "\t # winfo connected_clients\t : display connected clients information\n");
95 fprintf(stdout, "\t # winfo reslist\t\t : display each resources information of connected clients\n");
96 fprintf(stdout, "\t # winfo keymap\t\t : display current xkb keymap\n");
97 fprintf(stdout, "\t # winfo help\t\t\t : display this help message\n");
101 _headless_debug_protocol_trace_on(headless_debug_t *hdebug, void *data)
105 wl_debug_server_enable(1);
109 _headless_debug_protocol_trace_off(headless_debug_t *hdebug, void *data)
113 wl_debug_server_enable(0);
117 _headless_debug_dummy(headless_debug_t *hdebug, void *data)
121 _headless_debug_usage();
124 static enum wl_iterator_result
125 _client_get_resource_itr(struct wl_resource *resource, void *data)
127 int *n_resources = (int *)data;
129 PEPPER_TRACE("\t\t [resource][%d] class=%s, id=%u\n", ++(*n_resources), wl_resource_get_class(resource), wl_resource_get_id(resource));
131 return WL_ITERATOR_CONTINUE;
135 _headless_debug_connected_clients(headless_debug_t *hdebug, void *data)
142 uint32_t n_clients = 0;
145 struct wl_list *clist = NULL;
146 struct wl_client *client_itr = NULL;
147 pepper_bool_t need_reslist = PEPPER_FALSE;
149 const char *cmds = (const char *)data;
151 /* check if reslist feature is required */
152 if (cmds && !strncmp(cmds, CLIENT_RESOURCES, MAX_CMDS)) {
153 need_reslist = PEPPER_TRUE;
156 /* get client list which bound wl_compositor global */
157 clist = wl_display_get_client_list(pepper_compositor_get_display(hdebug->compositor));
158 PEPPER_CHECK(clist, return, "Failed to get client list from compositor->display.\n");
160 PEPPER_TRACE("========= [Connected clients information] =========\n");
161 wl_client_for_each(client_itr, clist) {
163 client_fd = wl_client_get_fd(client_itr);
164 wl_client_get_credentials(client_itr, &pid, &uid, &gid);
165 PEPPER_TRACE("\t client[%d]: pid=%d, user=%d, group=%d, socket_fd=%d", n_clients, pid, uid, gid, client_fd);
167 if (PEPPER_FALSE == need_reslist) {
173 wl_client_for_each_resource(client_itr, _client_get_resource_itr, &n_resources);
174 PEPPER_TRACE("\t\t number of resources = %d\n", n_resources);
180 PEPPER_TRACE("============ [No connected clients] ===========\n\n");
184 _headless_debug_redir_stdout(headless_debug_t *hdebug, void *data)
192 fd = open("/run/pepper/stdout.txt", O_CREAT | O_WRONLY | O_APPEND, S_IWUSR | S_IWGRP);
195 PEPPER_TRACE("Failed to open stdout.txt (errno=%s)\n", strerror(errno));
201 PEPPER_CHECK(ret >= 0, return, "Failed to redirect STDOUT.\n");
203 PEPPER_TRACE("STDOUT has been redirected to stdout.txt.\n");
207 _headless_debug_redir_stderr(headless_debug_t *hdebug, void *data)
215 fd = open("/run/pepper/stderr.txt", O_CREAT | O_WRONLY | O_APPEND, S_IWUSR | S_IWGRP);
218 PEPPER_TRACE("Failed to open stderr.txt (errno=%s)\n", strerror(errno));
224 PEPPER_CHECK(ret >= 0, return, "Failed to redirect STDERR.\n");
226 PEPPER_TRACE("STDERR has been redirected to stderr.txt.\n");
231 _headless_debug_topvwins(headless_debug_t *hdebug, void *data)
241 const pepper_list_t *list;
243 pepper_surface_t *surface;
244 pepper_view_t *top_visible = NULL;
246 PEPPER_CHECK(hdebug, return, "[%s] Invalid headless debug !\n", __FUNCTION__);
248 PEPPER_TRACE("No. WinID RscID PID w h x y Mapped Visible Top Top_Visible Focus\n");
249 PEPPER_TRACE("==========================================================================================\n");
251 list = pepper_compositor_get_view_list(hdebug->compositor);
253 pepper_list_for_each_list(l, list) {
254 view = (pepper_view_t *)l->item;
255 PEPPER_CHECK(view, continue, "[%s] Invalid object view:%p\n", __FUNCTION__, view);
257 surface = pepper_view_get_surface(view);
258 PEPPER_CHECK(surface, continue, "[%s] Invalid object surface:%p\n", __FUNCTION__, surface);
261 pepper_view_get_position(view, &x, &y);
262 pepper_view_get_size(view, &w, &h);
263 wl_client_get_credentials(wl_resource_get_client(pepper_surface_get_resource(surface)), &pid, NULL, NULL);
264 if (!top_visible && pepper_surface_get_buffer(surface))
267 pepper_log("DEBUG", PEPPER_LOG_LEVEL_DEBUG, "%3d 0x%08x 0x%08x %5d %4d %4d %4.0f %4.0f %s %s %s %s %s\n",
268 cnt, surface, pepper_surface_get_resource(surface), pid, w, h, x, y,
269 pepper_view_is_mapped(view) ? "O" : "X",
270 pepper_view_is_visible(view) ? "O" : "X",
271 (hdebug->top_mapped == view) ? "O" : "X",
272 (top_visible == view) ? "O" : "X",
273 (hdebug->focus == view) ? "O" : "X");
276 PEPPER_TRACE("==========================================================================================\n");
280 _headless_debug_keygrab_status(headless_debug_t *hdebug, void *data)
282 pepper_keyrouter_t *keyrouter;
284 keyrouter = headless_input_get_keyrouter(hdebug->compositor);
285 pepper_keyrouter_debug_keygrab_status_print(keyrouter);
289 _headless_debug_keymap(headless_debug_t *hdebug, void *data)
294 int min_keycode, max_keycode, num_mods, num_groups;
295 struct xkb_context *context = NULL;
296 struct xkb_keymap *keymap = NULL;
297 struct xkb_state *state = NULL;
298 xkb_keysym_t sym = XKB_KEY_NoSymbol;
299 char keyname[256] = {0, };
301 xkb = headless_input_get_xkb(hdebug->compositor);
302 PEPPER_CHECK(xkb, return, "xkb is not set\n");
304 context = pepper_xkb_get_context(xkb);
305 PEPPER_CHECK(context, return, "Current pepper_xkb has no context.\n");
306 keymap = pepper_xkb_get_keymap(xkb);
307 PEPPER_CHECK(keymap, return, "Current pepper_xkb has no keymap.\n");
308 state = pepper_xkb_get_state(xkb);
309 PEPPER_CHECK(state, return, "Current pepper_xkb has no state.\n");
311 min_keycode = xkb_keymap_min_keycode(keymap);
312 max_keycode = xkb_keymap_max_keycode(keymap);
313 num_groups = xkb_map_num_groups(keymap);
314 num_mods = xkb_keymap_num_mods(keymap);
317 printf(" min keycode: %d\n", min_keycode);
318 printf(" max keycode: %d\n", max_keycode);
319 printf(" num_groups : %d\n", num_groups);
320 printf(" num_mods : %d\n", num_mods);
321 for (i = 0; i < num_mods; i++) {
322 printf(" [%2d] mod: %s\n", i, xkb_keymap_mod_get_name(keymap, i));
325 printf("\n\n\tkeycode\t\tkeyname\t\t keysym\t repeat\n");
326 printf(" ----------------------------------------------------------------------\n");
328 for (i = min_keycode; i < (max_keycode + 1); i++) {
329 sym = xkb_state_key_get_one_sym(state, i);
331 memset(keyname, 0, sizeof(keyname));
332 xkb_keysym_get_name(sym, keyname, sizeof(keyname));
334 if (!strncmp(keyname, "NoSymbol", sizeof("NoSymbol")) && sym == 0x0)
337 printf("\t%4d%-5s%-25s%-20x%-5d\n", i, "", keyname, sym, xkb_keymap_key_repeats(keymap, i));
341 static const headless_debug_action_t debug_actions[] =
343 { STDOUT_REDIR, _headless_debug_redir_stdout, NULL },
344 { STDERR_REDIR, _headless_debug_redir_stderr, NULL },
345 { PROTOCOL_TRACE_ON, _headless_debug_protocol_trace_on, _headless_debug_protocol_trace_off },
346 { PROTOCOL_TRACE_OFF, _headless_debug_protocol_trace_off, NULL },
347 { KEYGRAB_STATUS, _headless_debug_keygrab_status, NULL },
348 { TOPVWINS, _headless_debug_topvwins, NULL },
349 { CONNECTED_CLIENTS, _headless_debug_connected_clients, NULL },
350 { CLIENT_RESOURCES, _headless_debug_connected_clients, NULL },
351 { KEYMAP, _headless_debug_keymap, NULL },
352 { HELP_MSG, _headless_debug_dummy, NULL },
356 _headless_debug_enable_action(headless_debug_t *hdebug, char *cmds)
358 int n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]);
360 for(int n=0 ; n < n_actions ; n++) {
361 if (!strncmp(cmds, debug_actions[n].cmds, MAX_CMDS)) {
362 PEPPER_TRACE("[%s : %s]\n", __FUNCTION__, debug_actions[n].cmds);
363 debug_actions[n].cb(hdebug, (void *)debug_actions[n].cmds);
371 _headless_debug_disable_action(headless_debug_t *hdebug, char *cmds)
373 int n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]);
375 for(int n=0 ; n < n_actions ; n++) {
376 if (!strncmp(cmds, debug_actions[n].cmds, MAX_CMDS)) {
377 if (debug_actions[n].disable_cb) {
378 PEPPER_TRACE("[%s : %s]\n", __FUNCTION__, debug_actions[n].cmds);
379 debug_actions[n].disable_cb(hdebug, (void *)debug_actions[n].cmds);
388 _trace_cb_handle_inotify_event(uint32_t type, pepper_inotify_event_t *ev, void *data)
390 headless_debug_t *hdebug = data;
391 char *file_name = pepper_inotify_event_name_get(ev);
393 PEPPER_CHECK(hdebug, return, "Invalid headless debug instance\n");
397 case PEPPER_INOTIFY_EVENT_TYPE_CREATE:
398 _headless_debug_enable_action(hdebug, file_name);
400 case PEPPER_INOTIFY_EVENT_TYPE_REMOVE:
401 _headless_debug_disable_action(hdebug, file_name);
403 case PEPPER_INOTIFY_EVENT_TYPE_MODIFY:
406 PEPPER_TRACE("[%s] Unhandled event type (%d)\n", __FUNCTION__, type);
412 headless_debug_set_focus_view(pepper_compositor_t *compositor, pepper_view_t *focus_view)
414 headless_debug_t *hdebug = NULL;
416 hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG);
417 PEPPER_CHECK(hdebug, return, "Invalid headless debug.\n");
419 if (hdebug->focus != focus_view) {
420 PEPPER_TRACE("[DEBUG] Focus view has been changed to 0x%x (from 0x%x)\n", focus_view, hdebug->focus);
421 hdebug->focus = focus_view;
426 headless_debug_set_top_view(pepper_compositor_t *compositor, pepper_view_t *top_view)
428 headless_debug_t *hdebug = NULL;
430 hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG);
431 PEPPER_CHECK(hdebug, return, "Invalid headless debug.\n");
433 if (hdebug->top_mapped != top_view) {
434 PEPPER_TRACE("[DEBUG] Top view has been changed to 0x%x (from 0x%x)\n", top_view, hdebug->top_mapped);
435 hdebug->top_mapped = top_view;
440 headless_debug_deinit(pepper_compositor_t * compositor)
442 headless_debug_t *hdebug = NULL;
444 hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG);
445 PEPPER_CHECK(hdebug, return, "Failed to get headless debug instance\n");
447 /* remove the directory watching already */
449 pepper_inotify_del(hdebug->inotify, "/run/pepper");
452 pepper_inotify_destroy(hdebug->inotify);
453 hdebug->inotify = NULL;
455 pepper_object_set_user_data((pepper_object_t *)hdebug->compositor, &KEY_DEBUG, NULL, NULL);
460 headless_debug_init(pepper_compositor_t *compositor)
463 headless_debug_t *hdebug = NULL;
464 pepper_inotify_t *inotify = NULL;
465 pepper_bool_t res = PEPPER_FALSE;
467 hdebug = (headless_debug_t*)calloc(1, sizeof(headless_debug_t));
468 PEPPER_CHECK(hdebug, goto error, "Failed to alloc for headless debug\n");
469 hdebug->compositor = compositor;
471 /* create inotify to watch file(s) for event trace */
472 inotify = pepper_inotify_create(hdebug->compositor, _trace_cb_handle_inotify_event, hdebug);
473 PEPPER_CHECK(inotify, goto error, "Failed to create inotify\n");
475 /* add a directory for watching */
476 res = pepper_inotify_add(inotify, "/run/pepper");
477 PEPPER_CHECK(res, goto error, "Failed on pepper_inotify_add()\n");
479 hdebug->inotify = inotify;
480 n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]);
482 PEPPER_TRACE("[%s] Done (%d actions have been defined.)\n", __FUNCTION__, n_actions);
484 pepper_object_set_user_data((pepper_object_t *)compositor, &KEY_DEBUG, hdebug, NULL);
488 headless_debug_deinit(compositor);