diff --git a/CHANGELOG.md b/CHANGELOG.md index f1c5ef39..b8df3300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Fix some GTK3 memory leaks - Add GTK4 backend behind the `gtk4` feature - MSRV is now 1.92 - Fix macOS file dialogs ignoring `set_directory` when `set_file_name` is also used. diff --git a/src/backend/gtk3/file_dialog/dialog_ffi.rs b/src/backend/gtk3/file_dialog/dialog_ffi.rs index 544eaad9..971b8165 100644 --- a/src/backend/gtk3/file_dialog/dialog_ffi.rs +++ b/src/backend/gtk3/file_dialog/dialog_ffi.rs @@ -3,8 +3,9 @@ use crate::FileDialog; use gtk_sys::GtkFileChooserNative; use std::{ - ffi::{CStr, CString}, + ffi::{c_char, CStr, CString, OsStr}, ops::Deref, + os::unix::ffi::OsStrExt, path::{Path, PathBuf}, }; @@ -110,55 +111,32 @@ impl GtkFileDialog { } pub fn get_result(&self) -> Option { - let cstr = unsafe { - let chosen_filename = gtk_sys::gtk_file_chooser_get_filename(self.ptr as _); - CStr::from_ptr(chosen_filename).to_str() - }; + unsafe { + let filename = gtk_sys::gtk_file_chooser_get_filename(self.ptr as _); + if filename.is_null() { + return None; + } - if let Ok(cstr) = cstr { - Some(PathBuf::from(cstr.to_owned())) - } else { - None + let path = filename_to_path(filename); + glib_sys::g_free(filename as _); + Some(path) } } pub fn get_results(&self) -> Vec { - #[derive(Debug)] - struct FileList(*mut glib_sys::GSList); - - impl Iterator for FileList { - type Item = glib_sys::GSList; - fn next(&mut self) -> Option { - let curr_ptr = self.0; - - if !curr_ptr.is_null() { - let curr = unsafe { *curr_ptr }; - - self.0 = curr.next; + unsafe { + let filenames = gtk_sys::gtk_file_chooser_get_filenames(self.ptr as _); - Some(curr) - } else { - None - } + let mut paths = Vec::new(); + let mut current = filenames; + while !current.is_null() { + paths.push(filename_to_path((*current).data as *const c_char)); + current = (*current).next; } - } - - let chosen_filenames = - unsafe { gtk_sys::gtk_file_chooser_get_filenames(self.ptr as *mut _) }; - - let paths: Vec = FileList(chosen_filenames) - .filter_map(|item| { - let cstr = unsafe { CStr::from_ptr(item.data as _).to_str() }; - - if let Ok(cstr) = cstr { - Some(PathBuf::from(cstr.to_owned())) - } else { - None - } - }) - .collect(); - paths + glib_sys::g_slist_free_full(filenames, Some(glib_sys::g_free)); + paths + } } pub fn run(&self) -> i32 { @@ -166,6 +144,10 @@ impl GtkFileDialog { } } +unsafe fn filename_to_path(filename: *const c_char) -> PathBuf { + PathBuf::from(OsStr::from_bytes(CStr::from_ptr(filename).to_bytes())) +} + fn parent_gtk_window(opt: &FileDialog) -> *mut gtk_sys::GtkWindow { if let Some(parent_handle) = &opt.parent { unsafe { super::super::utils::find_gtk_window(parent_handle) } diff --git a/src/backend/gtk3/message_dialog.rs b/src/backend/gtk3/message_dialog.rs index b079606a..a886bd77 100644 --- a/src/backend/gtk3/message_dialog.rs +++ b/src/backend/gtk3/message_dialog.rs @@ -168,14 +168,16 @@ unsafe fn is_label(type_instance: *const gobject_sys::GTypeInstance) -> bool { /// Sets the child labels of a widget selectable unsafe fn set_child_labels_selectable(dialog: *mut gtk_sys::GtkDialog) { let area = gtk_sys::gtk_message_dialog_get_message_area(dialog as _); - let mut children = gtk_sys::gtk_container_get_children(area as _); - while !children.is_null() { - let child = (*children).data; + let children = gtk_sys::gtk_container_get_children(area as _); + let mut current = children; + while !current.is_null() { + let child = (*current).data; if is_label(child as _) { gtk_sys::gtk_label_set_selectable(child as _, 1); } - children = (*children).next; + current = (*current).next; } + glib_sys::g_list_free(children); } impl Drop for GtkMessageDialog {