mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
Merge branch 'g-win32-app-info-launch-uris-async' into 'main'
GWin32AppInfo: Implement launch_uris_async Closes gtk#4400 See merge request GNOME/glib!2760
This commit is contained in:
commit
e1e9bc9429
@ -651,7 +651,9 @@ g_app_info_supports_files (GAppInfo *appinfo)
|
||||
* Launches the application. This passes the @uris to the launched application
|
||||
* as arguments, using the optional @context to get information
|
||||
* about the details of the launcher (like what screen it is on).
|
||||
* On error, @error will be set accordingly.
|
||||
* On error, @error will be set accordingly. If the application only supports
|
||||
* one URI per invocation as part of their command-line, multiple instances
|
||||
* of the application will be spawned.
|
||||
*
|
||||
* To launch the application without arguments pass a %NULL @uris list.
|
||||
*
|
||||
@ -1379,6 +1381,10 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
|
||||
* fails. The startup notification id is provided, so that the launcher
|
||||
* can cancel the startup notification.
|
||||
*
|
||||
* Because a launch operation may involve spawning multiple instances of the
|
||||
* target application, you should expect this signal to be emitted multiple
|
||||
* times, one for each spawned instance.
|
||||
*
|
||||
* Since: 2.36
|
||||
*/
|
||||
signals[LAUNCH_FAILED] = g_signal_new (I_("launch-failed"),
|
||||
@ -1409,6 +1415,10 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
|
||||
* It is guaranteed that this signal is followed by either a #GAppLaunchContext::launched or
|
||||
* #GAppLaunchContext::launch-failed signal.
|
||||
*
|
||||
* Because a launch operation may involve spawning multiple instances of the
|
||||
* target application, you should expect this signal to be emitted multiple
|
||||
* times, one for each spawned instance.
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
signals[LAUNCH_STARTED] = g_signal_new (I_("launch-started"),
|
||||
@ -1430,7 +1440,13 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
|
||||
* @platform_data: additional platform-specific data for this launch
|
||||
*
|
||||
* The #GAppLaunchContext::launched signal is emitted when a #GAppInfo is successfully
|
||||
* launched. The @platform_data is an GVariant dictionary mapping
|
||||
* launched.
|
||||
*
|
||||
* Because a launch operation may involve spawning multiple instances of the
|
||||
* target application, you should expect this signal to be emitted multiple
|
||||
* times, one time for each spawned instance.
|
||||
*
|
||||
* The @platform_data is an GVariant dictionary mapping
|
||||
* strings to variants (ie `a{sv}`), which contains additional,
|
||||
* platform-specific data about this launch. On UNIX, at least the
|
||||
* `pid` and `startup-notification-id` keys will be present.
|
||||
|
@ -3261,9 +3261,8 @@ g_desktop_app_info_launch_uris (GAppInfo *appinfo,
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GAppInfo *appinfo;
|
||||
GList *uris;
|
||||
GAppLaunchContext *context;
|
||||
GList *uris; /* (element-type utf8) (owned) (nullable) */
|
||||
GAppLaunchContext *context; /* (owned) (nullable) */
|
||||
} LaunchUrisData;
|
||||
|
||||
static void
|
||||
@ -3280,13 +3279,13 @@ launch_uris_with_dbus_cb (GObject *object,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task = G_TASK (user_data);
|
||||
GError *error = NULL;
|
||||
GError *local_error = NULL;
|
||||
|
||||
g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
|
||||
if (error != NULL)
|
||||
g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &local_error);
|
||||
if (local_error != NULL)
|
||||
{
|
||||
g_dbus_error_strip_remote_error (error);
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
g_dbus_error_strip_remote_error (local_error);
|
||||
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||
}
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
@ -3316,7 +3315,7 @@ launch_uris_bus_get_cb (GObject *object,
|
||||
LaunchUrisData *data = g_task_get_task_data (task);
|
||||
GCancellable *cancellable = g_task_get_cancellable (task);
|
||||
GDBusConnection *session_bus;
|
||||
GError *error = NULL;
|
||||
GError *local_error = NULL;
|
||||
|
||||
session_bus = g_bus_get_finish (result, NULL);
|
||||
|
||||
@ -3342,10 +3341,10 @@ launch_uris_bus_get_cb (GObject *object,
|
||||
data->uris, data->context,
|
||||
_SPAWN_FLAGS_DEFAULT, NULL,
|
||||
NULL, NULL, NULL, -1, -1, -1,
|
||||
&error);
|
||||
if (error != NULL)
|
||||
&local_error);
|
||||
if (local_error != NULL)
|
||||
{
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||
g_object_unref (task);
|
||||
}
|
||||
else if (session_bus)
|
||||
@ -3379,7 +3378,7 @@ g_desktop_app_info_launch_uris_async (GAppInfo *appinfo,
|
||||
|
||||
data = g_new0 (LaunchUrisData, 1);
|
||||
data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
|
||||
data->context = (context != NULL) ? g_object_ref (context) : NULL;
|
||||
g_set_object (&data->context, context);
|
||||
g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
|
||||
|
||||
g_bus_get (G_BUS_TYPE_SESSION, cancellable, launch_uris_bus_get_cb, task);
|
||||
@ -3929,7 +3928,7 @@ run_update_command (char *command,
|
||||
NULL,
|
||||
};
|
||||
GPid pid = 0;
|
||||
GError *error = NULL;
|
||||
GError *local_error = NULL;
|
||||
|
||||
argv[0] = command;
|
||||
argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
|
||||
@ -3942,7 +3941,7 @@ run_update_command (char *command,
|
||||
G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
NULL, NULL, /* No setup function */
|
||||
&pid,
|
||||
&error))
|
||||
&local_error))
|
||||
g_child_watch_add (pid, update_program_done, NULL);
|
||||
else
|
||||
{
|
||||
@ -3952,8 +3951,8 @@ run_update_command (char *command,
|
||||
* dialog at this point, so we just do a g_warning to give the user a
|
||||
* chance of debugging it.
|
||||
*/
|
||||
g_warning ("%s", error->message);
|
||||
g_error_free (error);
|
||||
g_warning ("%s", local_error->message);
|
||||
g_error_free (local_error);
|
||||
}
|
||||
|
||||
g_free (argv[1]);
|
||||
|
@ -4685,43 +4685,334 @@ get_appath_for_exe (const gchar *exe_basename)
|
||||
return appath;
|
||||
}
|
||||
|
||||
/* GDesktopAppInfo::launch_uris_async emits all GAppLaunchContext's signals
|
||||
* on the main thread.
|
||||
*
|
||||
* We do the same: when g_win32_app_info_launch_uris_impl has a non-NULL
|
||||
* from_task argument we schedule the signal emissions on the main loop,
|
||||
* taking care not to emit signals after the task itself is completed
|
||||
* (see g_task_get_completed).
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
GAppLaunchContext *context; /* (owned) */
|
||||
GWin32AppInfo *info; /* (owned) */
|
||||
} EmitLaunchStartedData;
|
||||
|
||||
static void
|
||||
emit_launch_started_data_free (EmitLaunchStartedData *data)
|
||||
{
|
||||
g_clear_object (&data->context);
|
||||
g_clear_object (&data->info);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
emit_launch_started_cb (EmitLaunchStartedData *data)
|
||||
{
|
||||
g_signal_emit_by_name (data->context, "launch-started", data->info, NULL);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_launch_started (GAppLaunchContext *context,
|
||||
GWin32AppInfo *info,
|
||||
GTask *from_task)
|
||||
{
|
||||
if (!context || !info)
|
||||
return;
|
||||
|
||||
if (!from_task)
|
||||
g_signal_emit_by_name (context, "launch-started", info, NULL);
|
||||
else
|
||||
{
|
||||
EmitLaunchStartedData *data;
|
||||
|
||||
data = g_new (EmitLaunchStartedData, 1);
|
||||
data->context = g_object_ref (context);
|
||||
data->info = g_object_ref (info);
|
||||
|
||||
g_main_context_invoke_full (g_task_get_context (from_task),
|
||||
g_task_get_priority (from_task),
|
||||
G_SOURCE_FUNC (emit_launch_started_cb),
|
||||
g_steal_pointer (&data),
|
||||
(GDestroyNotify) emit_launch_started_data_free);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GAppLaunchContext *context; /* (owned) */
|
||||
GWin32AppInfo *info; /* (owned) */
|
||||
GPid pid; /* (owned) */
|
||||
} EmitLaunchedData;
|
||||
|
||||
static void
|
||||
emit_launched_data_free (EmitLaunchedData *data)
|
||||
{
|
||||
g_clear_object (&data->context);
|
||||
g_clear_object (&data->info);
|
||||
g_spawn_close_pid (data->pid);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static GVariant*
|
||||
make_platform_data (GPid pid)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
||||
/* pid handles are never bigger than 2^24 as per
|
||||
* https://docs.microsoft.com/en-us/windows/win32/sysinfo/kernel-objects,
|
||||
* so truncating to `int32` is valid.
|
||||
* The gsize cast is to silence a compiler warning
|
||||
* about conversion from pointer to integer of
|
||||
* different size. */
|
||||
g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gsize) pid));
|
||||
|
||||
return g_variant_ref_sink (g_variant_builder_end (&builder));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
emit_launched_cb (EmitLaunchedData *data)
|
||||
{
|
||||
|
||||
GVariant *platform_data = make_platform_data (data->pid);
|
||||
|
||||
g_signal_emit_by_name (data->context, "launched", data->info, platform_data);
|
||||
g_variant_unref (platform_data);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_launched (GAppLaunchContext *context,
|
||||
GWin32AppInfo *info,
|
||||
GPid *pid,
|
||||
GTask *from_task)
|
||||
{
|
||||
if (!context || !info)
|
||||
return;
|
||||
|
||||
if (!from_task)
|
||||
{
|
||||
GVariant *platform_data = make_platform_data (*pid);
|
||||
g_signal_emit_by_name (context, "launched", info, platform_data);
|
||||
g_variant_unref (platform_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLaunchedData *data;
|
||||
|
||||
data = g_new (EmitLaunchedData, 1);
|
||||
data->context = g_object_ref (context);
|
||||
data->info = g_object_ref (info);
|
||||
data->pid = *pid;
|
||||
|
||||
g_main_context_invoke_full (g_task_get_context (from_task),
|
||||
g_task_get_priority (from_task),
|
||||
G_SOURCE_FUNC (emit_launched_cb),
|
||||
g_steal_pointer (&data),
|
||||
(GDestroyNotify) emit_launched_data_free);
|
||||
}
|
||||
|
||||
*pid = 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GAppLaunchContext *context; /* (owned) */
|
||||
GWin32AppInfo *info; /* (owned) */
|
||||
} EmitLaunchFailedData;
|
||||
|
||||
static void
|
||||
emit_launch_failed_data_free (EmitLaunchFailedData *data)
|
||||
{
|
||||
g_clear_object (&data->context);
|
||||
g_clear_object (&data->info);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
emit_launch_failed_cb (EmitLaunchFailedData *data)
|
||||
{
|
||||
g_signal_emit_by_name (data->context, "launch-failed", data->info, NULL);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_launch_failed (GAppLaunchContext *context,
|
||||
GWin32AppInfo *info,
|
||||
GTask *from_task)
|
||||
{
|
||||
if (!context || !info)
|
||||
return;
|
||||
|
||||
if (!from_task)
|
||||
g_signal_emit_by_name (context, "launch-failed", info, NULL);
|
||||
else
|
||||
{
|
||||
EmitLaunchFailedData *data;
|
||||
|
||||
data = g_new (EmitLaunchFailedData, 1);
|
||||
data->context = g_object_ref (context);
|
||||
data->info = g_object_ref (info);
|
||||
|
||||
g_main_context_invoke_full (g_task_get_context (from_task),
|
||||
g_task_get_priority (from_task),
|
||||
G_SOURCE_FUNC (emit_launch_failed_cb),
|
||||
g_steal_pointer (&data),
|
||||
(GDestroyNotify) emit_launch_failed_data_free);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
|
||||
gboolean for_files,
|
||||
IShellItemArray *items,
|
||||
GWin32AppInfoShellVerb *shverb,
|
||||
GAppLaunchContext *launch_context,
|
||||
GTask *from_task,
|
||||
GError **error)
|
||||
{
|
||||
DWORD pid;
|
||||
IApplicationActivationManager* paam = NULL;
|
||||
gboolean result = TRUE;
|
||||
gboolean com_initialized = FALSE;
|
||||
gboolean result = FALSE;
|
||||
DWORD process_id = 0;
|
||||
HRESULT hr;
|
||||
const wchar_t *app_canonical_name = (const wchar_t *) info->app->canonical_name;
|
||||
|
||||
hr = CoCreateInstance (&CLSID_ApplicationActivationManager, NULL, CLSCTX_INPROC_SERVER, &IID_IApplicationActivationManager, (void **) &paam);
|
||||
/* ApplicationActivationManager threading model is both,
|
||||
* prefer the multithreaded apartment type, as we don't
|
||||
* need anything of the STA here. */
|
||||
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||
if (SUCCEEDED (hr))
|
||||
com_initialized = TRUE;
|
||||
else if (hr != RPC_E_CHANGED_MODE)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Failed to initialize the COM support library for the thread: 0x%lx", hr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* It's best to instantiate ApplicationActivationManager out-of-proc,
|
||||
* as documented on MSDN:
|
||||
*
|
||||
* An IApplicationActivationManager object creates a thread in its
|
||||
* host process to serve any activated event arguments objects
|
||||
* (LaunchActivatedEventArgs, FileActivatedEventArgs, and Protocol-
|
||||
* ActivatedEventArgs) that are passed to the app. If the calling
|
||||
* process is long-lived, you can create this object in-proc,
|
||||
* based on the assumption that the event arguments will exist long
|
||||
* enough for the target app to use them.
|
||||
* However, if the calling process is spawned only to launch the
|
||||
* target app, it should create the IApplicationActivationManager
|
||||
* object out-of-process, by using CLSCTX_LOCAL_SERVER. This causes
|
||||
* the object to be created in a Dllhost instance that automatically
|
||||
* manages the object's lifetime based on outstanding references to
|
||||
* the activated event argument objects.
|
||||
*/
|
||||
hr = CoCreateInstance (&CLSID_ApplicationActivationManager, NULL,
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
&IID_IApplicationActivationManager, (void **) &paam);
|
||||
if (FAILED (hr))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Failed to create ApplicationActivationManager: 0x%lx", hr);
|
||||
return FALSE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
emit_launch_started (launch_context, info, from_task);
|
||||
|
||||
/* The Activate methods return a process identifier (PID), so we should consider
|
||||
* those methods as potentially blocking */
|
||||
if (items == NULL)
|
||||
hr = IApplicationActivationManager_ActivateApplication (paam, (const wchar_t *) info->app->canonical_name, NULL, AO_NONE, &pid);
|
||||
hr = IApplicationActivationManager_ActivateApplication (paam,
|
||||
app_canonical_name,
|
||||
NULL, AO_NONE,
|
||||
&process_id);
|
||||
else if (for_files)
|
||||
hr = IApplicationActivationManager_ActivateForFile (paam, (const wchar_t *) info->app->canonical_name, items, shverb->verb_name, &pid);
|
||||
hr = IApplicationActivationManager_ActivateForFile (paam,
|
||||
app_canonical_name,
|
||||
items, shverb->verb_name,
|
||||
&process_id);
|
||||
else
|
||||
hr = IApplicationActivationManager_ActivateForProtocol (paam, (const wchar_t *) info->app->canonical_name, items, &pid);
|
||||
hr = IApplicationActivationManager_ActivateForProtocol (paam,
|
||||
app_canonical_name,
|
||||
items,
|
||||
&process_id);
|
||||
|
||||
if (FAILED (hr))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"The app %s failed to launch: 0x%lx",
|
||||
g_win32_appinfo_application_get_some_name (info->app), hr);
|
||||
result = FALSE;
|
||||
|
||||
emit_launch_failed (launch_context, info, from_task);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
else if (launch_context)
|
||||
{
|
||||
DWORD access_rights = 0;
|
||||
HANDLE process_handle = NULL;
|
||||
|
||||
/* Unfortunately, there's a race condition here.
|
||||
* ApplicationActivationManager methods return a process ID, but it
|
||||
* keeps no open HANDLE to the spawned process internally (tested
|
||||
* on Windows 10 21H2). So we cannot guarantee that by the time
|
||||
* OpenProcess is called, process ID still referes to the spawned
|
||||
* process. Anyway hitting such case is extremely unlikely.
|
||||
*
|
||||
* https://docs.microsoft.com/en-us/answers/questions/942879/
|
||||
* iapplicationactivationmanager-race-condition.html
|
||||
*
|
||||
* Maybe we could make use of the WinRT APIs to activate UWP apps,
|
||||
* instead? */
|
||||
|
||||
/* As documented on MSDN, the handle returned by CreateProcess has
|
||||
* PROCESS_ALL_ACCESS rights. First try passing PROCESS_ALL_ACCESS
|
||||
* to have the same access rights as the non-UWP code-path; should
|
||||
* that fail with ERROR_ACCESS_DENIED error code, retry using safe
|
||||
* access rights */
|
||||
access_rights = PROCESS_ALL_ACCESS;
|
||||
|
||||
process_handle = OpenProcess (access_rights, FALSE, process_id);
|
||||
|
||||
if (!process_handle && GetLastError () == ERROR_ACCESS_DENIED)
|
||||
{
|
||||
DWORD access_rights = PROCESS_QUERY_LIMITED_INFORMATION |
|
||||
SYNCHRONIZE;
|
||||
|
||||
process_handle = OpenProcess (access_rights, FALSE, process_id);
|
||||
}
|
||||
|
||||
if (!process_handle)
|
||||
{
|
||||
g_warning ("OpenProcess failed with error code %" G_GUINT32_FORMAT,
|
||||
(guint32) GetLastError ());
|
||||
}
|
||||
|
||||
/* Emit the launched signal regardless if we have the process
|
||||
* HANDLE or NULL */
|
||||
emit_launched (launch_context, info, (GPid*) &process_handle, from_task);
|
||||
|
||||
g_spawn_close_pid ((GPid) process_handle);
|
||||
}
|
||||
|
||||
result = TRUE;
|
||||
|
||||
cleanup:
|
||||
|
||||
if (paam)
|
||||
{
|
||||
IApplicationActivationManager_Release (paam);
|
||||
paam = NULL;
|
||||
}
|
||||
|
||||
if (com_initialized)
|
||||
{
|
||||
CoUninitialize ();
|
||||
com_initialized = FALSE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -4734,6 +5025,7 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
||||
IShellItemArray *items, /* UWP only */
|
||||
GAppLaunchContext *launch_context,
|
||||
GSpawnFlags spawn_flags,
|
||||
GTask *from_task,
|
||||
GError **error)
|
||||
{
|
||||
gboolean completed = FALSE;
|
||||
@ -4742,6 +5034,7 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
||||
const gchar *command;
|
||||
gchar *apppath;
|
||||
GWin32AppInfoShellVerb *shverb;
|
||||
GPid pid = NULL;
|
||||
|
||||
g_return_val_if_fail (info != NULL, FALSE);
|
||||
g_return_val_if_fail (info->app != NULL, FALSE);
|
||||
@ -4776,6 +5069,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
||||
for_files,
|
||||
items,
|
||||
shverb,
|
||||
launch_context,
|
||||
from_task,
|
||||
error);
|
||||
|
||||
if (launch_context)
|
||||
@ -4832,7 +5127,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
||||
|
||||
do
|
||||
{
|
||||
GPid pid;
|
||||
if (from_task && g_task_return_error_if_cancelled (from_task))
|
||||
goto out;
|
||||
|
||||
if (!expand_application_parameters (info,
|
||||
command,
|
||||
@ -4842,6 +5138,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
emit_launch_started (launch_context, info, from_task);
|
||||
|
||||
if (!g_spawn_async (NULL,
|
||||
argv,
|
||||
envp,
|
||||
@ -4851,28 +5149,16 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
||||
NULL,
|
||||
&pid,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (launch_context != NULL)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
GVariant *platform_data;
|
||||
emit_launch_failed (launch_context, info, from_task);
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
||||
/* pid handles are never bigger than 2^24 as per
|
||||
* https://docs.microsoft.com/en-us/windows/win32/sysinfo/kernel-objects,
|
||||
* so truncating to `int32` is valid.
|
||||
* The gsize cast is to silence a compiler warning
|
||||
* about conversion from pointer to integer of
|
||||
* different size. */
|
||||
g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gsize) pid));
|
||||
|
||||
platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
|
||||
g_signal_emit_by_name (launch_context, "launched", info, platform_data);
|
||||
g_variant_unref (platform_data);
|
||||
goto out;
|
||||
}
|
||||
else if (launch_context)
|
||||
emit_launched (launch_context, info, &pid, from_task);
|
||||
|
||||
g_spawn_close_pid (pid);
|
||||
pid = NULL;
|
||||
g_strfreev (argv);
|
||||
argv = NULL;
|
||||
}
|
||||
@ -4880,7 +5166,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
||||
|
||||
completed = TRUE;
|
||||
|
||||
out:
|
||||
out:
|
||||
g_spawn_close_pid (pid);
|
||||
g_strfreev (argv);
|
||||
g_strfreev (envp);
|
||||
|
||||
@ -5028,9 +5315,10 @@ make_item_array (gboolean for_files,
|
||||
|
||||
|
||||
static gboolean
|
||||
g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
||||
g_win32_app_info_launch_uris_impl (GAppInfo *appinfo,
|
||||
GList *uris,
|
||||
GAppLaunchContext *launch_context,
|
||||
GTask *from_task,
|
||||
GError **error)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
@ -5049,7 +5337,7 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
||||
return res;
|
||||
}
|
||||
|
||||
res = g_win32_app_info_launch_internal (info, NULL, FALSE, items, launch_context, 0, error);
|
||||
res = g_win32_app_info_launch_internal (info, NULL, FALSE, items, launch_context, 0, from_task, error);
|
||||
|
||||
if (items != NULL)
|
||||
IShellItemArray_Release (items);
|
||||
@ -5090,6 +5378,7 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
||||
NULL,
|
||||
launch_context,
|
||||
G_SPAWN_SEARCH_PATH,
|
||||
from_task,
|
||||
error);
|
||||
|
||||
g_list_free_full (objs, free_file_or_uri);
|
||||
@ -5097,6 +5386,80 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
||||
GList *uris,
|
||||
GAppLaunchContext *launch_context,
|
||||
GError **error)
|
||||
{
|
||||
return g_win32_app_info_launch_uris_impl (appinfo, uris, launch_context, NULL, error);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GList *uris; /* (element-type utf8) (owned) (nullable) */
|
||||
GAppLaunchContext *context; /* (owned) (nullable) */
|
||||
} LaunchUrisData;
|
||||
|
||||
static void
|
||||
launch_uris_data_free (LaunchUrisData *data)
|
||||
{
|
||||
g_clear_object (&data->context);
|
||||
g_list_free_full (data->uris, g_free);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
launch_uris_async_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GAppInfo *appinfo = G_APP_INFO (source_object);
|
||||
LaunchUrisData *data = task_data;
|
||||
GError *local_error = NULL;
|
||||
gboolean succeeded;
|
||||
|
||||
succeeded = g_win32_app_info_launch_uris_impl (appinfo, data->uris, data->context, task, &local_error);
|
||||
if (succeeded)
|
||||
g_task_return_boolean (task, TRUE);
|
||||
else if (!g_task_had_error (task))
|
||||
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||
}
|
||||
|
||||
static void
|
||||
g_win32_app_info_launch_uris_async (GAppInfo *appinfo,
|
||||
GList *uris,
|
||||
GAppLaunchContext *context,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
LaunchUrisData *data;
|
||||
|
||||
task = g_task_new (appinfo, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, g_win32_app_info_launch_uris_async);
|
||||
|
||||
data = g_new0 (LaunchUrisData, 1);
|
||||
data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
|
||||
g_set_object (&data->context, context);
|
||||
g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
|
||||
|
||||
g_task_run_in_thread (task, launch_uris_async_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_win32_app_info_launch_uris_finish (GAppInfo *appinfo,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, appinfo), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_win32_app_info_should_show (GAppInfo *appinfo)
|
||||
{
|
||||
@ -5128,7 +5491,7 @@ g_win32_app_info_launch (GAppInfo *appinfo,
|
||||
return res;
|
||||
}
|
||||
|
||||
res = g_win32_app_info_launch_internal (info, NULL, TRUE, items, launch_context, 0, error);
|
||||
res = g_win32_app_info_launch_internal (info, NULL, TRUE, items, launch_context, 0, NULL, error);
|
||||
|
||||
if (items != NULL)
|
||||
IShellItemArray_Release (items);
|
||||
@ -5160,6 +5523,7 @@ g_win32_app_info_launch (GAppInfo *appinfo,
|
||||
NULL,
|
||||
launch_context,
|
||||
G_SPAWN_SEARCH_PATH,
|
||||
NULL,
|
||||
error);
|
||||
|
||||
g_list_free_full (objs, free_file_or_uri);
|
||||
@ -5242,6 +5606,8 @@ g_win32_app_info_iface_init (GAppInfoIface *iface)
|
||||
iface->supports_uris = g_win32_app_info_supports_uris;
|
||||
iface->supports_files = g_win32_app_info_supports_files;
|
||||
iface->launch_uris = g_win32_app_info_launch_uris;
|
||||
iface->launch_uris_async = g_win32_app_info_launch_uris_async;
|
||||
iface->launch_uris_finish = g_win32_app_info_launch_uris_finish;
|
||||
iface->should_show = g_win32_app_info_should_show;
|
||||
/* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
|
||||
/* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
|
||||
|
Loading…
Reference in New Issue
Block a user