Implement AppChooser portal#27
Conversation
|
Hi sorry I missed this one... Is there more to this? I don't see where |
There was a problem hiding this comment.
Pull request overview
This PR adds an org.freedesktop.impl.portal.AppChooser backend to xdg-desktop-portal-xapp, providing an “Open With…” chooser UI and offering app installation via mintinstall when no suitable apps are present.
Changes:
- Add AppChooser D-Bus backend implementation and hook it into portal startup.
- Introduce GTK UI (dialog + row) bundled via GResources.
- Add external parent window handling helpers (X11/Wayland) for transient/modal dialog parenting.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
src/xdg-desktop-portal-xapp.gresource.xml |
Adds UI resources (dialog/row templates) to the compiled resource bundle. |
src/xdg-desktop-portal-xapp.c |
Initializes the new AppChooser portal on bus acquisition. |
src/meson.build |
Adds AppChooser DBus interface codegen, GResources compilation, and new source files. |
src/appchooser.h |
Declares AppChooser portal init entrypoint. |
src/appchooser.c |
Implements AppChooser D-Bus handlers and request/dialog lifecycle. |
src/appchooserdialog.ui |
Defines the AppChooser dialog UI (list + empty state). |
src/appchooserdialog.h |
Declares AppChooserDialog API. |
src/appchooserdialog.c |
Implements dialog behavior, selection, “more” expansion, and mintinstall launch. |
src/appchooserrow.ui |
Defines the list row UI for an application entry. |
src/appchooserrow.h |
Declares AppChooserRow API. |
src/appchooserrow.c |
Implements the row widget binding app info -> UI. |
src/externalwindow.h |
Introduces ExternalWindow abstraction for parenting portal windows to external handles. |
src/externalwindow.c |
Implements handle parsing and ExternalWindow base type. |
src/externalwindow-x11.h |
Declares X11 backend for ExternalWindow. |
src/externalwindow-x11.c |
Implements X11 external window parenting via foreign XID. |
src/externalwindow-wayland.h |
Declares Wayland backend for ExternalWindow. |
src/externalwindow-wayland.c |
Implements Wayland external window parenting via exported handle. |
data/xapp.portal |
Registers the new AppChooser interface in the backend’s advertised interfaces list. |
Comments suppressed due to low confidence (2)
src/externalwindow-x11.h:36
- externalwindow-x11.h declares
GType external_window_get_type (void);but the type macro uses external_window_x11_get_type(). This mismatch will cause build failures or wrong symbol usage. The header should declareexternal_window_x11_get_type()(and the base type getter is already declared in externalwindow.h).
#define EXTERNAL_TYPE_WINDOW_X11 (external_window_x11_get_type ())
#define EXTERNAL_WINDOW_X11(object) (G_TYPE_CHECK_INSTANCE_CAST (object, EXTERNAL_TYPE_WINDOW_X11, ExternalWindowX11))
typedef struct _ExternalWindowX11 ExternalWindowX11;
typedef struct _ExternalWindowX11Class ExternalWindowX11Class;
GType external_window_get_type (void);
ExternalWindowX11 *external_window_x11_new (const char *handle_str);
src/meson.build:93
- meson.build unconditionally compiles externalwindow-x11.c/externalwindow-wayland.c, but config.h doesn't define HAVE_GTK_X11/HAVE_GTK_WAYLAND. As a result, externalwindow.c compiles with both backends disabled and create_external_window_from_handle() will always return NULL, breaking parent window association. Add proper Meson feature detection (e.g., gdk-x11-3.0 / gdk-wayland-3.0 deps) and define HAVE_GTK_X11/HAVE_GTK_WAYLAND in config.h, or remove the ifdefs if these backends are always expected.
config_entries = {
'GETTEXT_PACKAGE' : '"@0@"'.format(meson.project_name()),
'LOCALEDIR': '"@0@"'.format(prefix / get_option('localedir')),
'PACKAGE_STRING': '"xdg-desktop-portal-xapp @0@"'.format(meson.project_version()),
'G_LOG_DOMAIN': '"xdg-desktop-portal-xapp"'
}
config = configuration_data(config_entries)
built_sources += configure_file(output: 'config.h', configuration: config)
deps = [
meson.get_compiler('c').find_library('m'),
dependency('glib-2.0', version: '>= 2.44'),
dependency('gio-unix-2.0'),
dependency('gtk+-3.0'),
xdg_desktop_portal_dep,
]
sources = built_sources + files(
'appchooser.c',
'appchooserdialog.c',
'appchooserrow.c',
'background.c',
'externalwindow.c',
'externalwindow-wayland.c',
'externalwindow-x11.c',
'inhibit.c',
'lockdown.c',
'request.c',
'screenshot.c',
'session.c',
'settings.c',
'utils.c',
'wallpaper.c',
'xdg-desktop-portal-xapp.c',
)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| static void | ||
| ensure_default_in_initial_list (const char **choices, | ||
| const char *default_id) | ||
| { | ||
| int i; | ||
| guint n_choices; | ||
|
|
||
| if (default_id == NULL) | ||
| return; | ||
|
|
||
| n_choices = g_strv_length ((char **)choices); | ||
| if (n_choices <= INITIAL_LIST_SIZE) | ||
| return; | ||
|
|
||
| for (i = 0; i < INITIAL_LIST_SIZE; i++) | ||
| { | ||
| if (strcmp (choices[i], default_id) == 0) | ||
| return; | ||
| } | ||
|
|
||
| for (i = INITIAL_LIST_SIZE; i < n_choices; i++) | ||
| { | ||
| if (strcmp (choices[i], default_id) == 0) | ||
| { | ||
| const char *tmp = choices[0]; | ||
| choices[0] = choices[i]; | ||
| choices[i] = tmp; | ||
| return; | ||
| } |
| for (i = 0; choices[i]; i++) | ||
| { | ||
| if (g_strv_contains ((const char * const *)dialog->choices, choices[i])) | ||
| continue; | ||
|
|
||
| g_ptr_array_add (new_choices, g_strdup (choices[i])); | ||
|
|
||
| if (!gtk_widget_get_visible (dialog->more_row)) | ||
| { | ||
| g_autofree char *desktop_id = NULL; | ||
| g_autoptr(GAppInfo) info = NULL; | ||
| GtkWidget *row; | ||
|
|
||
| desktop_id = g_strconcat (choices[i], ".desktop", NULL); | ||
| info = G_APP_INFO (g_desktop_app_info_new (desktop_id)); | ||
|
|
||
| row = GTK_WIDGET (app_chooser_row_new (info)); | ||
| gtk_widget_set_visible (row, TRUE); | ||
| gtk_list_box_insert (GTK_LIST_BOX (dialog->list), row, -1); | ||
| } |
| g_object_ref_sink (fake_parent); | ||
|
|
||
| dialog = GTK_WIDGET (app_chooser_dialog_new (choices, latest_chosen_id, content_type, location)); | ||
| gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fake_parent)); |
| g_set_object (&row->info, info); | ||
|
|
||
| icon = g_app_info_get_icon (info); | ||
| if (!icon) | ||
| icon = g_themed_icon_new ("application-x-executable"); | ||
|
|
||
| gtk_image_set_from_gicon (GTK_IMAGE (row->icon), icon, GTK_ICON_SIZE_DIALOG); | ||
| gtk_image_set_pixel_size (GTK_IMAGE (row->icon), 32); |
|
Please fix src/meson.build:93 meson.build unconditionally compiles externalwindow-x11.c/externalwindow-wayland.c, but config.h doesn't define HAVE_GTK_X11/HAVE_GTK_WAYLAND. As a result, externalwindow.c compiles with both backends disabled and create_external_window_from_handle() will always return NULL, breaking parent window association. Add proper Meson feature detection (e.g., gdk-x11-3.0 / gdk-wayland-3.0 deps) and define HAVE_GTK_X11/HAVE_GTK_WAYLAND in config.h, or remove the ifdefs if these backends are always expected. |
|
Done |
|
Cinnamon (or XAPP DE in general) do not have any associated software manager. Mintinstall is a distribution-specific tool. Also, I think this is going way too far in terms of scope and responsibility. I am not keen in reinventing the wheel and re-implementing DE parts like this inside the portal. If something can't be open, just say it can't be open. A file manager which is running sandboxed, in flatpak, with no idea about the distro, shouldn't try to go that far, referencing other apps, playing with mimes and package management. This is pure distro-land at this stage and it's done natively. |
It can be added with list of popular software managers, e.g. GNOME Software or Ubuntu's App Center, and not break the user experience just like in first screenshot, where using
It didn't. AppChooser portal handle this for sandboxed apps, and it's useful in Wayland |
|
OK, xdg-desktop-portal-gtk isn't suitable as an AppChooser implementation because it's designed to be GNOME-specific.
We can implement an AppChooser in the XApp portal but:
Note: Dolphin should use org.freedesktop.portal.OpenURI.OpenFile on double-click, I don't understand why it's requesting org.freedesktop.impl.portal.AppChooser.ChooseApplication every time. By doing so, it ignores default MIME choices. |
|
@Sunderland93 I can work on adapting the PR if you want, unless you want to do it. Let me know. |
Yes, thank you. I'm currently don't have enough time to work on it |
This portal is used when the system has several programs capable of opening the selected file type. If such programs are missing, they are offered for installation through the system application manager (in this case, mintinstall). Without this PR, a similar portal for GNOME, linked to gnome-software, is used. Tested with Dolphin installed from Flatpak.
Before:

After:

