Skip to content

Commit 0b8b473

Browse files
committed
Adds a dialog to unmount devices when using Wayland.
1 parent 99c7644 commit 0b8b473

File tree

1 file changed

+216
-1
lines changed

1 file changed

+216
-1
lines changed

src/nemo-places-sidebar.c

Lines changed: 216 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include <gdk/gdkkeysyms.h>
2828
#include <gtk/gtk.h>
29+
#include <gdk/gdkwayland.h>
2930
#include <glib/gi18n.h>
3031
#include <gio/gio.h>
3132
#include <math.h>
@@ -148,6 +149,7 @@ typedef struct {
148149
guint expand_timeout_source;
149150
guint popup_menu_action_index;
150151
guint update_places_on_idle_id;
152+
gboolean unmount_dialog_active;
151153

152154
} NemoPlacesSidebar;
153155

@@ -217,6 +219,7 @@ static void nemo_places_sidebar_style_set (GtkWidget
217219
static gboolean eject_or_unmount_bookmark (NemoPlacesSidebar *sidebar,
218220
GtkTreePath *path);
219221
static gboolean eject_or_unmount_selection (NemoPlacesSidebar *sidebar);
222+
static gboolean idle_unmount_dialog (gpointer user_data);
220223
static void check_unmount_and_eject (GMount *mount,
221224
GVolume *volume,
222225
GDrive *drive,
@@ -227,6 +230,7 @@ static void update_places (NemoPlacesSidebar *sideb
227230
static void update_places_on_idle (NemoPlacesSidebar *sidebar);
228231
static void rebuild_menu (NemoPlacesSidebar *sidebar);
229232
static void actions_changed (gpointer user_data);
233+
230234
/* Identifiers for target types */
231235
enum {
232236
GTK_TREE_MODEL_ROW,
@@ -2739,6 +2743,211 @@ show_unmount_progress_aborted_cb (GMountOperation *op,
27392743
nemo_application_notify_unmount_done (app, NULL);
27402744
}
27412745

2746+
// Start code for unmount dialog on Wayland
2747+
typedef struct {
2748+
GtkMountOperation *op;
2749+
GtkWindow *parent_window;
2750+
NemoPlacesSidebar *sidebar_ref;
2751+
gboolean cancelled;
2752+
gchar *primary_text;
2753+
gchar *secondary_text;
2754+
GArray *processes;
2755+
gint ref_count;
2756+
} UnmountDialogData;
2757+
2758+
static UnmountDialogData*
2759+
unmount_dialog_data_ref (UnmountDialogData *data)
2760+
{
2761+
if (data) {
2762+
data->ref_count++;
2763+
}
2764+
return data;
2765+
}
2766+
2767+
static void
2768+
unmount_dialog_data_unref (UnmountDialogData *data)
2769+
{
2770+
if (data == NULL) {
2771+
return;
2772+
}
2773+
2774+
data->ref_count--;
2775+
if (data->ref_count == 0) {
2776+
if (data->op) {
2777+
g_object_unref (G_OBJECT (data->op));
2778+
}
2779+
if (data->processes) {
2780+
g_array_free (data->processes, TRUE);
2781+
}
2782+
if (data->sidebar_ref) {
2783+
g_object_unref (data->sidebar_ref);
2784+
}
2785+
g_free (data->primary_text);
2786+
g_free (data->secondary_text);
2787+
g_free (data);
2788+
}
2789+
}
2790+
2791+
static void
2792+
mount_op_finalized_cb (gpointer user_data,
2793+
GObject *where_the_object_was)
2794+
{
2795+
UnmountDialogData *data = (UnmountDialogData *)user_data;
2796+
if (data && data->sidebar_ref) {
2797+
NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR(data->sidebar_ref);
2798+
if (NEMO_IS_PLACES_SIDEBAR(sidebar)) {
2799+
sidebar->unmount_dialog_active = FALSE;
2800+
}
2801+
}
2802+
2803+
if (data) {
2804+
data->cancelled = TRUE;
2805+
if (data->op == GTK_MOUNT_OPERATION(where_the_object_was)) {
2806+
data->op = NULL;
2807+
} else {
2808+
g_warning("mount_op_finalized_cb: Finalized GtkMountOperation (0x%p) is not the one stored in UnmountDialogData (0x%p). This is unexpected.",
2809+
where_the_object_was, data->op);
2810+
if (data->op != NULL) {
2811+
data->op = NULL;
2812+
}
2813+
}
2814+
}
2815+
unmount_dialog_data_unref(data);
2816+
}
2817+
2818+
static void
2819+
on_show_processes_wayland_workaround (GtkMountOperation *op,
2820+
const gchar *message,
2821+
GArray *processes,
2822+
GArray *choices,
2823+
gpointer user_data)
2824+
{
2825+
NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR(user_data);
2826+
2827+
if (sidebar->unmount_dialog_active) {
2828+
g_signal_stop_emission_by_name(op, "show-processes");
2829+
return;
2830+
}
2831+
sidebar->unmount_dialog_active = TRUE;
2832+
2833+
/* Schedule unmount dialog in main loop to avoid Wayland reentrancy */
2834+
UnmountDialogData *data = g_new0 (UnmountDialogData, 1);
2835+
data->op = GTK_MOUNT_OPERATION (op);
2836+
g_object_ref (G_OBJECT (op));
2837+
data->cancelled = FALSE;
2838+
data->sidebar_ref = g_object_ref (sidebar);
2839+
data->parent_window = GTK_WINDOW (sidebar->window);
2840+
/* Split message into first line and rest */
2841+
char *newline_pos = strchr(message, '\n');
2842+
if (newline_pos != NULL) {
2843+
/* Found a newline - split the message */
2844+
int first_line_len = newline_pos - message;
2845+
data->primary_text = g_strndup(message, first_line_len);
2846+
data->secondary_text = g_strdup(newline_pos);
2847+
} else {
2848+
/* No newline found - use whole message as primary text */
2849+
data->primary_text = g_strdup(message);
2850+
}
2851+
/* Process using volume */
2852+
if (processes && processes->len > 0) {
2853+
GArray *filtered = g_array_new (FALSE, FALSE, sizeof (GPid));
2854+
for (guint i = 0; i < processes->len; ++i) {
2855+
GPid pid = g_array_index (processes, GPid, i);
2856+
g_array_append_val (filtered, pid);
2857+
}
2858+
data->processes = filtered;
2859+
} else {
2860+
data->processes = NULL;
2861+
}
2862+
2863+
/* Set a weak reference on the mount operation to know if it gets destroyed */
2864+
g_object_weak_ref (G_OBJECT (op), mount_op_finalized_cb, unmount_dialog_data_ref(data));
2865+
g_signal_stop_emission_by_name (op, "show-processes");
2866+
g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_unmount_dialog, unmount_dialog_data_ref(data), NULL);
2867+
}
2868+
2869+
static gboolean
2870+
idle_unmount_dialog (gpointer user_data)
2871+
{
2872+
UnmountDialogData *data = user_data;
2873+
gint response = GTK_RESPONSE_NONE;
2874+
2875+
/* If data->op is NULL, it means mount_op_finalized_cb was called and nulled it.
2876+
* If data->cancelled is TRUE, it also means mount_op_finalized_cb was called.
2877+
*/
2878+
if (data->op == NULL || data->cancelled) {
2879+
if (data->sidebar_ref) { // Reset flag if sidebar still exists
2880+
NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR(data->sidebar_ref);
2881+
if (NEMO_IS_PLACES_SIDEBAR(sidebar)) {
2882+
sidebar->unmount_dialog_active = FALSE;
2883+
}
2884+
}
2885+
return G_SOURCE_REMOVE;
2886+
}
2887+
2888+
if (data->processes && data->processes->len > 0) {
2889+
GString *plist = g_string_new ("");
2890+
g_string_append (plist, "\n");
2891+
for (guint i = 0; i < data->processes->len; i++) {
2892+
GPid pid = g_array_index (data->processes, GPid, i);
2893+
gchar *comm = NULL;
2894+
gchar procfile[64];
2895+
g_snprintf (procfile, sizeof(procfile), "/proc/%d/comm", pid);
2896+
if (!g_file_get_contents (procfile, &comm, NULL, NULL)) {
2897+
g_free (comm);
2898+
comm = g_strdup_printf ("%d", pid);
2899+
} else {
2900+
g_strchomp (comm);
2901+
}
2902+
g_string_append_printf (plist, "%s\n", comm);
2903+
g_free (comm);
2904+
}
2905+
gchar *plist_text = g_string_free (plist, FALSE);
2906+
gchar *new_secondary = g_strdup_printf ("%s\n%s", data->secondary_text, plist_text);
2907+
g_free (plist_text);
2908+
g_free (data->secondary_text);
2909+
data->secondary_text = new_secondary;
2910+
}
2911+
2912+
GtkWindow *parent = data->parent_window;
2913+
GtkWidget *dialog = gtk_message_dialog_new (parent,
2914+
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2915+
GTK_MESSAGE_WARNING,
2916+
GTK_BUTTONS_NONE,
2917+
"%s", data->primary_text);
2918+
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", data->secondary_text);
2919+
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2920+
_("_Unmount"), GTK_RESPONSE_YES,
2921+
_("_Cancel"), GTK_RESPONSE_NO,
2922+
NULL);
2923+
gtk_widget_show_all (dialog);
2924+
response = gtk_dialog_run (GTK_DIALOG (dialog));
2925+
gtk_widget_destroy (dialog);
2926+
if (data->sidebar_ref) { // Reset flag now that dialog is done
2927+
NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR(data->sidebar_ref);
2928+
if (NEMO_IS_PLACES_SIDEBAR(sidebar)) {
2929+
sidebar->unmount_dialog_active = FALSE;
2930+
}
2931+
}
2932+
2933+
/* data->op could have become NULL if mount_op_finalized_cb was called
2934+
* while gtk_dialog_run() was blocking.
2935+
*/
2936+
if (data->op == NULL || data->cancelled) {
2937+
g_warning("Mount operation was cancelled or finalized while dialog was open. Not replying.");
2938+
} else {
2939+
/* data->op is still valid here, and data->cancelled is false */
2940+
GMountOperation *mount_op = G_MOUNT_OPERATION(data->op);
2941+
if (response == GTK_RESPONSE_YES)
2942+
g_mount_operation_reply (mount_op, G_MOUNT_OPERATION_HANDLED);
2943+
else
2944+
g_mount_operation_reply (mount_op, G_MOUNT_OPERATION_ABORTED);
2945+
}
2946+
2947+
// End wayland code for umount dialog
2948+
return G_SOURCE_REMOVE;
2949+
}
2950+
27422951
static GMountOperation *
27432952
get_unmount_operation (NemoPlacesSidebar *sidebar)
27442953
{
@@ -2749,7 +2958,11 @@ get_unmount_operation (NemoPlacesSidebar *sidebar)
27492958
G_CALLBACK (show_unmount_progress_cb), sidebar);
27502959
g_signal_connect (mount_op, "aborted",
27512960
G_CALLBACK (show_unmount_progress_aborted_cb), sidebar);
2752-
2961+
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(sidebar));
2962+
if (GDK_IS_WAYLAND_DISPLAY (display)) {
2963+
g_signal_connect (mount_op, "show-processes",
2964+
G_CALLBACK (on_show_processes_wayland_workaround), sidebar);
2965+
}
27532966
return mount_op;
27542967
}
27552968

@@ -4130,6 +4343,8 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar)
41304343

41314344
sidebar->update_places_on_idle_id = 0;
41324345

4346+
sidebar->unmount_dialog_active = FALSE;
4347+
41334348
sidebar->my_computer_expanded = g_settings_get_boolean (nemo_window_state,
41344349
NEMO_WINDOW_STATE_MY_COMPUTER_EXPANDED);
41354350
sidebar->bookmarks_expanded = g_settings_get_boolean (nemo_window_state,

0 commit comments

Comments
 (0)