From f09791f3314de7b414bf21a46940ff684d936eaf Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 9 Mar 2023 10:00:43 -0800 Subject: [PATCH] rdp shell/rdp backend: support associate window id (#140) Co-authored-by: Hideyuki Nagase --- include/libweston/backend-rdp.h | 2 + libweston/backend-rdp/meson.build | 2 +- libweston/backend-rdp/rdprail.c | 45 ++++++++++++- rdprail-shell/app-list.c | 102 +++++++++++++++++++++++++++++- rdprail-shell/shell.c | 78 ++++++++++++++--------- rdprail-shell/shell.h | 1 + 6 files changed, 194 insertions(+), 36 deletions(-) diff --git a/include/libweston/backend-rdp.h b/include/libweston/backend-rdp.h index 65a10d311..0ef44a2a3 100644 --- a/include/libweston/backend-rdp.h +++ b/include/libweston/backend-rdp.h @@ -129,6 +129,7 @@ struct weston_rdprail_app_list_data { bool newAppId; bool deleteAppId; bool deleteAppProvider; + bool associateWindowId; char *appId; char *appGroup; char *appExecPath; @@ -136,6 +137,7 @@ struct weston_rdprail_app_list_data { char *appDesc; char *appProvider; pixman_image_t *appIcon; + uint32_t appWindowId; }; struct weston_rdp_rail_window_pos { diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build index 1443a671b..7d5d227db 100644 --- a/libweston/backend-rdp/meson.build +++ b/libweston/backend-rdp/meson.build @@ -28,7 +28,7 @@ if not dep_wpr.found() endif endif -dep_rdpapplist = dependency('rdpapplist', version: '>= 1.0.0', required: false) +dep_rdpapplist = dependency('rdpapplist', version: '>= 2.0.0', required: false) if dep_rdpapplist.found() config_h.set('HAVE_FREERDP_RDPAPPLIST_H', '1') endif diff --git a/libweston/backend-rdp/rdprail.c b/libweston/backend-rdp/rdprail.c index 555f2eff0..6edf5eab9 100644 --- a/libweston/backend-rdp/rdprail.c +++ b/libweston/backend-rdp/rdprail.c @@ -770,8 +770,8 @@ rail_client_ClientGetAppidReq_callback(bool freeOnly, void *arg) goto Exit; } - rdp_debug(b, "Client: ClientGetAppidReq: pid:%d appId:%s\n", - (uint32_t)pid, appId); + rdp_debug(b, "Client: ClientGetAppidReq: pid:%d appId:%s WindowId:0x%x\n", + (uint32_t)pid, appId, getAppidReq->windowId); rdp_debug_verbose(b, "Client: ClientGetAppidReq: pid:%d imageName:%s\n", (uint32_t)pid, imageName); @@ -3460,9 +3460,20 @@ rdp_rail_peer_activate(freerdp_peer* client) rdp_debug(b, "Server AppList caps version:%d\n", RDPAPPLIST_CHANNEL_VERSION); app_list_caps.version = RDPAPPLIST_CHANNEL_VERSION; + rdp_debug(b, " appListProviderName:%s\n", b->rdprail_shell_name); if (!utf8_string_to_rail_string(b->rdprail_shell_name, &app_list_caps.appListProviderName)) goto error_exit; +#if RDPAPPLIST_CHANNEL_VERSION >= 4 + /* assign unique id */ + char *s = getenv("WSLG_SERVICE_ID"); + if (!s) + s = b->rdprail_shell_name; + rdp_debug(b, " appListProviderUniqueId:%s\n", s); + if (!utf8_string_to_rail_string(s, + &app_list_caps.appListProviderUniqueId)) + goto error_exit; +#endif /* RDPAPPLIST_CHANNEL_VERSION >= 4 */ if (applist_ctx->ApplicationListCaps(applist_ctx, &app_list_caps) != CHANNEL_RC_OK) goto error_exit; free(app_list_caps.appListProviderName.string); @@ -4624,6 +4635,7 @@ rdp_rail_notify_app_list(void *rdp_backend, rdp_debug(b, " newAppId: %d\n", app_list_data->newAppId); rdp_debug(b, " deleteAppId: %d\n", app_list_data->deleteAppId); rdp_debug(b, " deleteAppProvider: %d\n", app_list_data->deleteAppProvider); + rdp_debug(b, " associateWindowId: %d\n", app_list_data->associateWindowId); rdp_debug(b, " appId: %s\n", app_list_data->appId); rdp_debug(b, " appGroup: %s\n", app_list_data->appGroup); rdp_debug(b, " appExecPath: %s\n", app_list_data->appExecPath); @@ -4631,8 +4643,35 @@ rdp_rail_notify_app_list(void *rdp_backend, rdp_debug(b, " appDesc: %s\n", app_list_data->appDesc); rdp_debug(b, " appIcon: %p\n", app_list_data->appIcon); rdp_debug(b, " appProvider: %s\n", app_list_data->appProvider); + rdp_debug(b, " appWindowId: 0x%x\n", app_list_data->appWindowId); + + if (app_list_data->associateWindowId) { + RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU associate_window_id = {}; + + assert(app_list_data->appProvider == NULL); + associate_window_id.flags = RDPAPPLIST_FIELD_ID | RDPAPPLIST_FIELD_WINDOW_ID; + associate_window_id.appWindowId = app_list_data->appWindowId; + if (app_list_data->appId == NULL || + !utf8_string_to_rail_string(app_list_data->appId, &associate_window_id.appId)) + goto Exit_associateWindowId; - if (app_list_data->deleteAppId) { + if (app_list_data->appGroup && + utf8_string_to_rail_string(app_list_data->appGroup, &associate_window_id.appGroup)) { + associate_window_id.flags |= RDPAPPLIST_FIELD_GROUP; + } + if (app_list_data->appExecPath && + utf8_string_to_rail_string(app_list_data->appExecPath, &associate_window_id.appExecPath)) { + associate_window_id.flags |= RDPAPPLIST_FIELD_EXECPATH; + } + if (app_list_data->appDesc && + utf8_string_to_rail_string(app_list_data->appDesc, &associate_window_id.appDesc)) { + associate_window_id.flags |= RDPAPPLIST_FIELD_DESC; + } + applist_ctx->AssociateWindowId(applist_ctx, &associate_window_id); + Exit_associateWindowId: + free(associate_window_id.appId.string); + free(associate_window_id.appGroup.string); + } else if (app_list_data->deleteAppId) { RDPAPPLIST_DELETE_APPLIST_PDU delete_app_list = {}; assert(app_list_data->appProvider == NULL); diff --git a/rdprail-shell/app-list.c b/rdprail-shell/app-list.c index 3600da400..22c008a2f 100644 --- a/rdprail-shell/app-list.c +++ b/rdprail-shell/app-list.c @@ -66,7 +66,7 @@ #if HAVE_GLIB && HAVE_WINPR -#define NUM_CONTROL_EVENT 5 +#define NUM_CONTROL_EVENT 6 #define EVENT_TIMEOUT_MS 2000 // 2 seconds #define MAX_ICON_RETRY_COUNT 5 @@ -79,6 +79,7 @@ struct app_list_context { HANDLE stopRdpNotifyEvent; // control event: wait index 2 HANDLE loadIconEvent; // control event: wait index 3 HANDLE findImageNameEvent; // control event: wait index 4 + HANDLE associateWindowAppIdEvent;// control event: wait index 5 HANDLE replyEvent; bool isRdpNotifyStarted; bool isAppListNamespaceAttached; @@ -97,6 +98,11 @@ struct app_list_context { char *image_name; size_t image_name_size; } find_image_name; + struct { + pid_t pid; + char *app_id; + uint32_t window_id; + } associate_window_app_id; struct { char requestedClientLanguageId[32]; // 32 = RDPAPPLIST_LANG_SIZE. char currentClientLanguageId[32]; @@ -389,6 +395,41 @@ send_app_entry(struct desktop_shell *shell, char *key, struct app_entry *entry, pixman_image_unref(app_list_data.appIcon); } +static void +send_associate_window_app_id(struct desktop_shell *shell, pid_t pid, char *app_id, uint32_t window_id) +{ + struct app_list_context *context = (struct app_list_context *)shell->app_list_context; + struct weston_rdprail_app_list_data app_list_data = {}; + struct app_entry *entry; + char *app_exec = NULL; + char *app_desc = NULL; + + if (!shell->rdprail_api->notify_app_list) + return; + + entry = (struct app_entry *)HashTable_GetItemValue(context->table, app_id); + if (entry) { + app_exec = entry->try_exec ? entry->try_exec : entry->exec; + app_desc = entry->name; + } + + if (!app_exec) { + /*TODO: obtain from /proc/[pid]/cmdline */ + } + + if (!app_desc) + app_desc = app_id; + + app_list_data.associateWindowId = true; + app_list_data.appId = app_id; + app_list_data.appGroup = NULL; + app_list_data.appExecPath = app_exec; + app_list_data.appDesc = app_desc; + app_list_data.appWindowId = window_id; + + shell->rdprail_api->notify_app_list(shell->rdp_backend, &app_list_data); +} + static void retry_find_icon_file(struct desktop_shell *shell) { @@ -909,6 +950,7 @@ app_list_monitor_thread(LPVOID arg) events[num_events++] = context->stopRdpNotifyEvent; events[num_events++] = context->loadIconEvent; events[num_events++] = context->findImageNameEvent; + events[num_events++] = context->associateWindowAppIdEvent; assert(num_events == NUM_CONTROL_EVENT); /* append optional folders */ @@ -1084,6 +1126,22 @@ app_list_monitor_thread(LPVOID arg) continue; } + /* Associate Window/AppId event */ + if (status == WAIT_OBJECT_0 + 5) { + shell_rdp_debug_verbose(shell, "app_list_monitor_thread: associateWindowAppIdEvent is signalled. pid:%d, app_id:%s, window_id:0x%x\n", + context->associate_window_app_id.pid, + context->associate_window_app_id.app_id, + context->associate_window_app_id.window_id); + + send_associate_window_app_id(shell, + context->associate_window_app_id.pid, + context->associate_window_app_id.app_id, + context->associate_window_app_id.window_id); + + SetEvent(context->replyEvent); + continue; + } + /* Somethings are changed in watch folders */ if (shell->rdprail_api->notify_app_list && num_watch) { len = read(fd[status - WAIT_OBJECT_0 - NUM_CONTROL_EVENT], buf, sizeof buf); @@ -1172,6 +1230,11 @@ start_app_list_monitor(struct desktop_shell *shell) if (!context->findImageNameEvent) goto Error_Exit; + /* bManualReset = TRUE, ideally here needs FALSE, but winpr doesn't support it */ + context->associateWindowAppIdEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!context->associateWindowAppIdEvent) + goto Error_Exit; + /* bManualReset = TRUE, ideally here needs FALSE, but winpr doesn't support it */ context->replyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!context->replyEvent) @@ -1190,6 +1253,11 @@ start_app_list_monitor(struct desktop_shell *shell) context->replyEvent = NULL; } + if (context->associateWindowAppIdEvent) { + CloseHandle(context->associateWindowAppIdEvent); + context->associateWindowAppIdEvent = NULL; + } + if (context->findImageNameEvent) { CloseHandle(context->findImageNameEvent); context->findImageNameEvent = NULL; @@ -1246,6 +1314,11 @@ stop_app_list_monitor(struct desktop_shell *shell) context->replyEvent = NULL; } + if (context->associateWindowAppIdEvent) { + CloseHandle(context->associateWindowAppIdEvent); + context->associateWindowAppIdEvent = NULL; + } + if (context->findImageNameEvent) { CloseHandle(context->findImageNameEvent); context->findImageNameEvent = NULL; @@ -1330,6 +1403,33 @@ void app_list_find_image_name(struct desktop_shell *shell, pid_t pid, char *imag return; } +void app_list_associate_window_app_id(struct desktop_shell *shell, pid_t pid, char *app_id, uint32_t window_id) +{ +#if HAVE_WINPR && HAVE_GLIB + struct app_list_context *context = (struct app_list_context *)shell->app_list_context; + + if (context) { + assert(context->associate_window_app_id.pid == (pid_t) 0); + assert(context->associate_window_app_id.app_id == NULL); + assert(context->associate_window_app_id.window_id == 0); + context->associate_window_app_id.pid = pid; + context->associate_window_app_id.app_id = app_id; + context->associate_window_app_id.window_id = window_id; + + /* signal worker thread to load icon at worker thread */ + SetEvent(context->associateWindowAppIdEvent); + WaitForSingleObject(context->replyEvent, INFINITE); + /* here must reset since winpr doesn't support auto reset event */ + ResetEvent(context->replyEvent); + + context->associate_window_app_id.pid = (pid_t) 0; + context->associate_window_app_id.app_id = NULL; + context->associate_window_app_id.window_id = 0; + } +#endif + return; +} + bool app_list_start_backend_update(struct desktop_shell *shell, char *clientLanguageId) { #if HAVE_WINPR && HAVE_GLIB diff --git a/rdprail-shell/shell.c b/rdprail-shell/shell.c index 0f843c5cc..c9869d296 100644 --- a/rdprail-shell/shell.c +++ b/rdprail-shell/shell.c @@ -165,6 +165,10 @@ struct shell_surface { bool is_icon_set; } icon; + struct { + bool is_window_app_id_associated; + } app_id; + struct wl_listener metadata_listener; }; @@ -2612,12 +2616,6 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, wl_list_for_each(view, &surface->views, surface_link) weston_view_update_transform(view); } - - if (!shsurf->icon.is_icon_set) { - /* TODO hook to meta data change notification */ - shell_surface_set_window_icon(desktop_surface, 0, 0, 0, NULL, NULL); - shsurf->icon.is_icon_set = true; - } } static void @@ -4635,6 +4633,7 @@ shell_backend_get_app_id(void *shell_context, struct weston_surface *surface, ch { struct desktop_shell *shell = (struct desktop_shell *)shell_context; struct weston_desktop_surface *desktop_surface; + struct weston_surface_rail_state *rail_state; struct shell_surface *shsurf; const struct weston_xwayland_surface_api *api; pid_t pid; @@ -4655,31 +4654,35 @@ shell_backend_get_app_id(void *shell_context, struct weston_surface *surface, ch if (!desktop_surface) return -1; - /* obtain application id specified via wayland interface */ - id = weston_desktop_surface_get_app_id(desktop_surface); - if (id) { - strncpy(app_id, id, app_id_size); - } else { - /* if app_id is not specified via wayland interface, - obtain class name from X server for X app, and use as app_id */ - shsurf = weston_desktop_surface_get_user_data(desktop_surface); - if (shsurf) { - api = shsurf->shell->xwayland_surface_api; - if (!api) { - api = weston_xwayland_surface_get_api(shsurf->shell->compositor); - shsurf->shell->xwayland_surface_api = api; - } - if (api && api->is_xwayland_surface(surface)) { - class_name = api->get_class_name(surface); - if (class_name) { - strncpy(app_id, class_name, app_id_size); - free(class_name); - /* app_id is from Xwayland */ - is_wayland = false; - } - } + shsurf = weston_desktop_surface_get_user_data(desktop_surface); + if (!shsurf) + return -1; + + rail_state = (struct weston_surface_rail_state *)surface->backend_state; + if (!rail_state) + return -1; + + /* first obtain class name from X server for X app, and use as app_id */ + api = shsurf->shell->xwayland_surface_api; + if (!api) { + api = weston_xwayland_surface_get_api(shsurf->shell->compositor); + shsurf->shell->xwayland_surface_api = api; + } + if (api && api->is_xwayland_surface(surface)) { + class_name = api->get_class_name(surface); + if (class_name) { + strncpy(app_id, class_name, app_id_size); + free(class_name); + /* app_id is from Xwayland */ + is_wayland = false; } } + /* if not, obtain application id specified via wayland interface */ + if (app_id[0] == '\0') { + id = weston_desktop_surface_get_app_id(desktop_surface); + if (id) + strncpy(app_id, id, app_id_size); + } /* obtain pid for execuable path */ pid = weston_desktop_surface_get_pid(desktop_surface); @@ -4700,8 +4703,21 @@ shell_backend_get_app_id(void *shell_context, struct weston_surface *surface, ch strncpy(image_name, app_id, image_name_size); } - shell_rdp_debug_verbose(shell, "shell_backend_get_app_id: 0x%p: pid:%d, app_id:%s, image_name:%s\n", - surface, pid, app_id, image_name); + shell_rdp_debug_verbose(shell, "shell_backend_get_app_id: 0x%p: pid:%d, app_id:%s, windowId:0x%x, image_name:%s\n", + surface, pid, app_id, rail_state->window_id, image_name); + + /* obtain window icon for app */ + if (!shsurf->icon.is_icon_set) { + /* TODO hook to meta data change notification */ + shell_surface_set_window_icon(desktop_surface, 0, 0, 0, NULL, NULL); + shsurf->icon.is_icon_set = true; + } + + /* associate window and app_id at client side */ + if (!shsurf->app_id.is_window_app_id_associated && app_id[0] != '\0') { + app_list_associate_window_app_id(shsurf->shell, pid, app_id, rail_state->window_id); + shsurf->app_id.is_window_app_id_associated = true; + } return pid; } diff --git a/rdprail-shell/shell.h b/rdprail-shell/shell.h index f70b4e272..aa98f126e 100644 --- a/rdprail-shell/shell.h +++ b/rdprail-shell/shell.h @@ -204,5 +204,6 @@ pixman_image_t *app_list_load_icon_file(struct desktop_shell *shell, const char bool app_list_start_backend_update(struct desktop_shell *shell, char *clientLanguageId); void app_list_stop_backend_update(struct desktop_shell *shell); void app_list_find_image_name(struct desktop_shell *shell, pid_t pid, char *image_name, size_t image_name_size, bool is_wayland); +void app_list_associate_window_app_id(struct desktop_shell *shell, pid_t pid, char *app_id, uint32_t window_id); // img-load.c pixman_image_t *load_icon_image(struct desktop_shell *shell, const char *filename);