From 50caed30c09632f861c6667e569825683716823a Mon Sep 17 00:00:00 2001 From: Stu Black Date: Fri, 14 Nov 2025 01:07:40 -0500 Subject: [PATCH 1/4] Add a wings-rs crate for porting WINGs proper to Rust. --- Makefile.am | 2 +- configure.ac | 1 + wings-rs/Cargo.toml | 7 +++++++ wings-rs/Makefile.am | 19 +++++++++++++++++++ wings-rs/src/lib.rs | 14 ++++++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 wings-rs/Cargo.toml create mode 100644 wings-rs/Makefile.am create mode 100644 wings-rs/src/lib.rs diff --git a/Makefile.am b/Makefile.am index 0a50bf18..10837d32 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,7 +39,7 @@ ACLOCAL_AMFLAGS = -I m4 AM_DISTCHECK_CONFIGURE_FLAGS = --enable-silent-rules LINGUAS='*' -SUBDIRS = wrlib wutil-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc +SUBDIRS = wrlib wutil-rs wings-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc DIST_SUBDIRS = $(SUBDIRS) test EXTRA_DIST = TODO BUGS BUGFORM FAQ INSTALL \ diff --git a/configure.ac b/configure.ac index 78313976..aed888ef 100644 --- a/configure.ac +++ b/configure.ac @@ -959,6 +959,7 @@ AC_CONFIG_FILES( dnl Rust implementation of WINGs libraries wutil-rs/Makefile + wings-rs/Makefile dnl WINGs toolkit WINGs/Makefile WINGs/WINGs/Makefile WINGs/po/Makefile diff --git a/wings-rs/Cargo.toml b/wings-rs/Cargo.toml new file mode 100644 index 00000000..2a743277 --- /dev/null +++ b/wings-rs/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wings-rs" +version = "0.1.0" +edition = "2024" + +[dependencies] +wutil-rs = { path = "../wutil-rs" } diff --git a/wings-rs/Makefile.am b/wings-rs/Makefile.am new file mode 100644 index 00000000..2aa61a59 --- /dev/null +++ b/wings-rs/Makefile.am @@ -0,0 +1,19 @@ +AUTOMAKE_OPTIONS = + +RUST_SOURCES = \ + src/lib.rs + +RUST_EXTRA = \ + Cargo.lock \ + Cargo.toml + +target/debug/libwings_rs.a: $(RUST_SOURCES) $(RUST_EXTRA) + $(CARGO) build + +check-local: + $(CARGO) test + +clean-local: + $(CARGO) clean + +all: target/debug/libwings_rs.a diff --git a/wings-rs/src/lib.rs b/wings-rs/src/lib.rs new file mode 100644 index 00000000..b93cf3ff --- /dev/null +++ b/wings-rs/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} -- 2.39.5 From bd18e0c60085e8e9d9ffa89c983de8426c42b345 Mon Sep 17 00:00:00 2001 From: Stu Black Date: Fri, 21 Nov 2025 16:01:41 -0500 Subject: [PATCH 2/4] add some hacky lines to start-captive-wmaker.sh to ease debugging of X11 integration from Emacs --- start-captive-wmaker.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/start-captive-wmaker.sh b/start-captive-wmaker.sh index 2afb4c33..35278414 100755 --- a/start-captive-wmaker.sh +++ b/start-captive-wmaker.sh @@ -89,6 +89,8 @@ Xephyr -screen 640x480 "$xephyr_display" & xephyr_pid=$! DISPLAY="$xephyr_display" gdb \ --directory "$project_base" \ + --directory "$HOME/src/libX11-1.5.0/build/src" \ --quiet \ + --fullname \ --args "$WindowMaker" -display "$xephyr_display" --for-real "$@" kill $xephyr_pid -- 2.39.5 From f8df6447eaab8c25fd238897433584bac17fb912 Mon Sep 17 00:00:00 2001 From: Stu Black Date: Mon, 24 Nov 2025 16:19:40 -0500 Subject: [PATCH 3/4] Store addresses insead of pointers in WMArray. Prior to this patch, when WINGs/Tests/wtest.c tries to build a FontPanel, it crashes. This is because the rewritten WMSortArray passes pointers to array items to its comparator function, but the original API passed pointers to pointers to array items to its comparator function. This has been corrected, and now WMSortArray should behave more like it originally did. This patch also stores `usize` addresses instead of `NonNull` pointers because some WMArray use sites actually store non-pointer data (by casting it to uintptr_t or similar). --- wutil-rs/src/array.rs | 68 ++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/wutil-rs/src/array.rs b/wutil-rs/src/array.rs index 42a0d45b..acae7c7a 100644 --- a/wutil-rs/src/array.rs +++ b/wutil-rs/src/array.rs @@ -1,7 +1,7 @@ -use std::{ffi::c_void, ptr::NonNull}; +use std::ffi::c_void; pub struct Array { - items: Vec>, + items: Vec, destructor: Option, } @@ -10,7 +10,7 @@ pub mod ffi { use std::{ ffi::{c_int, c_void}, - ptr::{self, NonNull}, + ptr, }; pub const NOT_FOUND: c_int = -1; @@ -64,7 +64,7 @@ pub mod ffi { let array = unsafe { &mut *array }; if let Some(f) = array.destructor { for item in &mut array.items { - unsafe { (f)(item.as_ptr()) } + unsafe { (f)(*item as *mut c_void) } } } array.items.clear(); @@ -94,10 +94,8 @@ pub mod ffi { if array.is_null() { return; } - if let Some(item) = NonNull::new(item) { - unsafe { - (*array).items.push(item); - } + unsafe { + (*array).items.push(item.addr()); } } @@ -114,9 +112,7 @@ pub mod ffi { if index >= array.len() { return; } - if let Some(item) = NonNull::new(item) { - array.insert(index, item); - } + array.insert(index, item.addr()); } #[unsafe(no_mangle)] @@ -141,15 +137,11 @@ pub mod ffi { return ptr::null_mut(); } - let item = match NonNull::new(item) { - Some(x) => x, - None => return ptr::null_mut(), - }; let array = unsafe { &mut (*array).items }; let old = array[index]; - array[index] = item; - old.as_ptr() + array[index] = item.addr(); + old as *mut c_void } #[unsafe(no_mangle)] @@ -168,7 +160,7 @@ pub mod ffi { let old = array.items.remove(index); if let Some(f) = array.destructor { unsafe { - (f)(old.as_ptr()); + (f)(old as *mut c_void); } } 1 @@ -192,8 +184,8 @@ pub mod ffi { let array = unsafe { &mut *array }; let original_len = array.items.len(); match pred { - Some(f) => array.items.retain(|x| unsafe { f(x.as_ptr(), cdata) != 0 }), - None => array.items.retain(|x| ptr::eq(x.as_ptr(), cdata)), + Some(f) => array.items.retain(|x| unsafe { f(*x as *const c_void, cdata) != 0 }), + None => array.items.retain(|x| ptr::eq(*x as *mut c_void, cdata)), } (original_len - array.items.len()) as c_int } @@ -207,7 +199,7 @@ pub mod ffi { (&(*array)) .items .get(index as usize) - .map(|p| p.as_ptr()) + .map(|p| *p as *mut c_void) .unwrap_or(ptr::null_mut()) } } @@ -226,7 +218,7 @@ pub mod ffi { (*array) .items .pop() - .map(|p| p.as_ptr()) + .map(|p| p as *mut c_void) .unwrap_or(ptr::null_mut()) } } @@ -246,7 +238,7 @@ pub mod ffi { .items .iter() .enumerate() - .find(|(_, item)| unsafe { f(item.as_ptr(), cdata) != 0 }) + .find(|(_, item)| unsafe { f(**item as *const c_void, cdata) != 0 }) .map(|(i, _)| i as c_int) .unwrap_or(NOT_FOUND) } else { @@ -254,7 +246,7 @@ pub mod ffi { .items .iter() .enumerate() - .find(|(_, item)| ptr::eq(item.as_ptr(), cdata)) + .find(|(_, item)| ptr::eq(**item as *const c_void, cdata)) .map(|(i, _)| i as c_int) .unwrap_or(NOT_FOUND) } @@ -269,7 +261,7 @@ pub mod ffi { array .items .iter() - .filter(|x| ptr::eq(x.as_ptr(), item)) + .filter(|x| ptr::eq(**x as *const c_void, item)) .count() as c_int } @@ -284,13 +276,17 @@ pub mod ffi { unsafe { (*array) .items - .sort_by(|&a, &b| match comparator(a.as_ptr(), b.as_ptr()).signum() { - -1 => std::cmp::Ordering::Less, - 0 => std::cmp::Ordering::Equal, - 1 => std::cmp::Ordering::Greater, - _ => unreachable!(), + .sort_by(|a, b| { + let a = a as *const _ as *const c_void; + let b = b as *const _ as *const c_void; + match comparator(a, b).signum() { + -1 => std::cmp::Ordering::Less, + 0 => std::cmp::Ordering::Equal, + 1 => std::cmp::Ordering::Greater, + _ => unreachable!(), + } }) - } + } } #[unsafe(no_mangle)] @@ -304,7 +300,7 @@ pub mod ffi { } unsafe { for a in &mut (*array).items { - (f)(a.as_ptr(), data); + (f)(*a as *mut c_void, data); } } } @@ -326,7 +322,7 @@ pub mod ffi { unsafe { *iter = 0; } - x.as_ptr() + *x as *mut c_void } } } @@ -348,7 +344,7 @@ pub mod ffi { unsafe { *iter = (array.items.len() - 1) as c_int; } - x.as_ptr() + *x as *mut c_void } } } @@ -368,7 +364,7 @@ pub mod ffi { unsafe { *iter += 1; } - i.as_ptr() + *i as *mut c_void } None => { unsafe { @@ -394,7 +390,7 @@ pub mod ffi { unsafe { *iter -= 1; } - i.as_ptr() + *i as *mut c_void } None => { unsafe { -- 2.39.5 From e0fc92bf51dc3b71d14683dc8ecd16a83df58873 Mon Sep 17 00:00:00 2001 From: Stu Black Date: Mon, 24 Nov 2025 22:15:32 -0500 Subject: [PATCH 4/4] Fix omissions in WMHashEnumerator C interface. The WMHashTable rewrite branch was prematurely merged. This patch fixes some of the things that were overlooked in that merge. WMHashEnumerator should be an opaque type that is free'd after it is created. We hope to axe WMHashTable entirely, but it's reasonable to get this fix in for now so that the WMFontPanel integration test in WINGs/Tests can run on top of the changes we've made so far. As of this commit, it's still broken, but it behaves better than it did. --- WINGs/WINGs/WUtil.h | 13 ++++------- WINGs/wballoon.c | 6 +++-- WINGs/wfontpanel.c | 5 +++-- wutil-rs/src/hash_table.rs | 46 +++++++++++++++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/WINGs/WINGs/WUtil.h b/WINGs/WINGs/WUtil.h index 18c8b52b..62e33dca 100644 --- a/WINGs/WINGs/WUtil.h +++ b/WINGs/WINGs/WUtil.h @@ -156,12 +156,7 @@ typedef struct { -/* DO NOT ACCESS THE CONTENTS OF THIS STRUCT */ -typedef struct { - void *table; - void *nextItem; - int index; -} WMHashEnumerator; +typedef struct WMHashEnumerator WMHashEnumerator; typedef struct { @@ -334,7 +329,7 @@ void WMDeleteInputHandler(WMHandlerID handlerID); void WHandleEvents(void); -/* ---[ WINGs/hashtable.c ]----------------------------------------------- */ +/* ---[ wutil-rs/src/hash_table.rs ]----------------------------------------------- */ WMHashTable* WMCreateIdentityHashTable(); @@ -366,7 +361,7 @@ void* WMHashInsert(WMHashTable *table, const void *key, const void *data); void WMHashRemove(WMHashTable *table, const void *key); /* warning: do not manipulate the table while using the enumerator functions */ -WMHashEnumerator WMEnumerateHashTable(WMHashTable *table); +WMHashEnumerator* WMEnumerateHashTable(WMHashTable *table); void* WMNextHashEnumeratorItem(WMHashEnumerator *enumerator); @@ -381,7 +376,7 @@ Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator *enumerator, void **item, void **key); - +void WMFreeHashEnumerator(WMHashEnumerator *enumerator); /* some predefined callback sets */ diff --git a/WINGs/wballoon.c b/WINGs/wballoon.c index 8a9c8a84..3e57e530 100644 --- a/WINGs/wballoon.c +++ b/WINGs/wballoon.c @@ -456,12 +456,12 @@ static void handleEvents(XEvent * event, void *data) static void destroyBalloon(Balloon * bPtr) { - WMHashEnumerator e; + WMHashEnumerator *e; char *str; e = WMEnumerateHashTable(bPtr->table); - while ((str = WMNextHashEnumeratorItem(&e))) { + while ((str = WMNextHashEnumeratorItem(e))) { wfree(str); } WMFreeHashTable(bPtr->table); @@ -472,5 +472,7 @@ static void destroyBalloon(Balloon * bPtr) if (bPtr->font) WMReleaseFont(bPtr->font); + WMFreeHashEnumerator(e); + wfree(bPtr); } diff --git a/WINGs/wfontpanel.c b/WINGs/wfontpanel.c index c57678ab..37b2e912 100644 --- a/WINGs/wfontpanel.c +++ b/WINGs/wfontpanel.c @@ -520,7 +520,7 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel) FcFontSet *fs; FcPattern *pat; WMHashTable *families; - WMHashEnumerator enumer; + WMHashEnumerator *enumer; WMArray *array; int i; @@ -551,7 +551,8 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel) enumer = WMEnumerateHashTable(families); - while ((array = WMNextHashEnumeratorItem(&enumer))) { + while ((array = WMNextHashEnumeratorItem(enumer))) { + printf("listFamilies: got family with %d items\n", WMGetArrayItemCount(array)); WMArrayIterator i; Family *fam; char buffer[256]; diff --git a/wutil-rs/src/hash_table.rs b/wutil-rs/src/hash_table.rs index 77c20823..e9ef66ce 100644 --- a/wutil-rs/src/hash_table.rs +++ b/wutil-rs/src/hash_table.rs @@ -69,7 +69,6 @@ impl HashTable { } } -#[derive(Debug)] #[repr(transparent)] pub struct StringKey(*const u8); @@ -85,6 +84,12 @@ impl PartialEq for StringKey { impl Eq for StringKey {} +impl std::fmt::Debug for StringKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "StringKey({:?})", unsafe { std::ffi::CStr::from_ptr(self.0) }) + } +} + impl Hash for StringKey { fn hash(&self, h: &mut H) { if self.0.is_null() { @@ -291,4 +296,43 @@ pub mod ffi { }, } } + + #[cfg(test)] + mod test { + use super::*; + + use std::{ffi::CString, ptr}; + + #[test] + fn enumerate_nonempty() { + unsafe { + let table = WMCreateStringHashTable(); + let k1 = CString::new("hello").unwrap().into_raw(); + let v1 = CString::new("world").unwrap().into_raw(); + let k2 = CString::new("foo").unwrap().into_raw(); + let v2 = CString::new("bar").unwrap().into_raw(); + WMHashInsert(table, k1.cast(), v1.cast()); + WMHashInsert(table, k2.cast(), v2.cast()); + + let i = WMEnumerateHashTable(table); + + let v = WMNextHashEnumeratorItem(i); + assert_ne!(v, ptr::null_mut()); + assert!(v == v1.cast() || v == v2.cast()); + + let v = WMNextHashEnumeratorItem(i); + assert_ne!(v, ptr::null_mut()); + assert!(v == v1.cast() || v == v2.cast()); + + let v = WMNextHashEnumeratorItem(i); + assert_eq!(v, ptr::null_mut()); + WMFreeHashEnumerator(i); + + let _ = CString::from_raw(k1); + let _ = CString::from_raw(v1); + let _ = CString::from_raw(k2); + let _ = CString::from_raw(v2); + } + } + } } -- 2.39.5