56 Commits

Author SHA1 Message Date
cd711ba52b Restore function prototype for W_ClearNotificationCenter.
This was mistakenly removed when writing wutil-rs/src/notification.rs.
2025-12-20 12:38:29 -05:00
d46810291b Remove unused import. 2025-12-20 12:38:29 -05:00
7c875284dc Add build-generated files to .gitignore. 2025-12-20 12:35:10 -05:00
52db12fbf2 Merge pull request 'Merge refactor/wings-rs into refactor/riir' (#13) from refactor/wings-rs into refactor/riir
Reviewed-on: #13
2025-12-15 13:03:10 -05:00
b2481cf657 Name arguments directly instead of aping the Google C++ style guide. 2025-12-15 13:00:00 -05:00
c371e26d05 Add new Rust source files to Makefile.am.
Forgot to do this earlier.
2025-12-15 13:00:00 -05:00
98421afc38 Rewrite all functions from WINGs wfont.c in Rust.
This starts to establish the structure for an in-place rewrite of WINGs. An
actual redesign of WINGs may follow, but for now there are no plans to alter the
structure of WINGs substantially.
2025-12-15 13:00:00 -05:00
9e49ed98a2 Make WINGs configuration visible from Rust. 2025-12-15 13:00:00 -05:00
a466f17c35 Fix build order issues by moving wings-rs/ into WINGs/.
Having wings-rs depend on WINGs/WINGs/WINGsP.h was not working very well if all
of WINGs/ had to be built before wings-rs/. Moving wings-rs/ into WINGs/
addresses this.
2025-12-15 13:00:00 -05:00
d33c05ef08 Ignore target dir for nested Cargo subdirs, too. 2025-12-15 13:00:00 -05:00
79873413db Tweak build so that WINGsP.rs gets generated at the right time. 2025-12-15 13:00:00 -05:00
57ac6b8178 Put the font and y (ascent) fields of W_Font behind accessors. 2025-12-15 13:00:00 -05:00
d30fe6182d Mark wings-rs as a staticlib. 2025-12-15 13:00:00 -05:00
e20009a880 Use bindgen to generate Rust bindings for WINGs types.
We will use these to port W_Font, W_Screen, etc., to Rust.

Bindings are regenerated whenever the relevant C source files change and then
patched to make sure they will build. This is a bit hacky but will serve our
purposes: the struct definitions should be migrated to Rust sooner rather than
later, so the hackishness should be transient.
2025-12-15 13:00:00 -05:00
ee71db6693 Remove unused functions WMSetWidgetDefaultFont, WMSetWidgetDefaultBoldFont. 2025-12-15 13:00:00 -05:00
01c7eb7275 Remove unused function WMCopyFontWithStyle. 2025-12-15 13:00:00 -05:00
fa99c12fd7 Remove unused function WMIsAntialiasingEnabled. 2025-12-15 13:00:00 -05:00
d7e815010b Merge pull request 'Merge refactor/wutil-rs into refactor/riir' (#12) from trurl/wmaker:refactor/wutil-rs into refactor/riir
Reviewed-on: #12
2025-12-15 12:50:52 -05:00
a31fa582bd Merge branch 'refactor/riir' into refactor/wutil-rs 2025-12-15 12:49:49 -05:00
d3dac752cc Indent with tabs, as is standard for this file. 2025-12-08 13:31:05 -05:00
0893be1cea Use BTreeMap instead of HashMap for notification subscriptions.
This allows us to initialize the global NotificationCenter singleton in a const
context, which eliminates the need for OnceLock.
2025-12-08 13:24:44 -05:00
d8057575ce Use a helper method for adding notification listeners. 2025-12-08 13:14:12 -05:00
d8912c58e6 Fix typo in comment. 2025-12-08 13:06:58 -05:00
b7f765e3f6 Rewrite WINGs/notification.c in Rust. 2025-12-08 13:06:04 -05:00
d88d626fbe Remove direct creation and posting of notifications from WINGs.
We can reduce the WMNotification API surface area further by getting rid of
WMCreateNotification and WMPostNotification. WMNotification remains a
first-class object, but it is no longer possible for client code to create a
notification object directly. Notifications must now be posted through
WMPostNotificationName, which handles notification creation and destruction on
its own.

This will simplify the notification lifecycle and make the Rust rewrite
simpler. (Notifications no longer need to be reference-counted, heap-allocated
objects that might be saved somewhere after they are dispatched.)

WTextField code which reused the WMNotification struct has been modified to take
parameters of the correct type directly, instead of through a WMNotification's
void* data field.
2025-12-08 13:05:20 -05:00
5847e9d68f Ditch the notification function WMRemoveNotificationObserverWithName.
This reduces the notification API in a way that is helpful for rewriting in
Rust. This function is only used in one place, and the object that is being
deregistered is free'd immediately after WMRemoveNotificationObserverWithName is
called, so it should be safe just to use WMRemoveNotificationObserver (since I
hope it's an error to keep a notification to a free'd object registered).
2025-12-08 13:05:20 -05:00
adb967ab15 Drop dead notification queue code.
This appears to have been used by now-defunct support for network
connections (WMConnection). No live code instantiates a notification queue or
pushes/dequeues notifications from a notification queue. (The global
NotificationCenter in WINGs/notification.c is still in use, so it is not going
anywhere in this commit.)
2025-12-08 13:05:20 -05:00
0c4d78a53d Drop unused macro (dead code) that invokes WMCreateNotification. 2025-12-08 13:05:20 -05:00
dcd45f0677 Make some unused public WMNotification APIs private.
`WMRetainNotification`, `WMGetDefaultNotificationQueue`,
`WMDequeueNotificationMatching`, `WMEnqueueNotification`, and
`WMEnqueueCoalesceNotification`, are only used in WINGs/notification.c. To
reduce the API surface area that needs to be migrated to Rust, they are being
made private.
2025-12-08 13:05:20 -05:00
0097d1819e Move functions on Bag which were only called by FFI code inline. 2025-12-08 13:00:14 -05:00
8270124869 Nix dead code that was left commented out. 2025-12-08 13:00:14 -05:00
89183f3bcb Rewrite WMBag in Rust.
We should eventually get rid of this entirely, in favor of something along the
lines of a sorted Vec that is fully typed (so the payload isn't just a void*).
2025-12-08 13:00:14 -05:00
46e540b1b1 Prune the WMBag API in preparation for rewriting it in Rust. 2025-12-08 13:00:14 -05:00
9802b684ae Rewrite WINGs/tree.c in Rust.
This is a bit of a red herring, since WMTree is only used in wmmenugen, which I
don't think I've ever directly used. See notes at the top of tree.rs for more
musings on whether this should be in wutil-rs at all.
2025-12-08 12:48:37 -05:00
0a04a4c12e Remove depthFirst parameter from WMTreeWalk.
WMTreeWalk is only called once, with depthFirst set to true, so we might as well
just hard-code that behavior.

Also, this parameter appears to have been misnamed (since the search is always DFS)
and should properly be named to indicate that it controls if this is a pre- or
post-order traversal.
2025-12-08 12:47:39 -05:00
e1a263cc5b Prune unused WMTree API functions.
This is a first step towards migrating WINGs/tree.c to Rust.

Happy finding: no parent pointer are actually needed, so we don't have to use a
shared structure for node pointers!
2025-12-08 12:47:39 -05:00
c298b5f96f Avoid an unnecessary allocation in wtrimspace. 2025-12-08 12:40:33 -05:00
dfd77b11a9 Don't scan ahead unnecessarily in wstrndup. 2025-12-08 12:40:33 -05:00
927cc93e0a Add string.rs to Makefile.am for wutil-rs.
Without this, `make` won't automatically rebuild wutil-rs when string.rs
changes.
2025-12-08 12:40:33 -05:00
d2046de7ff Unit tests for wtokenjoin. 2025-12-08 12:40:33 -05:00
a7a44397a4 Rewrite all functions from WUtils string.c in Rust.
These functions should be gotten rid of as we transition to Rust, but replacing
them with minimally tested Rust code should suffice as a first step.
2025-12-08 12:40:33 -05:00
72a1f8cb9e Throw some comments around the FST table for tokenizing command line.
This is slated for replacement, but it will help to be better documented first.
2025-12-08 12:40:33 -05:00
60902b9222 Use system strlcpy/strlcat instead of packaging our own.
These functions were added to glibc 2.38, so we don't even need to check for
libbsd. There isn't a strong need to worry about supporting older systems
because use of these functions should go away as we rewrite string.c in
Rust. And, apparently, they are not held in high regard historically. That's at
least 2 reasons to be rid of them.
2025-12-08 12:40:33 -05:00
e0fc92bf51 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.
2025-11-29 12:51:51 -05:00
f8df6447ea 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).
2025-11-29 12:51:51 -05:00
bd18e0c600 add some hacky lines to start-captive-wmaker.sh to ease debugging of X11 integration from Emacs 2025-11-21 16:01:41 -05:00
50caed30c0 Add a wings-rs crate for porting WINGs proper to Rust. 2025-11-14 01:07:40 -05:00
46af2c27ee Tweak start-captive-wmaker.sh to behave a little more nicely.
* Select $DISPLAY dynamically because X11 likes :0 and Wayland likes :1 and who
knows what else might like some other value.
* Kill Xephyr after wmaker exits.
* 640x480 should be big enough for anyone. (And the window shouldn't get in the
way so much.)
2025-10-28 21:30:34 -04:00
026426e6c3 Remove VoidPointer impls that were commented out in previous commit. 2025-10-25 12:41:52 -04:00
564501953f Use wfree instead of XFree in a few more places.
This fixes some crashes found in cursory smoke tests. You can now open and close
some windows without crashing immediately.
2025-10-25 01:53:23 -04:00
8c68f95291 Update calls to malloc/free, etc., to use wmalloc/wfree/...
This fixes a lot of memory bugs which arose as a result of doing something
different from the system malloc/free when allocators were rewritten in Rust.

These changes originate from a different approach to writing the allocator in
Rust:
https://git.sdf.org/vitrine/wmaker/pulls/1/files#diff-04a0fd2319b9969373b75377716e45c836d22869

There are other function calls (to XFree) that need to be fixed, but that can be
done in another commit. This one is already getting large.
2025-10-24 15:35:07 -04:00
d66eb34f16 Satisfy the dangerous_implicit_autorefs lint. 2025-10-23 16:26:47 -04:00
65726a1e6a WMHashTable ownership: it doesn't own keys or values.
The WMHashTable rewrite was prematurely merged into refactor/wutil-rs, so we're
picking up the pieces now.
2025-10-23 16:25:37 -04:00
fbd6400186 Remove stale reference to libwings_rs (which should have been deleted in a rebase). 2025-10-23 16:25:01 -04:00
4f4dcf551b Restore proplist.c, which was clobbered by mistake during a rebase.
Lessons learned: don't rebase so freely, review commits properly.
2025-10-23 15:40:50 -04:00
Dan Cross
d91275d959 memory: replace most of memory.c with Rust mem
Replace `wmalloc` et al with wrappers around the Rust allocation
library.
2025-10-14 02:55:03 +00:00
98 changed files with 2829 additions and 3056 deletions

15
.gitignore vendored
View File

@@ -83,13 +83,20 @@ WINGs/Tests/wtest
WPrefs.app/WPrefs
# These files are generated from make rules
wmlib/wmlib.pc
wrlib/wrlib.pc
WINGs/WINGs.pc
WINGs/WUtil.pc
doc/wmaker.1
doc/wmsetbg.1
wrlib/libwraster.map
WindowMaker/appearance.menu
WindowMaker/appearance.menu.fy
WindowMaker/appearance.menu.nl
WindowMaker/appearance.menu.sr
WindowMaker/menu
WindowMaker/menu.bg
WindowMaker/menu.fi
@@ -99,6 +106,7 @@ WindowMaker/menu.ko
WindowMaker/menu.nl
WindowMaker/menu.ro
WindowMaker/menu.sk
WindowMaker/menu.sr
WindowMaker/menu.zh_TW
WindowMaker/plmenu
WindowMaker/plmenu.bg
@@ -111,6 +119,7 @@ WindowMaker/plmenu.nl
WindowMaker/plmenu.pl
WindowMaker/plmenu.ro
WindowMaker/plmenu.sk
WindowMaker/plmenu.sr
WindowMaker/plmenu.zh_CN
WindowMaker/plmenu.zh_TW
WindowMaker/wmmacros
@@ -142,4 +151,8 @@ WPrefs.app/WPrefs.desktop
.pc
# Rust stuff.
/*/target/**
/**/target/**
WINGs/wings-rs/src/WINGsP.rs
WINGs/wings-rs/Cargo.lock
wmaker-rs/Cargo.lock
wutil-rs/Cargo.lock

View File

@@ -2,7 +2,7 @@
AUTOMAKE_OPTIONS =
SUBDIRS = WINGs . po Documentation Resources
SUBDIRS = WINGs wings-rs . po Documentation Resources
DIST_SUBDIRS = $(SUBDIRS) Tests Examples Extras
libWINGs_la_LDFLAGS = -version-info @WINGS_VERSION@
@@ -15,7 +15,7 @@ wraster = $(top_builddir)/wrlib/libwraster.la
LDADD= libWUtil.la libWINGs.la $(wraster) $(wutilrs) @INTLIBS@
libWINGs_la_LIBADD = libWUtil.la $(wraster) $(wutilrs) @XLIBS@ @XFT_LIBS@ @FCLIBS@ @LIBM@ @PANGO_LIBS@
libWUtil_la_LIBADD = @LIBBSD@ $(wutilrs)
libWUtil_la_LIBADD = $(wutilrs)
EXTRA_DIST = BUGS make-rgb Examples Extras Tests
@@ -41,7 +41,6 @@ libWINGs_la_SOURCES = \
wevent.c \
wfilepanel.c \
wframe.c \
wfont.c \
wfontpanel.c \
widgets.c \
winputmethod.c \
@@ -65,7 +64,6 @@ libWINGs_la_SOURCES = \
wwindow.c
libWUtil_la_SOURCES = \
bagtree.c \
error.c \
error.h \
findfile.c \
@@ -74,10 +72,7 @@ libWUtil_la_SOURCES = \
menuparser.h \
menuparser_macros.c \
misc.c \
notification.c \
proplist.c \
string.c \
tree.c \
userdefaults.c \
userdefaults.h \
usleep.c \

View File

@@ -233,7 +233,7 @@ typedef enum {
/* text movement types */
enum {
typedef enum {
WMIllegalTextMovement,
WMReturnTextMovement,
WMEscapeTextMovement,
@@ -243,13 +243,13 @@ enum {
WMRightTextMovement,
WMUpTextMovement,
WMDownTextMovement
};
} WMTextMovementType;
/* text field special events */
enum {
typedef enum {
WMInsertTextEvent,
WMDeleteTextEvent
};
} WMTextFieldSpecialEventType;
enum {
@@ -533,14 +533,11 @@ typedef struct WMBrowserDelegate {
typedef struct WMTextFieldDelegate {
void *data;
void (*didBeginEditing)(struct WMTextFieldDelegate *self,
WMNotification *notif);
void (*didBeginEditing)(struct WMTextFieldDelegate *self, WMTextMovementType reason);
void (*didChange)(struct WMTextFieldDelegate *self,
WMNotification *notif);
void (*didChange)(struct WMTextFieldDelegate *self, WMTextFieldSpecialEventType reason);
void (*didEndEditing)(struct WMTextFieldDelegate *self,
WMNotification *notif);
void (*didEndEditing)(struct WMTextFieldDelegate *self, WMTextMovementType reason);
Bool (*shouldBeginEditing)(struct WMTextFieldDelegate *self,
WMTextField *tPtr);
@@ -781,12 +778,8 @@ void WMSetViewDragDestinationProcs(WMView *view, WMDragDestinationProcs *procs);
/* ---[ WINGs/wfont.c ]--------------------------------------------------- */
Bool WMIsAntialiasingEnabled(WMScreen *scrPtr);
WMFont* WMCreateFont(WMScreen *scrPtr, const char *fontName);
WMFont* WMCopyFontWithStyle(WMScreen *scrPtr, WMFont *font, WMFontStyle style);
WMFont* WMRetainFont(WMFont *font);
void WMReleaseFont(WMFont *font);
@@ -795,12 +788,10 @@ char* WMGetFontName(WMFont *font);
unsigned int WMFontHeight(WMFont *font);
unsigned int WMFontAscent(WMFont *font);
void WMGetScaleBaseFromSystemFont(WMScreen *scrPtr, int *alphabetWidth, int *fontHeight);
void WMSetWidgetDefaultFont(WMScreen *scr, WMFont *font);
void WMSetWidgetDefaultBoldFont(WMScreen *scr, WMFont *font);
WMFont* WMDefaultSystemFont(WMScreen *scrPtr);
WMFont* WMDefaultBoldSystemFont(WMScreen *scrPtr);
@@ -818,6 +809,8 @@ void WMDrawImageString(WMScreen *scr, Drawable d, WMColor *color,
int WMWidthOfString(WMFont *font, const char *text, int length);
struct _XftFont *WMFontXftFont(WMFont *font);
/* ---[ WINGs/wpixmap.c ]------------------------------------------------- */
WMPixmap* WMRetainPixmap(WMPixmap *pixmap);

View File

@@ -152,11 +152,11 @@ typedef struct W_Screen {
GC drawImStringGC; /* for WMDrawImageString() */
struct W_Font *normalFont;
WMFont *normalFont;
struct W_Font *boldFont;
WMFont *boldFont;
WMHashTable *fontCache;
void *fontCache; /* owned/maintainted by wings-rs/src/screen.rs */
Bool antialiasedText;
@@ -378,9 +378,7 @@ void W_InitNotificationCenter(void);
void W_ReleaseNotificationCenter(void);
void W_FlushASAPNotificationQueue(void);
void W_FlushIdleNotificationQueue(void);
void W_ClearNotificationCenter(void);
/* ---[ selection.c ]----------------------------------------------------- */
@@ -445,24 +443,6 @@ typedef struct W_EventHandler {
void W_CallDestroyHandlers(W_View *view);
/* ---[ wfont.c ]--------------------------------------------------------- */
typedef struct W_Font {
struct W_Screen *screen;
struct _XftFont *font;
short height;
short y;
short refCount;
char *name;
@USE_PANGO@ PangoLayout *layout;
} W_Font;
#define W_FONTID(f) (f)->font->fid
/* ---[ widgets.c ]------------------------------------------------------- */
#define WC_UserWidget 128
@@ -508,7 +488,7 @@ void W_DrawReliefWithGC(W_Screen *scr, Drawable d, int x, int y,
GC black, GC dark, GC light, GC white);
void W_PaintTextAndImage(W_View *view, int wrap, WMColor *textColor,
W_Font *font, WMReliefType relief, const char *text,
WMFont *font, WMReliefType relief, const char *text,
WMAlignment alignment, W_Pixmap *image,
WMImagePosition position, WMColor *backColor, int ofs);

View File

@@ -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 {
@@ -173,7 +168,7 @@ typedef struct {
typedef int WMArrayIterator;
typedef void *WMBagIterator;
typedef int WMBagIterator;
typedef void WMNotificationObserverAction(void *observerData,
@@ -271,14 +266,9 @@ char* wstrconcat(const char *str1, const char *str2);
* so always assign the returned address to avoid dangling pointers. */
char* wstrappend(char *dst, const char *src);
size_t wstrlcpy(char *, const char *, size_t);
size_t wstrlcat(char *, const char *, size_t);
void wtokensplit(char *command, char ***argv, int *argc);
char* wtokennext(char *word, char **next);
char* wtokenjoin(char **list, int count);
void wtokenfree(char **tokens, int count);
@@ -334,7 +324,7 @@ void WMDeleteInputHandler(WMHandlerID handlerID);
void WHandleEvents(void);
/* ---[ WINGs/hashtable.c ]----------------------------------------------- */
/* ---[ wutil-rs/src/hash_table.rs ]----------------------------------------------- */
WMHashTable* WMCreateIdentityHashTable();
@@ -366,7 +356,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 +371,7 @@ Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator *enumerator,
void **item, void **key);
void WMFreeHashEnumerator(WMHashEnumerator *enumerator);
/* some predefined callback sets */
@@ -484,7 +474,7 @@ void* WMArrayPrevious(WMArray *array, WMArrayIterator *iter);
for (var = WMArrayLast(array, &(i)); (i) != WANotFound; \
var = WMArrayPrevious(array, &(i)))
/* ---[ WINGs/bagtree.c ]------------------------------------------------- */
/* ---[ wutil-rs/src/bag.rs ]------------------------------------------------- */
/*
* Tree bags use a red-black tree for storage.
@@ -499,58 +489,16 @@ void* WMArrayPrevious(WMArray *array, WMArrayIterator *iter);
* Slow for storing small numbers of elements
*/
#define WMCreateBag(size) WMCreateTreeBag()
#define WMCreateBagWithDestructor(size, d) WMCreateTreeBagWithDestructor(d)
WMBag* WMCreateTreeBag(void);
WMBag* WMCreateTreeBagWithDestructor(WMFreeDataProc *destructor);
int WMGetBagItemCount(WMBag *bag);
void WMAppendBag(WMBag *bag, WMBag *other);
void WMPutInBag(WMBag *bag, void *item);
/* insert will increment the index of elements after it by 1 */
void WMInsertInBag(WMBag *bag, int index, void *item);
/* erase will remove the element from the bag,
* but will keep the index of the other elements unchanged */
int WMEraseFromBag(WMBag *bag, int index);
/* delete and remove will remove the elements and cause the elements
* after them to decrement their indexes by 1 */
int WMDeleteFromBag(WMBag *bag, int index);
int WMRemoveFromBag(WMBag *bag, void *item);
void* WMGetFromBag(WMBag *bag, int index);
void* WMReplaceInBag(WMBag *bag, int index, void *item);
#define WMSetInBag(bag, index, item) WMReplaceInBag(bag, index, item)
/* comparer must return:
* < 0 if a < b
* > 0 if a > b
* = 0 if a = b
*/
void WMSortBag(WMBag *bag, WMCompareDataProc *comparer);
void WMSetInBag(WMBag *bag, int index, void *item);
void WMEmptyBag(WMBag *bag);
void WMFreeBag(WMBag *bag);
void WMMapBag(WMBag *bag, void (*function)(void*, void*), void *data);
int WMGetFirstInBag(WMBag *bag, void *item);
int WMCountInBag(WMBag *bag, void *item);
int WMFindInBag(WMBag *bag, WMMatchDataProc *match, void *cdata);
void* WMBagFirst(WMBag *bag, WMBagIterator *ptr);
void* WMBagLast(WMBag *bag, WMBagIterator *ptr);
@@ -562,16 +510,11 @@ void* WMBagPrevious(WMBag *bag, WMBagIterator *ptr);
void* WMBagIteratorAtIndex(WMBag *bag, int index, WMBagIterator *ptr);
int WMBagIndexForIterator(WMBag *bag, WMBagIterator ptr);
/* The following 2 macros assume that the bag doesn't change in the for loop */
#define WM_ITERATE_BAG(bag, var, i) \
for (var = WMBagFirst(bag, &(i)); (i) != NULL; \
var = WMBagNext(bag, &(i)))
/* The following macro assumes that the bag doesn't change in the for loop */
#define WM_ETARETI_BAG(bag, var, i) \
for (var = WMBagLast(bag, &(i)); (i) != NULL; \
for (var = WMBagLast(bag, &(i)); (i) >= 0; \
var = WMBagPrevious(bag, &(i)))
@@ -620,14 +563,12 @@ void WMSetDataFormat(WMData *aData, unsigned format);
unsigned WMGetDataFormat(WMData *aData);
/* Storing data */
/* ---[ WINGs/tree.c ]---------------------------------------------------- */
/* ---[ wutil-rs/src/tree.rs ]---------------------------------------------------- */
/* Generic Tree and TreeNode */
WMTreeNode* WMCreateTreeNode(void *data);
WMTreeNode* WMCreateTreeNodeWithDestructor(void *data, WMFreeDataProc *destructor);
WMTreeNode* WMInsertItemInTree(WMTreeNode *parent, int index, void *item);
#define WMAddItemToTree(parent, item) WMInsertItemInTree(parent, -1, item)
@@ -636,48 +577,23 @@ WMTreeNode* WMInsertNodeInTree(WMTreeNode *parent, int index, WMTreeNode *aNode)
#define WMAddNodeToTree(parent, aNode) WMInsertNodeInTree(parent, -1, aNode)
void WMDestroyTreeNode(WMTreeNode *aNode);
void WMDeleteLeafForTreeNode(WMTreeNode *aNode, int index);
void WMRemoveLeafForTreeNode(WMTreeNode *aNode, void *leaf);
void* WMReplaceDataForTreeNode(WMTreeNode *aNode, void *newData);
void* WMGetDataForTreeNode(WMTreeNode *aNode);
int WMGetTreeNodeDepth(WMTreeNode *aNode);
WMTreeNode* WMGetParentForTreeNode(WMTreeNode *aNode);
/* Sort only the leaves of the passed node */
void WMSortLeavesForTreeNode(WMTreeNode *aNode, WMCompareDataProc *comparer);
/* Sort all tree recursively starting from the passed node */
void WMSortTree(WMTreeNode *aNode, WMCompareDataProc *comparer);
/* Returns the first node which matches node's data with cdata by 'match' */
WMTreeNode* WMFindInTree(WMTreeNode *aTree, WMMatchDataProc *match, void *cdata);
void WMSortTree(WMTreeNode *aNode, int (*comparer)(const WMTreeNode *a, const WMTreeNode *b));
/* Returns the first node where node's data matches cdata by 'match' and node is
* at most `limit' depths down from `aTree'. */
WMTreeNode *WMFindInTreeWithDepthLimit(WMTreeNode * aTree, WMMatchDataProc * match, void *cdata, int limit);
/* Returns first tree node that has data == cdata */
#define WMGetFirstInTree(aTree, cdata) WMFindInTree(aTree, NULL, cdata)
WMTreeNode *WMFindInTreeWithDepthLimit(WMTreeNode * aTree, int (*match)(const WMTreeNode *item, const void *cdata), void *cdata, int limit);
/* Walk every node of aNode with `walk' */
void WMTreeWalk(WMTreeNode *aNode, WMTreeWalkProc * walk, void *data, Bool DepthFirst);
void WMTreeWalk(WMTreeNode *aNode, WMTreeWalkProc * walk, void *data);
/* ---[ WINGs/data.c ]---------------------------------------------------- */
/* ---[ WINGs/notification.c ]---------------------------------------------------- */
WMNotification* WMCreateNotification(const char *name, void *object, void *clientData);
void WMReleaseNotification(WMNotification *notification);
WMNotification* WMRetainNotification(WMNotification *notification);
void* WMGetNotificationClientData(WMNotification *notification);
void* WMGetNotificationObject(WMNotification *notification);
@@ -688,33 +604,10 @@ const char* WMGetNotificationName(WMNotification *notification);
void WMAddNotificationObserver(WMNotificationObserverAction *observerAction,
void *observer, const char *name, void *object);
void WMPostNotification(WMNotification *notification);
void WMRemoveNotificationObserver(void *observer);
void WMRemoveNotificationObserverWithName(void *observer, const char *name,
void *object);
void WMPostNotificationName(const char *name, void *object, void *clientData);
WMNotificationQueue* WMGetDefaultNotificationQueue(void);
WMNotificationQueue* WMCreateNotificationQueue(void);
void WMDequeueNotificationMatching(WMNotificationQueue *queue,
WMNotification *notification,
unsigned mask);
void WMEnqueueNotification(WMNotificationQueue *queue,
WMNotification *notification,
WMPostingStyle postingStyle);
void WMEnqueueCoalesceNotification(WMNotificationQueue *queue,
WMNotification *notification,
WMPostingStyle postingStyle,
unsigned coalesceMask);
/* Property Lists handling */
/* ---[ WINGs/proplist.c ]------------------------------------------------ */

View File

@@ -1,745 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "WUtil.h"
typedef struct W_Node {
struct W_Node *parent;
struct W_Node *left;
struct W_Node *right;
int color;
void *data;
int index;
} W_Node;
typedef struct W_Bag {
W_Node *root;
W_Node *nil; /* sentinel */
int count;
void (*destructor) (void *item);
} W_Bag;
#define IS_LEFT(node) (node == node->parent->left)
#define IS_RIGHT(node) (node == node->parent->right)
static void leftRotate(W_Bag * tree, W_Node * node)
{
W_Node *node2;
node2 = node->right;
node->right = node2->left;
node2->left->parent = node;
node2->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = node2;
} else {
if (IS_LEFT(node)) {
node->parent->left = node2;
} else {
node->parent->right = node2;
}
}
node2->left = node;
node->parent = node2;
}
static void rightRotate(W_Bag * tree, W_Node * node)
{
W_Node *node2;
node2 = node->left;
node->left = node2->right;
node2->right->parent = node;
node2->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = node2;
} else {
if (IS_LEFT(node)) {
node->parent->left = node2;
} else {
node->parent->right = node2;
}
}
node2->right = node;
node->parent = node2;
}
static void treeInsert(W_Bag * tree, W_Node * node)
{
W_Node *y = tree->nil;
W_Node *x = tree->root;
while (x != tree->nil) {
y = x;
if (node->index <= x->index)
x = x->left;
else
x = x->right;
}
node->parent = y;
if (y == tree->nil)
tree->root = node;
else if (node->index <= y->index)
y->left = node;
else
y->right = node;
}
static void rbTreeInsert(W_Bag * tree, W_Node * node)
{
W_Node *y;
treeInsert(tree, node);
node->color = 'R';
while (node != tree->root && node->parent->color == 'R') {
if (IS_LEFT(node->parent)) {
y = node->parent->parent->right;
if (y->color == 'R') {
node->parent->color = 'B';
y->color = 'B';
node->parent->parent->color = 'R';
node = node->parent->parent;
} else {
if (IS_RIGHT(node)) {
node = node->parent;
leftRotate(tree, node);
}
node->parent->color = 'B';
node->parent->parent->color = 'R';
rightRotate(tree, node->parent->parent);
}
} else {
y = node->parent->parent->left;
if (y->color == 'R') {
node->parent->color = 'B';
y->color = 'B';
node->parent->parent->color = 'R';
node = node->parent->parent;
} else {
if (IS_LEFT(node)) {
node = node->parent;
rightRotate(tree, node);
}
node->parent->color = 'B';
node->parent->parent->color = 'R';
leftRotate(tree, node->parent->parent);
}
}
}
tree->root->color = 'B';
}
static void rbDeleteFixup(W_Bag * tree, W_Node * node)
{
W_Node *w;
while (node != tree->root && node->color == 'B') {
if (IS_LEFT(node)) {
w = node->parent->right;
if (w->color == 'R') {
w->color = 'B';
node->parent->color = 'R';
leftRotate(tree, node->parent);
w = node->parent->right;
}
if (w->left->color == 'B' && w->right->color == 'B') {
w->color = 'R';
node = node->parent;
} else {
if (w->right->color == 'B') {
w->left->color = 'B';
w->color = 'R';
rightRotate(tree, w);
w = node->parent->right;
}
w->color = node->parent->color;
node->parent->color = 'B';
w->right->color = 'B';
leftRotate(tree, node->parent);
node = tree->root;
}
} else {
w = node->parent->left;
if (w->color == 'R') {
w->color = 'B';
node->parent->color = 'R';
rightRotate(tree, node->parent);
w = node->parent->left;
}
if (w->left->color == 'B' && w->right->color == 'B') {
w->color = 'R';
node = node->parent;
} else {
if (w->left->color == 'B') {
w->right->color = 'B';
w->color = 'R';
leftRotate(tree, w);
w = node->parent->left;
}
w->color = node->parent->color;
node->parent->color = 'B';
w->left->color = 'B';
rightRotate(tree, node->parent);
node = tree->root;
}
}
}
node->color = 'B';
}
static W_Node *treeMinimum(W_Node * node, W_Node * nil)
{
while (node->left != nil)
node = node->left;
return node;
}
static W_Node *treeMaximum(W_Node * node, W_Node * nil)
{
while (node->right != nil)
node = node->right;
return node;
}
static W_Node *treeSuccessor(W_Node * node, W_Node * nil)
{
W_Node *y;
if (node->right != nil) {
return treeMinimum(node->right, nil);
}
y = node->parent;
while (y != nil && node == y->right) {
node = y;
y = y->parent;
}
return y;
}
static W_Node *treePredecessor(W_Node * node, W_Node * nil)
{
W_Node *y;
if (node->left != nil) {
return treeMaximum(node->left, nil);
}
y = node->parent;
while (y != nil && node == y->left) {
node = y;
y = y->parent;
}
return y;
}
static W_Node *rbTreeDelete(W_Bag * tree, W_Node * node)
{
W_Node *nil = tree->nil;
W_Node *x, *y;
if (node->left == nil || node->right == nil) {
y = node;
} else {
y = treeSuccessor(node, nil);
}
if (y->left != nil) {
x = y->left;
} else {
x = y->right;
}
x->parent = y->parent;
if (y->parent == nil) {
tree->root = x;
} else {
if (IS_LEFT(y)) {
y->parent->left = x;
} else {
y->parent->right = x;
}
}
if (y != node) {
node->index = y->index;
node->data = y->data;
}
if (y->color == 'B') {
rbDeleteFixup(tree, x);
}
return y;
}
static W_Node *treeSearch(W_Node * root, W_Node * nil, int index)
{
if (root == nil || root->index == index) {
return root;
}
if (index < root->index) {
return treeSearch(root->left, nil, index);
} else {
return treeSearch(root->right, nil, index);
}
}
static W_Node *treeFind(W_Node * root, W_Node * nil, void *data)
{
W_Node *tmp;
if (root == nil || root->data == data)
return root;
tmp = treeFind(root->left, nil, data);
if (tmp != nil)
return tmp;
tmp = treeFind(root->right, nil, data);
return tmp;
}
#if 0
static char buf[512];
static void printNodes(W_Node * node, W_Node * nil, int depth)
{
if (node == nil) {
return;
}
printNodes(node->left, nil, depth + 1);
memset(buf, ' ', depth * 2);
buf[depth * 2] = 0;
if (IS_LEFT(node))
printf("%s/(%2i\n", buf, node->index);
else
printf("%s\\(%2i\n", buf, node->index);
printNodes(node->right, nil, depth + 1);
}
void PrintTree(WMBag * bag)
{
W_TreeBag *tree = (W_TreeBag *) bag->data;
printNodes(tree->root, tree->nil, 0);
}
#endif
WMBag *WMCreateTreeBag(void)
{
return WMCreateTreeBagWithDestructor(NULL);
}
WMBag *WMCreateTreeBagWithDestructor(WMFreeDataProc * destructor)
{
WMBag *bag;
bag = wmalloc(sizeof(WMBag));
bag->nil = wmalloc(sizeof(W_Node));
bag->nil->left = bag->nil->right = bag->nil->parent = bag->nil;
bag->nil->index = WBNotFound;
bag->root = bag->nil;
bag->destructor = destructor;
return bag;
}
int WMGetBagItemCount(WMBag * self)
{
return self->count;
}
void WMAppendBag(WMBag * self, WMBag * bag)
{
WMBagIterator ptr;
void *data;
for (data = WMBagFirst(bag, &ptr); data != NULL; data = WMBagNext(bag, &ptr)) {
WMPutInBag(self, data);
}
}
void WMPutInBag(WMBag * self, void *item)
{
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = self->count;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
self->count++;
}
void WMInsertInBag(WMBag * self, int index, void *item)
{
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = index;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
while ((ptr = treeSuccessor(ptr, self->nil)) != self->nil) {
ptr->index++;
}
self->count++;
}
static int treeDeleteNode(WMBag * self, W_Node *ptr)
{
if (ptr != self->nil) {
W_Node *tmp;
self->count--;
tmp = treeSuccessor(ptr, self->nil);
while (tmp != self->nil) {
tmp->index--;
tmp = treeSuccessor(tmp, self->nil);
}
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
return 1;
}
return 0;
}
int WMRemoveFromBag(WMBag * self, void *item)
{
W_Node *ptr = treeFind(self->root, self->nil, item);
return treeDeleteNode(self, ptr);
}
int WMEraseFromBag(WMBag * self, int index)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
if (ptr != self->nil) {
self->count--;
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
wassertrv(self->count == 0 || self->root->index >= 0, 1);
return 1;
} else {
return 0;
}
}
int WMDeleteFromBag(WMBag * self, int index)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
return treeDeleteNode(self, ptr);
}
void *WMGetFromBag(WMBag * self, int index)
{
W_Node *node;
node = treeSearch(self->root, self->nil, index);
if (node != self->nil)
return node->data;
else
return NULL;
}
int WMGetFirstInBag(WMBag * self, void *item)
{
W_Node *node;
node = treeFind(self->root, self->nil, item);
if (node != self->nil)
return node->index;
else
return WBNotFound;
}
static int treeCount(W_Node * root, W_Node * nil, void *item)
{
int count = 0;
if (root == nil)
return 0;
if (root->data == item)
count++;
if (root->left != nil)
count += treeCount(root->left, nil, item);
if (root->right != nil)
count += treeCount(root->right, nil, item);
return count;
}
int WMCountInBag(WMBag * self, void *item)
{
return treeCount(self->root, self->nil, item);
}
void *WMReplaceInBag(WMBag * self, int index, void *item)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
void *old = NULL;
if (item == NULL) {
self->count--;
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
} else if (ptr != self->nil) {
old = ptr->data;
ptr->data = item;
} else {
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = index;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
self->count++;
}
return old;
}
void WMSortBag(WMBag * self, WMCompareDataProc * comparer)
{
void **items;
W_Node *tmp;
int i;
if (self->count == 0)
return;
items = wmalloc(sizeof(void *) * self->count);
i = 0;
tmp = treeMinimum(self->root, self->nil);
while (tmp != self->nil) {
items[i++] = tmp->data;
tmp = treeSuccessor(tmp, self->nil);
}
qsort(&items[0], self->count, sizeof(void *), comparer);
i = 0;
tmp = treeMinimum(self->root, self->nil);
while (tmp != self->nil) {
tmp->index = i;
tmp->data = items[i++];
tmp = treeSuccessor(tmp, self->nil);
}
wfree(items);
}
static void deleteTree(WMBag * self, W_Node * node)
{
if (node == self->nil)
return;
deleteTree(self, node->left);
if (self->destructor)
self->destructor(node->data);
deleteTree(self, node->right);
wfree(node);
}
void WMEmptyBag(WMBag * self)
{
deleteTree(self, self->root);
self->root = self->nil;
self->count = 0;
}
void WMFreeBag(WMBag * self)
{
WMEmptyBag(self);
wfree(self->nil);
wfree(self);
}
static void mapTree(W_Bag * tree, W_Node * node, void (*function) (void *, void *), void *data)
{
if (node == tree->nil)
return;
mapTree(tree, node->left, function, data);
(*function) (node->data, data);
mapTree(tree, node->right, function, data);
}
void WMMapBag(WMBag * self, void (*function) (void *, void *), void *data)
{
mapTree(self, self->root, function, data);
}
static int findInTree(W_Bag * tree, W_Node * node, WMMatchDataProc * function, void *cdata)
{
int index;
if (node == tree->nil)
return WBNotFound;
index = findInTree(tree, node->left, function, cdata);
if (index != WBNotFound)
return index;
if ((*function) (node->data, cdata)) {
return node->index;
}
return findInTree(tree, node->right, function, cdata);
}
int WMFindInBag(WMBag * self, WMMatchDataProc * match, void *cdata)
{
return findInTree(self, self->root, match, cdata);
}
void *WMBagFirst(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
node = treeMinimum(self->root, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagLast(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
node = treeMaximum(self->root, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagNext(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
if (*ptr == NULL)
return NULL;
node = treeSuccessor(*ptr, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagPrevious(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
if (*ptr == NULL)
return NULL;
node = treePredecessor(*ptr, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagIteratorAtIndex(WMBag * self, int index, WMBagIterator * ptr)
{
W_Node *node;
node = treeSearch(self->root, self->nil, index);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
int WMBagIndexForIterator(WMBag * bag, WMBagIterator ptr)
{
/* Parameter not used, but tell the compiler that it is ok */
(void) bag;
return ((W_Node *) ptr)->index;
}

View File

@@ -498,7 +498,7 @@ static void registerDescriptionList(WMScreen * scr, WMView * view, WMArray * ope
for (i = 0; i < count; i++) {
text = WMGetDragOperationItemText(WMGetFromArray(operationArray, i));
wstrlcpy(textListItem, text, size);
strlcpy(textListItem, text, size);
/* to next text offset */
textListItem = &(textListItem[strlen(textListItem) + 1]);

View File

@@ -76,8 +76,8 @@ char *wfindfileinarray(WMPropList *array, const char *file)
path = wmalloc(len + flen + 2);
path = memcpy(path, p, len);
path[len] = 0;
if (wstrlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
wstrlcat(path, file, len + flen + 2) >= len + flen + 2) {
if (strlcat(path, "/", len + flen + 2) >= len + flen + 2 ||
strlcat(path, file, len + flen + 2) >= len + flen + 2) {
wfree(path);
return NULL;
}

View File

@@ -126,7 +126,7 @@ WMHandlerID WMAddTimerHandler(int milliseconds, WMCallback * callback, void *cda
{
TimerHandler *handler;
handler = malloc(sizeof(TimerHandler));
handler = wmalloc(sizeof(TimerHandler));
if (!handler)
return NULL;
@@ -214,7 +214,7 @@ WMHandlerID WMAddIdleHandler(WMCallback * callback, void *cdata)
{
IdleHandler *handler;
handler = malloc(sizeof(IdleHandler));
handler = wmalloc(sizeof(IdleHandler));
if (!handler)
return NULL;
@@ -274,7 +274,6 @@ Bool W_CheckIdleHandlers(void)
WMArrayIterator iter;
if (!idleHandler || WMGetArrayItemCount(idleHandler) == 0) {
W_FlushIdleNotificationQueue();
/* make sure an observer in queue didn't added an idle handler */
return (idleHandler != NULL && WMGetArrayItemCount(idleHandler) > 0);
}
@@ -292,8 +291,6 @@ Bool W_CheckIdleHandlers(void)
WMFreeArray(handlerCopy);
W_FlushIdleNotificationQueue();
/* this is not necesarrily False, because one handler can re-add itself */
return (WMGetArrayItemCount(idleHandler) > 0);
}
@@ -304,7 +301,6 @@ void W_CheckTimerHandlers(void)
struct timeval now;
if (!timerHandler) {
W_FlushASAPNotificationQueue();
return;
}
@@ -331,8 +327,6 @@ void W_CheckTimerHandlers(void)
wfree(handler);
}
}
W_FlushASAPNotificationQueue();
}
/*
@@ -384,7 +378,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
nfds = 0;
if (!extrafd && nfds == 0) {
W_FlushASAPNotificationQueue();
return False;
}
@@ -461,8 +454,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
wfree(fds);
W_FlushASAPNotificationQueue();
return (count > 0);
#else
#ifdef HAVE_SELECT
@@ -479,7 +470,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
nfds = 0;
if (inputfd < 0 && nfds == 0) {
W_FlushASAPNotificationQueue();
return False;
}
@@ -556,8 +546,6 @@ Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
WMFreeArray(handlerCopy);
}
W_FlushASAPNotificationQueue();
return (count > 0);
#else /* not HAVE_SELECT, not HAVE_POLL */
# error Neither select nor poll. You lose.

View File

@@ -652,7 +652,7 @@ static void mpm_get_hostname(WParserMacro *this, WMenuParser parser)
return;
}
}
wstrlcpy((char *) this->value, h, sizeof(this->value) );
strlcpy((char *) this->value, h, sizeof(this->value) );
}
/* Name of the current user */
@@ -677,7 +677,7 @@ static void mpm_get_user_name(WParserMacro *this, WMenuParser parser)
user = pw_user->pw_name;
if (user == NULL) goto error_no_username;
}
wstrlcpy((char *) this->value, user, sizeof(this->value) );
strlcpy((char *) this->value, user, sizeof(this->value) );
}
/* Number id of the user under which we are running */

View File

@@ -1,482 +0,0 @@
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "WUtil.h"
#include "WINGsP.h"
typedef struct W_Notification {
const char *name;
void *object;
void *clientData;
int refCount;
} Notification;
const char *WMGetNotificationName(WMNotification * notification)
{
return notification->name;
}
void *WMGetNotificationObject(WMNotification * notification)
{
return notification->object;
}
void *WMGetNotificationClientData(WMNotification * notification)
{
return notification->clientData;
}
WMNotification *WMCreateNotification(const char *name, void *object, void *clientData)
{
Notification *nPtr;
nPtr = wmalloc(sizeof(Notification));
nPtr->name = name;
nPtr->object = object;
nPtr->clientData = clientData;
nPtr->refCount = 1;
return nPtr;
}
void WMReleaseNotification(WMNotification * notification)
{
notification->refCount--;
if (notification->refCount < 1) {
wfree(notification);
}
}
WMNotification *WMRetainNotification(WMNotification * notification)
{
notification->refCount++;
return notification;
}
/***************** Notification Center *****************/
typedef struct NotificationObserver {
WMNotificationObserverAction *observerAction;
void *observer;
const char *name;
void *object;
struct NotificationObserver *prev; /* for tables */
struct NotificationObserver *next;
struct NotificationObserver *nextAction; /* for observerTable */
} NotificationObserver;
typedef struct W_NotificationCenter {
WMHashTable *nameTable; /* names -> observer lists */
WMHashTable *objectTable; /* object -> observer lists */
NotificationObserver *nilList; /* obervers that catch everything */
WMHashTable *observerTable; /* observer -> NotificationObserver */
} NotificationCenter;
/* default (and only) center */
static NotificationCenter *notificationCenter = NULL;
void W_InitNotificationCenter(void)
{
notificationCenter = wmalloc(sizeof(NotificationCenter));
notificationCenter->nameTable = WMCreateStringHashTable();
notificationCenter->objectTable = WMCreateIdentityHashTable();
notificationCenter->nilList = NULL;
notificationCenter->observerTable = WMCreateIdentityHashTable();
}
void W_ReleaseNotificationCenter(void)
{
if (notificationCenter) {
if (notificationCenter->nameTable)
WMFreeHashTable(notificationCenter->nameTable);
if (notificationCenter->objectTable)
WMFreeHashTable(notificationCenter->objectTable);
if (notificationCenter->observerTable)
WMFreeHashTable(notificationCenter->observerTable);
wfree(notificationCenter);
notificationCenter = NULL;
}
}
void
WMAddNotificationObserver(WMNotificationObserverAction * observerAction,
void *observer, const char *name, void *object)
{
NotificationObserver *oRec, *rec;
oRec = wmalloc(sizeof(NotificationObserver));
oRec->observerAction = observerAction;
oRec->observer = observer;
oRec->name = name;
oRec->object = object;
oRec->next = NULL;
oRec->prev = NULL;
/* put this action in the list of actions for this observer */
rec = (NotificationObserver *) WMHashInsert(notificationCenter->observerTable, observer, oRec);
if (rec) {
/* if this is not the first action for the observer */
oRec->nextAction = rec;
} else {
oRec->nextAction = NULL;
}
if (!name && !object) {
/* catch-all */
oRec->next = notificationCenter->nilList;
if (notificationCenter->nilList) {
notificationCenter->nilList->prev = oRec;
}
notificationCenter->nilList = oRec;
} else if (!name) {
/* any message coming from object */
rec = (NotificationObserver *) WMHashInsert(notificationCenter->objectTable, object, oRec);
oRec->next = rec;
if (rec) {
rec->prev = oRec;
}
} else {
/* name && (object || !object) */
rec = (NotificationObserver *) WMHashInsert(notificationCenter->nameTable, name, oRec);
oRec->next = rec;
if (rec) {
rec->prev = oRec;
}
}
}
void WMPostNotification(WMNotification * notification)
{
NotificationObserver *orec, *tmp;
WMRetainNotification(notification);
/* tell the observers that want to know about a particular message */
orec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, notification->name);
while (orec) {
tmp = orec->next;
if (!orec->object || !notification->object || orec->object == notification->object) {
/* tell the observer */
if (orec->observerAction) {
(*orec->observerAction) (orec->observer, notification);
}
}
orec = tmp;
}
/* tell the observers that want to know about an object */
orec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, notification->object);
while (orec) {
tmp = orec->next;
/* tell the observer */
if (orec->observerAction) {
(*orec->observerAction) (orec->observer, notification);
}
orec = tmp;
}
/* tell the catch all observers */
orec = notificationCenter->nilList;
while (orec) {
tmp = orec->next;
/* tell the observer */
if (orec->observerAction) {
(*orec->observerAction) (orec->observer, notification);
}
orec = tmp;
}
WMReleaseNotification(notification);
}
void WMRemoveNotificationObserver(void *observer)
{
NotificationObserver *orec, *tmp, *rec;
/* get the list of actions the observer is doing */
orec = (NotificationObserver *) WMHashGet(notificationCenter->observerTable, observer);
/*
* FOREACH orec IN actionlist for observer
* DO
* remove from respective lists/tables
* free
* END
*/
while (orec) {
tmp = orec->nextAction;
if (!orec->name && !orec->object) {
/* catch-all */
if (notificationCenter->nilList == orec)
notificationCenter->nilList = orec->next;
} else if (!orec->name) {
/* any message coming from object */
rec = (NotificationObserver *) WMHashGet(notificationCenter->objectTable, orec->object);
if (rec == orec) {
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->objectTable, orec->object, orec->next);
} else {
WMHashRemove(notificationCenter->objectTable, orec->object);
}
}
} else {
/* name && (object || !object) */
rec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable, orec->name);
if (rec == orec) {
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->nameTable, orec->name, orec->next);
} else {
WMHashRemove(notificationCenter->nameTable, orec->name);
}
}
}
if (orec->prev)
orec->prev->next = orec->next;
if (orec->next)
orec->next->prev = orec->prev;
wfree(orec);
orec = tmp;
}
WMHashRemove(notificationCenter->observerTable, observer);
}
void WMRemoveNotificationObserverWithName(void *observer, const char *name, void *object)
{
NotificationObserver *orec, *tmp, *rec;
NotificationObserver *newList = NULL;
/* get the list of actions the observer is doing */
orec = (NotificationObserver *) WMHashGet(notificationCenter->observerTable, observer);
WMHashRemove(notificationCenter->observerTable, observer);
/* rebuild the list of actions for the observer */
while (orec) {
tmp = orec->nextAction;
if (orec->name == name && orec->object == object) {
if (!name && !object) {
if (notificationCenter->nilList == orec)
notificationCenter->nilList = orec->next;
} else if (!name) {
rec =
(NotificationObserver *) WMHashGet(notificationCenter->objectTable,
orec->object);
if (rec == orec) {
assert(rec->prev == NULL);
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->objectTable,
orec->object, orec->next);
} else {
WMHashRemove(notificationCenter->objectTable, orec->object);
}
}
} else {
rec = (NotificationObserver *) WMHashGet(notificationCenter->nameTable,
orec->name);
if (rec == orec) {
assert(rec->prev == NULL);
/* replace table entry */
if (orec->next) {
WMHashInsert(notificationCenter->nameTable,
orec->name, orec->next);
} else {
WMHashRemove(notificationCenter->nameTable, orec->name);
}
}
}
if (orec->prev)
orec->prev->next = orec->next;
if (orec->next)
orec->next->prev = orec->prev;
wfree(orec);
} else {
/* append this action in the new action list */
orec->nextAction = NULL;
if (!newList) {
newList = orec;
} else {
NotificationObserver *p;
p = newList;
while (p->nextAction) {
p = p->nextAction;
}
p->nextAction = orec;
}
}
orec = tmp;
}
/* reinsert the list to the table */
if (newList) {
WMHashInsert(notificationCenter->observerTable, observer, newList);
}
}
void WMPostNotificationName(const char *name, void *object, void *clientData)
{
WMNotification *notification;
notification = WMCreateNotification(name, object, clientData);
WMPostNotification(notification);
WMReleaseNotification(notification);
}
/**************** Notification Queues ****************/
typedef struct W_NotificationQueue {
WMArray *asapQueue;
WMArray *idleQueue;
struct W_NotificationQueue *next;
} NotificationQueue;
static WMNotificationQueue *notificationQueueList = NULL;
/* default queue */
static WMNotificationQueue *notificationQueue = NULL;
WMNotificationQueue *WMGetDefaultNotificationQueue(void)
{
if (!notificationQueue)
notificationQueue = WMCreateNotificationQueue();
return notificationQueue;
}
WMNotificationQueue *WMCreateNotificationQueue(void)
{
NotificationQueue *queue;
queue = wmalloc(sizeof(NotificationQueue));
queue->asapQueue = WMCreateArrayWithDestructor(8, (WMFreeDataProc *) WMReleaseNotification);
queue->idleQueue = WMCreateArrayWithDestructor(8, (WMFreeDataProc *) WMReleaseNotification);
queue->next = notificationQueueList;
notificationQueueList = queue;
return queue;
}
void WMEnqueueNotification(WMNotificationQueue * queue, WMNotification * notification, WMPostingStyle postingStyle)
{
WMEnqueueCoalesceNotification(queue, notification, postingStyle, WNCOnName | WNCOnSender);
}
#define NOTIF ((WMNotification*)cdata)
#define ITEM ((WMNotification*)item)
static int matchSenderAndName(const void *item, const void *cdata)
{
return (NOTIF->object == ITEM->object && strcmp(NOTIF->name, ITEM->name) == 0);
}
static int matchSender(const void *item, const void *cdata)
{
return (NOTIF->object == ITEM->object);
}
static int matchName(const void *item, const void *cdata)
{
return (strcmp(NOTIF->name, ITEM->name) == 0);
}
#undef NOTIF
#undef ITEM
void WMDequeueNotificationMatching(WMNotificationQueue * queue, WMNotification * notification, unsigned mask)
{
WMMatchDataProc *matchFunc;
if ((mask & WNCOnName) && (mask & WNCOnSender))
matchFunc = matchSenderAndName;
else if (mask & WNCOnName)
matchFunc = matchName;
else if (mask & WNCOnSender)
matchFunc = matchSender;
else
return;
WMRemoveFromArrayMatching(queue->asapQueue, matchFunc, notification);
WMRemoveFromArrayMatching(queue->idleQueue, matchFunc, notification);
}
void
WMEnqueueCoalesceNotification(WMNotificationQueue * queue,
WMNotification * notification, WMPostingStyle postingStyle, unsigned coalesceMask)
{
if (coalesceMask != WNCNone)
WMDequeueNotificationMatching(queue, notification, coalesceMask);
switch (postingStyle) {
case WMPostNow:
WMPostNotification(notification);
WMReleaseNotification(notification);
break;
case WMPostASAP:
WMAddToArray(queue->asapQueue, notification);
break;
case WMPostWhenIdle:
WMAddToArray(queue->idleQueue, notification);
break;
}
}
void W_FlushASAPNotificationQueue(void)
{
WMNotificationQueue *queue = notificationQueueList;
while (queue) {
while (WMGetArrayItemCount(queue->asapQueue)) {
WMPostNotification(WMGetFromArray(queue->asapQueue, 0));
WMDeleteFromArray(queue->asapQueue, 0);
}
queue = queue->next;
}
}
void W_FlushIdleNotificationQueue(void)
{
WMNotificationQueue *queue = notificationQueueList;
while (queue) {
while (WMGetArrayItemCount(queue->idleQueue)) {
WMPostNotification(WMGetFromArray(queue->idleQueue, 0));
WMDeleteFromArray(queue->idleQueue, 0);
}
queue = queue->next;
}
}

30
WINGs/proplist.c Normal file
View File

@@ -0,0 +1,30 @@
#include <stdarg.h>
#include "WUtil.h"
/*
* This should be written in Rust whenever va_args support improves.
*/
WMPropList *WMCreatePLArray(WMPropList * elem, ...)
{
WMPropList *plist, *nelem;
va_list ap;
plist = WMCreateEmptyPLArray();
if (!elem)
return plist;
WMAddToPLArray(plist, elem);
va_start(ap, elem);
while (1) {
nelem = va_arg(ap, WMPropList *);
if (!nelem) {
va_end(ap);
return plist;
}
WMAddToPLArray(plist, elem);
}
}

View File

@@ -1,425 +0,0 @@
/*
* Until FreeBSD gets their act together;
* http://www.mail-archive.com/freebsd-hackers@freebsd.org/msg69469.html
*/
#if defined( FREEBSD )
# undef _XOPEN_SOURCE
#endif
#include "wconfig.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#ifdef HAVE_BSD_STRING_H
#include <bsd/string.h>
#endif
#include "WUtil.h"
#define PRC_ALPHA 0
#define PRC_BLANK 1
#define PRC_ESCAPE 2
#define PRC_DQUOTE 3
#define PRC_EOS 4
#define PRC_SQUOTE 5
typedef struct {
short nstate;
short output;
} DFA;
static DFA mtable[9][6] = {
{{3, 1}, {0, 0}, {4, 0}, {1, 0}, {8, 0}, {6, 0}},
{{1, 1}, {1, 1}, {2, 0}, {3, 0}, {5, 0}, {1, 1}},
{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {5, 0}, {1, 1}},
{{3, 1}, {5, 0}, {4, 0}, {1, 0}, {5, 0}, {6, 0}},
{{3, 1}, {3, 1}, {3, 1}, {3, 1}, {5, 0}, {3, 1}},
{{-1, -1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* final state */
{{6, 1}, {6, 1}, {7, 0}, {6, 1}, {5, 0}, {3, 0}},
{{6, 1}, {6, 1}, {6, 1}, {6, 1}, {5, 0}, {6, 1}},
{{-1, -1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* final state */
};
char *wtokennext(char *word, char **next)
{
char *ptr;
char *ret, *t;
int state, ctype;
t = ret = wmalloc(strlen(word) + 1);
ptr = word;
state = 0;
while (1) {
if (*ptr == 0)
ctype = PRC_EOS;
else if (*ptr == '\\')
ctype = PRC_ESCAPE;
else if (*ptr == '"')
ctype = PRC_DQUOTE;
else if (*ptr == '\'')
ctype = PRC_SQUOTE;
else if (*ptr == ' ' || *ptr == '\t')
ctype = PRC_BLANK;
else
ctype = PRC_ALPHA;
if (mtable[state][ctype].output) {
*t = *ptr;
t++;
*t = 0;
}
state = mtable[state][ctype].nstate;
ptr++;
if (mtable[state][0].output < 0) {
break;
}
}
if (*ret == 0) {
wfree(ret);
ret = NULL;
}
if (ctype == PRC_EOS)
*next = NULL;
else
*next = ptr;
return ret;
}
/* separate a string in tokens, taking " and ' into account */
void wtokensplit(char *command, char ***argv, int *argc)
{
char *token, *line;
int count;
count = 0;
line = command;
do {
token = wtokennext(line, &line);
if (token) {
if (count == 0)
*argv = wmalloc(sizeof(**argv));
else
*argv = wrealloc(*argv, (count + 1) * sizeof(**argv));
(*argv)[count++] = token;
}
} while (token != NULL && line != NULL);
*argc = count;
}
char *wtokenjoin(char **list, int count)
{
int i, j;
char *flat_string, *wspace;
j = 0;
for (i = 0; i < count; i++) {
if (list[i] != NULL && list[i][0] != 0) {
j += strlen(list[i]);
if (strpbrk(list[i], " \t"))
j += 2;
}
}
flat_string = wmalloc(j + count + 1);
for (i = 0; i < count; i++) {
if (list[i] != NULL && list[i][0] != 0) {
if (i > 0 &&
wstrlcat(flat_string, " ", j + count + 1) >= j + count + 1)
goto error;
wspace = strpbrk(list[i], " \t");
if (wspace &&
wstrlcat(flat_string, "\"", j + count + 1) >= j + count + 1)
goto error;
if (wstrlcat(flat_string, list[i], j + count + 1) >= j + count + 1)
goto error;
if (wspace &&
wstrlcat(flat_string, "\"", j + count + 1) >= j + count + 1)
goto error;
}
}
return flat_string;
error:
wfree(flat_string);
return NULL;
}
void wtokenfree(char **tokens, int count)
{
while (count--)
wfree(tokens[count]);
wfree(tokens);
}
char *wtrimspace(const char *s)
{
const char *t;
if (s == NULL)
return NULL;
while (isspace(*s) && *s)
s++;
t = s + strlen(s) - 1;
while (t > s && isspace(*t))
t--;
return wstrndup(s, t - s + 1);
}
char *wstrdup(const char *str)
{
assert(str != NULL);
return strcpy(wmalloc(strlen(str) + 1), str);
}
char *wstrndup(const char *str, size_t len)
{
char *copy;
assert(str != NULL);
len = WMIN(len, strlen(str));
copy = strncpy(wmalloc(len + 1), str, len);
copy[len] = 0;
return copy;
}
char *wstrconcat(const char *str1, const char *str2)
{
char *str;
size_t slen, slen1;
if (!str1 && str2)
return wstrdup(str2);
else if (str1 && !str2)
return wstrdup(str1);
else if (!str1 && !str2)
return NULL;
slen1 = strlen(str1);
slen = slen1 + strlen(str2) + 1;
str = wmalloc(slen);
strcpy(str, str1);
strcpy(str + slen1, str2);
return str;
}
char *wstrappend(char *dst, const char *src)
{
size_t slen;
if (!src || *src == 0)
return dst;
else if (!dst)
return wstrdup(src);
slen = strlen(dst) + strlen(src) + 1;
dst = wrealloc(dst, slen);
strcat(dst, src);
return dst;
}
#ifdef HAVE_STRLCAT
size_t
wstrlcat(char *dst, const char *src, size_t siz)
{
return strlcat(dst, src, siz);
}
#else
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
wstrlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
#endif /* HAVE_STRLCAT */
#ifdef HAVE_STRLCPY
size_t
wstrlcpy(char *dst, const char *src, size_t siz)
{
return strlcpy(dst, src, siz);
}
#else
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
wstrlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
#endif /* HAVE_STRLCPY */
/* transform `s' so that the result is safe to pass to the shell as an argument.
* returns a newly allocated string.
* with very heavy inspirations from NetBSD's shquote(3).
*/
char *wshellquote(const char *s)
{
char *p, *r, *last, *ret;
size_t slen;
int needs_quoting;
if (!s)
return NULL;
needs_quoting = !*s; /* the empty string does need quoting */
/* do not quote if consists only of the following characters */
for (p = (char *)s; *p && !needs_quoting; p++) {
needs_quoting = !(isalnum(*p) || (*p == '+') || (*p == '/') ||
(*p == '.') || (*p == ',') || (*p == '-'));
}
if (!needs_quoting)
return wstrdup(s);
for (slen = 0, p = (char *)s; *p; p++) /* count space needed (worst case) */
slen += *p == '\'' ? 4 : 1; /* every single ' becomes ''\' */
slen += 2 /* leading + trailing "'" */ + 1 /* NULL */;
ret = r = wmalloc(slen);
p = (char *)s;
last = p;
if (*p != '\'') /* if string doesn't already begin with "'" */
*r++ ='\''; /* start putting it in quotes */
while (*p) {
last = p;
if (*p == '\'') { /* turn each ' into ''\' */
if (p != s) /* except if it's the first ', in which case */
*r++ = '\''; /* only escape it */
*r++ = '\\';
*r++ = '\'';
while (*++p && *p == '\'') { /* keep turning each consecutive 's into \' */
*r++ = '\\';
*r++ = '\'';
}
if (*p) /* if more input follows, terminate */
*r++ = '\''; /* what we have so far */
} else {
*r++ = *p++;
}
}
if (*last != '\'') /* if the last one isn't already a ' */
*r++ = '\''; /* terminate the whole shebang */
*r = '\0';
return ret; /* technically, we lose (but not leak) a couple of */
/* bytes (twice the number of consecutive 's in the */
/* input or so), but since these are relatively rare */
/* and short-lived strings, not sure if a trip to */
/* wstrdup+wfree worths the gain. */
}

View File

@@ -1,255 +0,0 @@
#include <string.h>
#include "WUtil.h"
typedef struct W_TreeNode {
void *data;
/*unsigned int uflags:16; */
WMArray *leaves;
int depth;
struct W_TreeNode *parent;
WMFreeDataProc *destructor;
} W_TreeNode;
static void destroyNode(void *data)
{
WMTreeNode *aNode = (WMTreeNode *) data;
if (aNode->destructor) {
(*aNode->destructor) (aNode->data);
}
if (aNode->leaves) {
WMFreeArray(aNode->leaves);
}
wfree(aNode);
}
WMTreeNode *WMCreateTreeNode(void *data)
{
return WMCreateTreeNodeWithDestructor(data, NULL);
}
WMTreeNode *WMCreateTreeNodeWithDestructor(void *data, WMFreeDataProc * destructor)
{
WMTreeNode *aNode;
aNode = (WMTreeNode *) wmalloc(sizeof(W_TreeNode));
aNode->destructor = destructor;
aNode->data = data;
aNode->parent = NULL;
aNode->depth = 0;
aNode->leaves = NULL;
/*aNode->leaves = WMCreateArrayWithDestructor(1, destroyNode); */
return aNode;
}
WMTreeNode *WMInsertItemInTree(WMTreeNode * parent, int index, void *item)
{
WMTreeNode *aNode;
wassertrv(parent != NULL, NULL);
aNode = WMCreateTreeNodeWithDestructor(item, parent->destructor);
aNode->parent = parent;
aNode->depth = parent->depth + 1;
if (!parent->leaves) {
parent->leaves = WMCreateArrayWithDestructor(1, destroyNode);
}
if (index < 0) {
WMAddToArray(parent->leaves, aNode);
} else {
WMInsertInArray(parent->leaves, index, aNode);
}
return aNode;
}
static void updateNodeDepth(WMTreeNode * aNode, int depth)
{
int i;
aNode->depth = depth;
if (aNode->leaves) {
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
updateNodeDepth(WMGetFromArray(aNode->leaves, i), depth + 1);
}
}
}
WMTreeNode *WMInsertNodeInTree(WMTreeNode * parent, int index, WMTreeNode * aNode)
{
wassertrv(parent != NULL, NULL);
wassertrv(aNode != NULL, NULL);
aNode->parent = parent;
updateNodeDepth(aNode, parent->depth + 1);
if (!parent->leaves) {
parent->leaves = WMCreateArrayWithDestructor(1, destroyNode);
}
if (index < 0) {
WMAddToArray(parent->leaves, aNode);
} else {
WMInsertInArray(parent->leaves, index, aNode);
}
return aNode;
}
void WMDestroyTreeNode(WMTreeNode * aNode)
{
wassertr(aNode != NULL);
if (aNode->parent && aNode->parent->leaves) {
WMRemoveFromArray(aNode->parent->leaves, aNode);
} else {
destroyNode(aNode);
}
}
void WMDeleteLeafForTreeNode(WMTreeNode * aNode, int index)
{
wassertr(aNode != NULL);
wassertr(aNode->leaves != NULL);
WMDeleteFromArray(aNode->leaves, index);
}
static int sameData(const void *item, const void *data)
{
return (((WMTreeNode *) item)->data == data);
}
void WMRemoveLeafForTreeNode(WMTreeNode * aNode, void *leaf)
{
int index;
wassertr(aNode != NULL);
wassertr(aNode->leaves != NULL);
index = WMFindInArray(aNode->leaves, sameData, leaf);
if (index != WANotFound) {
WMDeleteFromArray(aNode->leaves, index);
}
}
void *WMReplaceDataForTreeNode(WMTreeNode * aNode, void *newData)
{
void *old;
wassertrv(aNode != NULL, NULL);
old = aNode->data;
aNode->data = newData;
return old;
}
void *WMGetDataForTreeNode(WMTreeNode * aNode)
{
return aNode->data;
}
int WMGetTreeNodeDepth(WMTreeNode * aNode)
{
return aNode->depth;
}
WMTreeNode *WMGetParentForTreeNode(WMTreeNode * aNode)
{
return aNode->parent;
}
void WMSortLeavesForTreeNode(WMTreeNode * aNode, WMCompareDataProc * comparer)
{
wassertr(aNode != NULL);
if (aNode->leaves) {
WMSortArray(aNode->leaves, comparer);
}
}
static void sortLeavesForNode(WMTreeNode * aNode, WMCompareDataProc * comparer)
{
int i;
if (!aNode->leaves)
return;
WMSortArray(aNode->leaves, comparer);
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
sortLeavesForNode(WMGetFromArray(aNode->leaves, i), comparer);
}
}
void WMSortTree(WMTreeNode * aNode, WMCompareDataProc * comparer)
{
wassertr(aNode != NULL);
sortLeavesForNode(aNode, comparer);
}
static WMTreeNode *findNodeInTree(WMTreeNode * aNode, WMMatchDataProc * match, void *cdata, int limit)
{
if (match == NULL && aNode->data == cdata)
return aNode;
else if (match && (*match) (aNode->data, cdata))
return aNode;
if (aNode->leaves && limit != 0) {
WMTreeNode *leaf;
int i;
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
leaf = findNodeInTree(WMGetFromArray(aNode->leaves, i),
match, cdata, limit > 0 ? limit - 1 : limit);
if (leaf)
return leaf;
}
}
return NULL;
}
WMTreeNode *WMFindInTree(WMTreeNode * aTree, WMMatchDataProc * match, void *cdata)
{
wassertrv(aTree != NULL, NULL);
return findNodeInTree(aTree, match, cdata, -1);
}
WMTreeNode *WMFindInTreeWithDepthLimit(WMTreeNode * aTree, WMMatchDataProc * match, void *cdata, int limit)
{
wassertrv(aTree != NULL, NULL);
wassertrv(limit >= 0, NULL);
return findNodeInTree(aTree, match, cdata, limit);
}
void WMTreeWalk(WMTreeNode * aNode, WMTreeWalkProc * walk, void *data, Bool DepthFirst)
{
int i;
WMTreeNode *leaf;
wassertr(aNode != NULL);
if (DepthFirst)
(*walk)(aNode, data);
if (aNode->leaves) {
for (i = 0; i < WMGetArrayItemCount(aNode->leaves); i++) {
leaf = (WMTreeNode *)WMGetFromArray(aNode->leaves, i);
WMTreeWalk(leaf, walk, data, DepthFirst);
}
}
if (!DepthFirst)
(*walk)(aNode, data);
}

View File

@@ -44,9 +44,6 @@ void WMInitializeApplication(const char *applicationName, int *argc, char **argv
WMApplication.argv[i] = wstrdup(argv[i]);
}
WMApplication.argv[i] = NULL;
/* initialize notification center */
W_InitNotificationCenter();
}
void WMReleaseApplication(void) {
@@ -60,7 +57,7 @@ void WMReleaseApplication(void) {
*/
w_save_defaults_changes();
W_ReleaseNotificationCenter();
W_ClearNotificationCenter();
if (WMApplication.applicationName) {
wfree(WMApplication.applicationName);
@@ -101,21 +98,21 @@ static char *checkFile(const char *path, const char *folder, const char *ext, co
slen = strlen(path) + strlen(resource) + 1 + extralen;
ret = wmalloc(slen);
if (wstrlcpy(ret, path, slen) >= slen)
if (strlcpy(ret, path, slen) >= slen)
goto error;
if (folder &&
(wstrlcat(ret, "/", slen) >= slen ||
wstrlcat(ret, folder, slen) >= slen))
(strlcat(ret, "/", slen) >= slen ||
strlcat(ret, folder, slen) >= slen))
goto error;
if (ext &&
(wstrlcat(ret, "/", slen) >= slen ||
wstrlcat(ret, ext, slen) >= slen))
(strlcat(ret, "/", slen) >= slen ||
strlcat(ret, ext, slen) >= slen))
goto error;
if (wstrlcat(ret, "/", slen) >= slen ||
wstrlcat(ret, resource, slen) >= slen)
if (strlcat(ret, "/", slen) >= slen ||
strlcat(ret, resource, slen) >= slen)
goto error;
if (access(ret, F_OK) != 0)

View File

@@ -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);
}

View File

@@ -314,7 +314,7 @@ static void removeColumn(WMBrowser * bPtr, int column)
wfree(bPtr->titles[i]);
bPtr->titles[i] = NULL;
}
WMRemoveNotificationObserverWithName(bPtr, WMListSelectionDidChangeNotification, bPtr->columns[i]);
WMRemoveNotificationObserver(bPtr->columns[i]);
WMDestroyWidget(bPtr->columns[i]);
bPtr->columns[i] = NULL;
}
@@ -720,14 +720,14 @@ char *WMGetBrowserPathToColumn(WMBrowser * bPtr, int column)
path = wmalloc(slen);
/* ignore first `/' */
for (i = 0; i <= column; i++) {
if (wstrlcat(path, bPtr->pathSeparator, slen) >= slen)
if (strlcat(path, bPtr->pathSeparator, slen) >= slen)
goto error;
item = WMGetListSelectedItem(bPtr->columns[i]);
if (!item)
break;
if (wstrlcat(path, item->text, slen) >= slen)
if (strlcat(path, item->text, slen) >= slen)
goto error;
}
@@ -782,7 +782,7 @@ WMArray *WMGetBrowserPaths(WMBrowser * bPtr)
path = wmalloc(slen);
/* ignore first `/' */
for (i = 0; i <= column; i++) {
wstrlcat(path, bPtr->pathSeparator, slen);
strlcat(path, bPtr->pathSeparator, slen);
if (i == column) {
item = lastItem;
} else {
@@ -790,7 +790,7 @@ WMArray *WMGetBrowserPaths(WMBrowser * bPtr)
}
if (!item)
break;
wstrlcat(path, item->text, slen);
strlcat(path, item->text, slen);
}
WMAddToArray(paths, path);
}
@@ -1130,25 +1130,25 @@ static char *createTruncatedString(WMFont * font, const char *text, int *textLen
if (width >= 3 * dLen) {
int tmpTextLen = *textLen;
if (wstrlcpy(textBuf, text, slen) >= slen)
if (strlcpy(textBuf, text, slen) >= slen)
goto error;
while (tmpTextLen && (WMWidthOfString(font, textBuf, tmpTextLen) + 3 * dLen > width))
tmpTextLen--;
if (wstrlcpy(textBuf + tmpTextLen, "...", slen) >= slen)
if (strlcpy(textBuf + tmpTextLen, "...", slen) >= slen)
goto error;
*textLen = tmpTextLen + 3;
} else if (width >= 2 * dLen) {
if (wstrlcpy(textBuf, "..", slen) >= slen)
if (strlcpy(textBuf, "..", slen) >= slen)
goto error;
*textLen = 2;
} else if (width >= dLen) {
if (wstrlcpy(textBuf, ".", slen) >= slen)
if (strlcpy(textBuf, ".", slen) >= slen)
goto error;
*textLen = 1;

View File

@@ -513,12 +513,12 @@ static void listDirectoryOnColumn(WMFilePanel * panel, int column, const char *p
if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
continue;
if (wstrlcpy(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf))
if (strlcpy(pbuf, path, sizeof(pbuf)) >= sizeof(pbuf))
goto out;
if (strcmp(path, "/") != 0 &&
wstrlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf))
strlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf))
goto out;
if (wstrlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf))
if (strlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf))
goto out;
if (stat(pbuf, &stat_buf) != 0) {
@@ -626,11 +626,11 @@ static void createDir(WMWidget *widget, void *p_panel)
file = wmalloc(slen);
if (directory &&
(wstrlcat(file, directory, slen) >= slen ||
wstrlcat(file, "/", slen) >= slen))
(strlcat(file, directory, slen) >= slen ||
strlcat(file, "/", slen) >= slen))
goto out;
if (wstrlcat(file, dirName, slen) >= slen)
if (strlcat(file, dirName, slen) >= slen)
goto out;
if (mkdir(file, 00777) != 0) {

View File

@@ -1,433 +0,0 @@
#include <stdlib.h>
#include "wconfig.h"
#include "WINGsP.h"
#include <wraster.h>
#include <assert.h>
#include <X11/Xlocale.h>
#include <X11/Xft/Xft.h>
#include <fontconfig/fontconfig.h>
#ifdef USE_PANGO
#include <pango/pango.h>
#include <pango/pangofc-fontmap.h>
#include <pango/pangoxft.h>
#endif
#define DEFAULT_FONT "sans serif:pixelsize=12"
#define DEFAULT_SIZE WINGsConfiguration.defaultFontSize
static FcPattern *xlfdToFcPattern(const char *xlfd)
{
FcPattern *pattern;
char *fname, *ptr;
/* Just skip old font names that contain %d in them.
* We don't support that anymore. */
if (strchr(xlfd, '%') != NULL)
return FcNameParse((FcChar8 *) DEFAULT_FONT);
fname = wstrdup(xlfd);
if ((ptr = strchr(fname, ','))) {
*ptr = 0;
}
pattern = XftXlfdParse(fname, False, False);
wfree(fname);
if (!pattern) {
wwarning(_("invalid font: %s. Trying '%s'"), xlfd, DEFAULT_FONT);
pattern = FcNameParse((FcChar8 *) DEFAULT_FONT);
}
return pattern;
}
static char *xlfdToFcName(const char *xlfd)
{
FcPattern *pattern;
char *fname;
char *result;
pattern = xlfdToFcPattern(xlfd);
fname = (char *)FcNameUnparse(pattern);
result = wstrdup(fname);
free(fname);
FcPatternDestroy(pattern);
return result;
}
static Bool hasProperty(FcPattern * pattern, const char *property)
{
FcValue val;
if (FcPatternGet(pattern, property, 0, &val) == FcResultMatch) {
return True;
}
return False;
}
static Bool hasPropertyWithStringValue(FcPattern * pattern, const char *object, const char *value)
{
FcChar8 *str;
int id;
if (!value || value[0] == 0)
return True;
id = 0;
while (FcPatternGetString(pattern, object, id, &str) == FcResultMatch) {
if (strcasecmp(value, (char *)str) == 0) {
return True;
}
id++;
}
return False;
}
static char *makeFontOfSize(const char *font, int size, const char *fallback)
{
FcPattern *pattern;
char *name;
char *result;
if (font[0] == '-') {
pattern = xlfdToFcPattern(font);
} else {
pattern = FcNameParse((const FcChar8 *) font);
}
/*FcPatternPrint(pattern); */
if (size > 0) {
FcPatternDel(pattern, FC_PIXEL_SIZE);
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)size);
} else if (size == 0 && !hasProperty(pattern, "size") && !hasProperty(pattern, FC_PIXEL_SIZE)) {
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)DEFAULT_SIZE);
}
if (fallback && !hasPropertyWithStringValue(pattern, FC_FAMILY, fallback)) {
FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) fallback);
}
/*FcPatternPrint(pattern); */
name = (char *)FcNameUnparse(pattern);
result = wstrdup(name);
free(name);
FcPatternDestroy(pattern);
return result;
}
WMFont *WMCreateFont(WMScreen * scrPtr, const char *fontName)
{
Display *display = scrPtr->display;
WMFont *font;
char *fname;
#ifdef USE_PANGO
PangoFontMap *fontmap;
PangoContext *context;
PangoLayout *layout;
FcPattern *pattern;
PangoFontDescription *description;
double size;
#endif
if (fontName[0] == '-') {
fname = xlfdToFcName(fontName);
} else {
fname = wstrdup(fontName);
}
if (!WINGsConfiguration.antialiasedText && !strstr(fname, ":antialias=")) {
fname = wstrappend(fname, ":antialias=false");
}
font = WMHashGet(scrPtr->fontCache, fname);
if (font) {
WMRetainFont(font);
wfree(fname);
return font;
}
font = wmalloc(sizeof(WMFont));
font->screen = scrPtr;
font->font = XftFontOpenName(display, scrPtr->screen, fname);
if (!font->font) {
wfree(font);
wfree(fname);
return NULL;
}
font->height = font->font->ascent + font->font->descent;
font->y = font->font->ascent;
font->refCount = 1;
font->name = fname;
#ifdef USE_PANGO
fontmap = pango_xft_get_font_map(scrPtr->display, scrPtr->screen);
context = pango_font_map_create_context(fontmap);
layout = pango_layout_new(context);
pattern = FcNameParse((FcChar8 *) font->name);
description = pango_fc_font_description_from_pattern(pattern, FALSE);
/* Pango examines FC_SIZE but not FC_PIXEL_SIZE of the patten, but
* font-name has only "pixelsize", so set the size manually here.
*/
if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
pango_font_description_set_absolute_size(description, size * PANGO_SCALE);
pango_layout_set_font_description(layout, description);
font->layout = layout;
#endif
assert(WMHashInsert(scrPtr->fontCache, font->name, font) == NULL);
return font;
}
WMFont *WMRetainFont(WMFont * font)
{
wassertrv(font != NULL, NULL);
font->refCount++;
return font;
}
void WMReleaseFont(WMFont * font)
{
wassertr(font != NULL);
font->refCount--;
if (font->refCount < 1) {
XftFontClose(font->screen->display, font->font);
if (font->name) {
WMHashRemove(font->screen->fontCache, font->name);
wfree(font->name);
}
wfree(font);
}
}
Bool WMIsAntialiasingEnabled(WMScreen * scrPtr)
{
return scrPtr->antialiasedText;
}
unsigned int WMFontHeight(WMFont * font)
{
wassertrv(font != NULL, 0);
return font->height;
}
char *WMGetFontName(WMFont * font)
{
wassertrv(font != NULL, NULL);
return font->name;
}
void WMGetScaleBaseFromSystemFont(WMScreen *scrPtr, int *alphabetWidth, int *fontHeight)
{
WMFont *font;
font = WMDefaultSystemFont(scrPtr);
*alphabetWidth = WMWidthOfString(font, "abcdefghijklmnopqrstuvwxyz", 26);
*fontHeight = WMFontHeight(font);
WMReleaseFont(font);
}
WMFont *WMDefaultSystemFont(WMScreen * scrPtr)
{
return WMRetainFont(scrPtr->normalFont);
}
WMFont *WMDefaultBoldSystemFont(WMScreen * scrPtr)
{
return WMRetainFont(scrPtr->boldFont);
}
WMFont *WMSystemFontOfSize(WMScreen * scrPtr, int size)
{
WMFont *font;
char *fontSpec;
fontSpec = makeFontOfSize(WINGsConfiguration.systemFont, size, NULL);
font = WMCreateFont(scrPtr, fontSpec);
if (!font) {
wwarning(_("could not load font: %s."), fontSpec);
}
wfree(fontSpec);
return font;
}
WMFont *WMBoldSystemFontOfSize(WMScreen * scrPtr, int size)
{
WMFont *font;
char *fontSpec;
fontSpec = makeFontOfSize(WINGsConfiguration.boldSystemFont, size, NULL);
font = WMCreateFont(scrPtr, fontSpec);
if (!font) {
wwarning(_("could not load font: %s."), fontSpec);
}
wfree(fontSpec);
return font;
}
int WMWidthOfString(WMFont * font, const char *text, int length)
{
#ifdef USE_PANGO
const char *previous_text;
int width;
#else
XGlyphInfo extents;
#endif
wassertrv(font != NULL && text != NULL, 0);
#ifdef USE_PANGO
previous_text = pango_layout_get_text(font->layout);
if ((previous_text == NULL) || (strncmp(text, previous_text, length) != 0) || previous_text[length] != '\0')
pango_layout_set_text(font->layout, text, length);
pango_layout_get_pixel_size(font->layout, &width, NULL);
return width;
#else
XftTextExtentsUtf8(font->screen->display, font->font, (XftChar8 *) text, length, &extents);
return extents.xOff; /* don't ask :P */
#endif
}
void WMDrawString(WMScreen * scr, Drawable d, WMColor * color, WMFont * font, int x, int y, const char *text, int length)
{
XftColor xftcolor;
#ifdef USE_PANGO
const char *previous_text;
#endif
wassertr(font != NULL);
xftcolor.color.red = color->color.red;
xftcolor.color.green = color->color.green;
xftcolor.color.blue = color->color.blue;
xftcolor.color.alpha = color->alpha;;
xftcolor.pixel = W_PIXEL(color);
XftDrawChange(scr->xftdraw, d);
#ifdef USE_PANGO
previous_text = pango_layout_get_text(font->layout);
if ((previous_text == NULL) || (strcmp(text, previous_text) != 0))
pango_layout_set_text(font->layout, text, length);
pango_xft_render_layout(scr->xftdraw, &xftcolor, font->layout, x * PANGO_SCALE, y * PANGO_SCALE);
#else
XftDrawStringUtf8(scr->xftdraw, &xftcolor, font->font, x, y + font->y, (XftChar8 *) text, length);
#endif
}
void
WMDrawImageString(WMScreen * scr, Drawable d, WMColor * color, WMColor * background,
WMFont * font, int x, int y, const char *text, int length)
{
XftColor textColor;
XftColor bgColor;
#ifdef USE_PANGO
const char *previous_text;
#endif
wassertr(font != NULL);
textColor.color.red = color->color.red;
textColor.color.green = color->color.green;
textColor.color.blue = color->color.blue;
textColor.color.alpha = color->alpha;;
textColor.pixel = W_PIXEL(color);
bgColor.color.red = background->color.red;
bgColor.color.green = background->color.green;
bgColor.color.blue = background->color.blue;
bgColor.color.alpha = background->alpha;;
bgColor.pixel = W_PIXEL(background);
XftDrawChange(scr->xftdraw, d);
XftDrawRect(scr->xftdraw, &bgColor, x, y, WMWidthOfString(font, text, length), font->height);
#ifdef USE_PANGO
previous_text = pango_layout_get_text(font->layout);
if ((previous_text == NULL) || (strcmp(text, previous_text) != 0))
pango_layout_set_text(font->layout, text, length);
pango_xft_render_layout(scr->xftdraw, &textColor, font->layout, x * PANGO_SCALE, y * PANGO_SCALE);
#else
XftDrawStringUtf8(scr->xftdraw, &textColor, font->font, x, y + font->y, (XftChar8 *) text, length);
#endif
}
WMFont *WMCopyFontWithStyle(WMScreen * scrPtr, WMFont * font, WMFontStyle style)
{
FcPattern *pattern;
WMFont *copy;
char *name;
if (!font)
return NULL;
/* It's enough to add italic to slant, even if the font has no italic
* variant, but only oblique. This is because fontconfig will actually
* return the closest match font to what we requested which is the
* oblique font. Same goes for using bold for weight.
*/
pattern = FcNameParse((FcChar8 *) WMGetFontName(font));
switch (style) {
case WFSNormal:
FcPatternDel(pattern, FC_WEIGHT);
FcPatternDel(pattern, FC_SLANT);
break;
case WFSBold:
FcPatternDel(pattern, FC_WEIGHT);
FcPatternAddString(pattern, FC_WEIGHT, (FcChar8 *) "bold");
break;
case WFSItalic:
FcPatternDel(pattern, FC_SLANT);
FcPatternAddString(pattern, FC_SLANT, (FcChar8 *) "italic");
break;
case WFSBoldItalic:
FcPatternDel(pattern, FC_WEIGHT);
FcPatternDel(pattern, FC_SLANT);
FcPatternAddString(pattern, FC_WEIGHT, (FcChar8 *) "bold");
FcPatternAddString(pattern, FC_SLANT, (FcChar8 *) "italic");
break;
}
name = (char *)FcNameUnparse(pattern);
copy = WMCreateFont(scrPtr, name);
FcPatternDestroy(pattern);
free(name);
return copy;
}

View File

@@ -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,14 +551,15 @@ 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];
WMListItem *item;
WM_ITERATE_ARRAY(array, fam, i) {
wstrlcpy(buffer, fam->name, sizeof(buffer));
strlcpy(buffer, fam->name, sizeof(buffer));
item = WMAddListItem(panel->famLs, buffer);
item->clientData = fam;
@@ -640,7 +641,7 @@ static void familyClick(WMWidget * w, void *data)
int top = 0;
WMListItem *fitem;
wstrlcpy(buffer, face->typeface, sizeof(buffer));
strlcpy(buffer, face->typeface, sizeof(buffer));
if (strcasecmp(face->typeface, "Roman") == 0)
top = 1;
if (strcasecmp(face->typeface, "Regular") == 0)
@@ -773,7 +774,7 @@ static void setFontPanelFontName(FontPanel * panel, const char *family, const ch
int top = 0;
WMListItem *fitem;
wstrlcpy(buffer, face->typeface, sizeof(buffer));
strlcpy(buffer, face->typeface, sizeof(buffer));
if (strcasecmp(face->typeface, "Roman") == 0)
top = 1;
if (top)

View File

@@ -610,10 +610,9 @@ WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * c
assert(W_ApplicationInitialized());
}
scrPtr = malloc(sizeof(W_Screen));
scrPtr = wmalloc(sizeof(W_Screen));
if (!scrPtr)
return NULL;
memset(scrPtr, 0, sizeof(W_Screen));
scrPtr->aflags.hasAppIcon = 1;
@@ -630,7 +629,8 @@ WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * c
scrPtr->rootWin = RootWindow(display, screen);
scrPtr->fontCache = WMCreateStringHashTable();
// Will be initialized lazily in wings-rs/src/screen.rs.
scrPtr->fontCache = NULL;
scrPtr->xftdraw = XftDrawCreate(scrPtr->display, W_DRAWABLE(scrPtr), scrPtr->visual, scrPtr->colormap);
@@ -910,18 +910,6 @@ WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * c
return scrPtr;
}
void WMSetWidgetDefaultFont(WMScreen * scr, WMFont * font)
{
WMReleaseFont(scr->normalFont);
scr->normalFont = WMRetainFont(font);
}
void WMSetWidgetDefaultBoldFont(WMScreen * scr, WMFont * font)
{
WMReleaseFont(scr->boldFont);
scr->boldFont = WMRetainFont(font);
}
void WMHangData(WMWidget * widget, void *data)
{
W_VIEW(widget)->hangedData = data;

14
WINGs/wings-rs/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "wings-rs"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["staticlib"]
[dependencies]
libc = "0.2.177"
pango-sys = "0.21.2"
wutil-rs = { path = "../../wutil-rs" }
x11 = "2.21.0"
yeslogic-fontconfig-sys = "6.0"

View File

@@ -0,0 +1,28 @@
AUTOMAKE_OPTIONS =
RUST_SOURCES = \
src/WINGsP.rs \
src/configuration.rs \
src/font.rs \
src/lib.rs \
src/pango_extras.rs \
src/screen.rs
RUST_EXTRA = \
Cargo.lock \
Cargo.toml
src/WINGsP.rs: ../WINGs/WINGsP.h ../../wrlib/wraster.h ../WINGs/WINGs.h ../WINGs/WUtil.h Makefile patch_WINGsP.sh
$(BINDGEN) ../WINGs/WINGsP.h --ignore-functions --allowlist-type "^W_.+|^WM(View|Array|DragOperationType|Point|Data|OpenPanel|SavePanel|HashTable|DraggingInfo|SelectionProcs|Rect|EventProc|Widget|Size|Color|Pixmap|FilePanel)|R(Context|ContextAttributes|Image|RenderingMode|ScalingFilter|StdColormapMode|ImageFormat|Color)|_WINGsConfiguration" --no-recursive-allowlist -o src/WINGsP.rs -- @PANGO_CFLAGS@ -I../../wrlib -I.. && ./patch_WINGsP.sh src/WINGsP.rs
target/debug/libwings_rs.a: $(RUST_SOURCES) $(RUST_EXTRA)
$(CARGO) build
check-local:
$(CARGO) test
clean-local:
$(CARGO) clean
rm -f src/WINGsP.rs
all: target/debug/libwings_rs.a

22
WINGs/wings-rs/patch_WINGsP.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
# This file provides ad-hoc fixups to the WINGsP provided by bindgen:
# - Import Xlib symbols so that everything compiles.
# - The opaque type names _XftDraw and _XftFont are replaced with void*.
# - Pango bindings aren't yet pulled into our Rust code, so PangoLayout is also demoted to void*.
set -e
if [ "x$1" = "x" ]; then
echo "Usage: $(basename $0) <file to patch>"
exit 1
fi
FILE="$1"
exec sed -i -r \
-e "1s/^/use x11::xlib::*;\nuse crate::font::ffi::WMFont;\n\n/" \
-e "s/_XftDraw/::std::ffi::c_void/g" \
-e "s/_XftFont/::std::ffi::c_void/g" \
-e "s/PangoLayout/::std::ffi::c_void/g" \
"$1"

View File

@@ -0,0 +1,66 @@
//! Global WINGs configuration.
//!
//! Use [`Configuration::global`] to get a snapshot of current configuration
//! settings.
//!
//! ## Rust rewrite notes
//!
//! This accesses a global that is defined in C code. Once more of WINGs is
//! migrated to Rust, we should use a different approach that is more
//! Rust-friendly. Threading a configuration object down the stack is a likely
//! way to go.
use crate::WINGsP::_WINGsConfiguration;
use std::{ffi::CStr, ptr::NonNull};
unsafe extern "C" {
static WINGsConfiguration: _WINGsConfiguration;
}
#[derive(Clone, Copy, Debug)]
pub struct Configuration {
pub system_font: &'static CStr,
pub bold_system_font: &'static CStr,
pub default_font_size: u16,
pub antialiased_text: bool,
pub floppy_path: &'static CStr,
pub double_click_delay: u32,
pub mouse_wheel_up: u32,
pub mouse_wheel_down: u32,
}
impl Configuration {
/// Returns the current WINGs configuration. Returns `None` if the
/// configuration appears not to have been loaded.
///
/// This should only be called after `W_ReadConfigurations` is called
/// (presumably when initializing WINGs in C code), but it should be safe to
/// call it at any time. (It may just contain nonsense values.)
///
/// ## Rust rewrite notes
///
/// We should migrate away from a static global and thread a configuration
/// object in some other way.
pub fn global() -> Option<Self> {
let c = unsafe { &WINGsConfiguration };
let system_font = unsafe { CStr::from_ptr(NonNull::new(c.systemFont)?.as_ptr()) };
let bold_system_font = unsafe { CStr::from_ptr(NonNull::new(c.boldSystemFont)?.as_ptr()) };
let default_font_size = u16::try_from(c.defaultFontSize).ok()?;
let antialiased_text = c.antialiasedText != 0;
let floppy_path = unsafe { CStr::from_ptr(NonNull::new(c.floppyPath)?.as_ptr()) };
let double_click_delay = u32::try_from(c.doubleClickDelay).ok()?;
let mouse_wheel_up = u32::try_from(c.mouseWheelUp).ok()?;
let mouse_wheel_down = u32::try_from(c.mouseWheelDown).ok()?;
Some(Configuration {
system_font,
bold_system_font,
default_font_size,
antialiased_text,
floppy_path,
double_click_delay,
mouse_wheel_up,
mouse_wheel_down,
})
}
}

586
WINGs/wings-rs/src/font.rs Normal file
View File

@@ -0,0 +1,586 @@
use crate::pango_extras;
use std::{
ffi::{CStr, CString, c_double, c_int, c_uint, c_void},
ptr::NonNull,
rc::Rc,
};
use crate::configuration::Configuration;
pub const DEFAULT_FONT: &'static str = "sans serif:pixelsize=12";
pub const DEFAULT_FONT_CSTR: &'static CStr = c"sans serif:pixelsize=12";
/// A loaded font, with support for basic drawing operations.
///
/// This owns Pango rendering state, and draw operations may mutate it. As a
/// result, this is not at all thread-safe.
///
/// ## Rust rewrite notes
///
/// Unlike the original C API, font caching is handled by methods on
/// [`W_Screen`].
pub struct Font {
name: FontName,
font: NonNull<x11::xft::XftFont>,
height: c_uint,
ascent: c_uint,
layout: NonNull<pango_sys::PangoLayout>,
}
/// Attempts to convert `xlfd` to a Fontconfig pattern. Returns `None` if this
/// cannot be accomplished (e.g., `xlfd` could not be parsed).
fn xlfd_to_fc_pattern(xlfd: &str) -> Option<NonNull<x11::xft::FcPattern>> {
if xlfd.contains('%') {
return NonNull::new(unsafe {
fontconfig_sys::FcNameParse(DEFAULT_FONT_CSTR.as_ptr()).cast::<x11::xft::FcPattern>()
});
}
let name = xlfd.split(',').next().unwrap();
let ignore_scalable = 0;
let complete = 0;
NonNull::new(unsafe {
x11::xft::XftXlfdParse(
name.as_ptr(),
ignore_scalable,
complete,
)
})
.or_else(|| {
// TODO: warn.
NonNull::new(unsafe {
x11::xft::XftXlfdParse(
DEFAULT_FONT_CSTR.as_ptr(),
ignore_scalable,
complete,
)
})
})
}
/// A font name, with support for X logical font descriptions (like
/// `-bitstream-charter-medium-r-normal--12-120-75-75-p-68-iso8859-1`, including
/// `*`-style wildcards but not old-style `%d` tokens) and Fontconfig
/// descriptions (like `sans serif:pixelsize=12`).
///
/// Data is held in a [`std::rc::Rc`], so copies are lightweight, and ownership
/// may be shared between multiple objects (e.g., keying a hashtable and
/// referenced by hashtable values).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct FontName(Rc<CStr>);
impl FontName {
/// Tries to interpret `name` as an XLFD or Fontconfig name. Returns an
/// instantiated `FontName` if this succeeds, or `None`.
pub fn new(name: &str) -> Option<Self> {
let name = if name.starts_with('-') {
let pattern = xlfd_to_fc_pattern(name)?;
let name = unsafe { fontconfig_sys::FcNameUnparse(pattern.as_ptr().cast::<c_void>()) };
let name = NonNull::new(name)?;
let result = unsafe { CStr::from_ptr(name.as_ptr()) }.to_str().ok()?;
unsafe {
libc::free(name.as_ptr().cast::<c_void>());
fontconfig_sys::FcPatternDestroy(pattern.as_ptr().cast::<c_void>());
}
result
} else {
name
};
if !Configuration::global()
.map(|c| c.antialiased_text)
.unwrap_or(false)
&& !name.contains(":antialias=")
{
let mut name = String::from(name);
name += ":antialias=false";
Some(FontName(Rc::from(CString::new(name).ok()?)))
} else {
Some(FontName(Rc::from(CString::new(name).ok()?)))
}
}
/// Returns a C-style string owned by this object that names the font in a
/// way that is suitable for `x11::xft::XftFontOpenName` and other Xft
/// functions.
pub fn as_xft_name(&self) -> *const u8 {
self.0.as_ptr()
}
/// Converts `self` into a shareable name.
pub fn into_name(self) -> Rc<CStr> {
self.0.into()
}
}
impl Font {
/// Tries to load the named font for `screen` on `display`. Returns `None`
/// if `name` can't be handled or font cannot be loaded.
pub fn load(display: &mut x11::xlib::Display, screen: c_int, name: &FontName) -> Option<Self> {
let font = NonNull::new(unsafe {
x11::xft::XftFontOpenName(display, screen, name.as_xft_name())
})?;
let f = unsafe { font.as_ref() };
let height = u32::try_from(f.ascent + f.descent).ok()?;
let fontmap = unsafe { pango_extras::pango_xft_get_font_map(display, screen) };
let context = unsafe { pango_sys::pango_font_map_create_context(fontmap) };
let layout = NonNull::new(unsafe { pango_sys::pango_layout_new(context) })?;
let pattern = unsafe { fontconfig_sys::FcNameParse(name.as_xft_name()) };
let description =
unsafe { pango_extras::pango_fc_font_description_from_pattern(pattern.cast(), 0) };
// Pango examines FC_SIZE but not FC_PIXEL_SIZE of the pattern, but
// font-name has only "pixelsize", so set the size manually here.
let mut size: c_double = 0.0;
unsafe {
if fontconfig_sys::FcPatternGetDouble(
pattern,
fontconfig_sys::constants::FC_PIXEL_SIZE.as_ptr(),
0,
&mut size,
) == fontconfig_sys::FcResultMatch
{
pango_sys::pango_font_description_set_absolute_size(
description,
size * (pango_sys::PANGO_SCALE as c_double),
);
}
pango_sys::pango_layout_set_font_description(layout.as_ptr(), description);
pango_sys::pango_font_description_free(description);
}
Some(Font {
font,
height,
ascent: u32::try_from(f.ascent).ok()?,
name: name.clone(),
layout,
})
}
/// Loads the font `name` at the size `size` (in pixels).
pub fn load_at_size(
display: &mut x11::xlib::Display,
screen: c_int,
name: &FontName,
size: u32,
) -> Option<Self> {
let pattern = unsafe { fontconfig_sys::FcNameParse(name.as_xft_name()) };
unsafe {
fontconfig_sys::FcPatternDel(
pattern,
fontconfig_sys::constants::FC_PIXEL_SIZE.as_ptr(),
);
fontconfig_sys::FcPatternAddDouble(
pattern,
fontconfig_sys::constants::FC_PIXEL_SIZE.as_ptr(),
size as f64,
);
}
let name = unsafe { fontconfig_sys::FcNameUnparse(pattern) };
unsafe {
fontconfig_sys::FcPatternDestroy(pattern);
}
let name = NonNull::new(name)?;
let font_name = FontName(Rc::from(unsafe { CStr::from_ptr(name.as_ptr()) }));
unsafe {
libc::free(name.as_ptr().cast::<c_void>());
}
Font::load(display, screen, &font_name)
}
pub fn name(&self) -> &FontName {
&self.name
}
}
impl Drop for Font {
fn drop(&mut self) {
unsafe {
libc::free(self.font.as_ptr().cast::<c_void>());
}
}
}
pub mod ffi {
use super::{Font, FontName};
use std::ffi::CStr;
use crate::{WINGsP, configuration::Configuration, pango_extras};
use std::{
ffi::{c_char, c_int, c_uint},
ptr,
rc::Rc,
};
pub type WMFont = Rc<Font>;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateFont(
screen: *mut WINGsP::W_Screen,
font_name: *const c_char,
) -> *mut WMFont {
if screen.is_null() || font_name.is_null() {
return ptr::null_mut();
}
let screen = unsafe { &mut *screen };
if screen.display.is_null() {
return ptr::null_mut();
}
let font_name = unsafe { CStr::from_ptr(font_name) };
let Ok(font_name) = font_name.to_str() else {
return ptr::null_mut();
};
let Some(font_name) = FontName::new(font_name) else {
return ptr::null_mut();
};
let display = unsafe { &mut *screen.display };
let screen_n = screen.screen;
screen
.font_cache_get_or_else(font_name, |font_name| {
Font::load(display, screen_n, font_name)
})
.map(|font| Box::leak(Box::new(font)) as *mut WMFont)
.unwrap_or(ptr::null_mut())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRetainFont(font: *mut WMFont) -> *mut WMFont {
if font.is_null() {
return ptr::null_mut();
}
let font = unsafe { (*font).clone() };
Box::leak(Box::new(font))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMReleaseFont(font: *mut WMFont) {
if font.is_null() {
return;
}
let _ = unsafe { Box::from_raw(font) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFontName(font: *const WMFont) -> *const c_char {
if font.is_null() {
return ptr::null_mut();
}
unsafe { (**font).name.as_xft_name() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFontAscent(font: *mut WMFont) -> c_uint {
if font.is_null() {
return 0;
}
return unsafe { (**font).ascent };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFontXftFont(font: *mut WMFont) -> *mut x11::xft::XftFont {
if font.is_null() {
return ptr::null_mut();
}
return unsafe { (**font).font.as_ptr() };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFontHeight(font: *const WMFont) -> c_uint {
if font.is_null() {
return 0;
}
unsafe { (**font).height }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetScaleBaseFromSystemFont(
screen: *mut WINGsP::W_Screen,
alphabet_width: *mut c_int,
font_height: *mut c_int,
) {
let font = unsafe { WMDefaultSystemFont(screen) };
unsafe {
*alphabet_width = WMWidthOfString(font, c"abcdefghijklmnopqrstuvwxyz".as_ptr(), 26);
}
if let Ok(x) = unsafe { (**font).height.try_into() } {
unsafe {
*font_height = x;
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDefaultSystemFont(screen: *mut WINGsP::W_Screen) -> *mut WMFont {
if screen.is_null() {
return ptr::null_mut();
}
unsafe { WMRetainFont((*screen).normalFont) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDefaultBoldSystemFont(screen: *mut WINGsP::W_Screen) -> *mut WMFont {
if screen.is_null() {
return ptr::null_mut();
}
unsafe { WMRetainFont((*screen).boldFont) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSystemFontOfSize(
screen: *mut WINGsP::W_Screen,
size: c_int,
) -> *mut WMFont {
if screen.is_null() {
return ptr::null_mut();
}
let screen = unsafe { &*screen };
if screen.display.is_null() {
return ptr::null_mut();
}
if screen.display.is_null() {
return ptr::null_mut();
}
let display = unsafe { &mut *screen.display };
let size = if size > 0 {
size as u32
} else {
match Configuration::global().map(|c| c.default_font_size as u32) {
Some(x) => x,
None => return ptr::null_mut(),
}
};
let Some(font_name) = Configuration::global()
.and_then(|c| c.system_font.to_str().ok())
.and_then(|name| FontName::new(name))
else {
return ptr::null_mut();
};
if let Some(font) = Font::load_at_size(display, screen.screen, &font_name, size) {
Box::leak(Box::new(Rc::new(font)))
} else {
// TODO: warn.
ptr::null_mut()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBoldSystemFontOfSize(
screen: *mut WINGsP::W_Screen,
size: c_int,
) -> *mut WMFont {
if screen.is_null() {
return ptr::null_mut();
}
let screen = unsafe { &*screen };
if screen.display.is_null() {
return ptr::null_mut();
}
if screen.display.is_null() {
return ptr::null_mut();
}
let display = unsafe { &mut *screen.display };
let size = if size > 0 {
size as u32
} else {
match Configuration::global().map(|c| c.default_font_size as u32) {
Some(x) => x,
None => return ptr::null_mut(),
}
};
let Some(font_name) = Configuration::global()
.and_then(|c| c.bold_system_font.to_str().ok())
.and_then(|name| FontName::new(name))
else {
return ptr::null_mut();
};
if let Some(font) = Font::load_at_size(display, screen.screen, &font_name, size) {
Box::leak(Box::new(Rc::new(font)))
} else {
// TODO: warn.
ptr::null_mut()
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMWidthOfString(
font: *mut WMFont,
text: *const c_char,
length: c_int,
) -> c_int {
if font.is_null() || text.is_null() || length <= 0 {
return 0;
}
let font = unsafe { &*font };
let layout = font.layout.as_ptr();
let previous_text = unsafe { pango_sys::pango_layout_get_text(layout) };
if previous_text.is_null() {
unsafe {
pango_sys::pango_layout_set_text(layout, text, length);
}
} else {
let length = length as usize;
let previous_text = unsafe { CStr::from_ptr(previous_text).to_bytes() };
let text = unsafe { std::slice::from_raw_parts(text, length) };
if previous_text.len() < length || previous_text[0..length] != text[0..length] {
unsafe {
pango_sys::pango_layout_set_text(layout, text.as_ptr(), length as i32);
}
}
}
let mut width = 0;
unsafe {
pango_sys::pango_layout_get_pixel_size(layout, &mut width, ptr::null_mut());
}
width
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDrawString(
screen: *mut WINGsP::W_Screen,
d: x11::xlib::Drawable,
color: *mut WINGsP::W_Color,
font: *mut WMFont,
x: c_int,
y: c_int,
text: *const c_char,
length: c_int,
) {
if screen.is_null() || color.is_null() || font.is_null() || text.is_null() {
return;
}
let screen = unsafe { &*screen };
let color = unsafe { &*color };
let layout = unsafe { &mut *(**font).layout.as_ptr() };
let mut xftcolor = x11::xft::XftColor {
color: x11::xrender::XRenderColor {
red: color.color.red,
green: color.color.green,
blue: color.color.blue,
alpha: color.alpha,
},
pixel: color.color.pixel,
};
let previous_text = unsafe { pango_sys::pango_layout_get_text(layout) };
if previous_text.is_null() {
unsafe {
pango_sys::pango_layout_set_text(layout, text, length);
}
} else {
let text = unsafe { CStr::from_ptr(text) };
let previous_text = unsafe { CStr::from_ptr(previous_text) };
if previous_text != text {
unsafe {
pango_sys::pango_layout_set_text(layout, text.as_ptr(), length);
}
}
}
unsafe {
x11::xft::XftDrawChange(screen.xftdraw.cast(), d);
pango_extras::pango_xft_render_layout(
screen.xftdraw.cast(),
&mut xftcolor,
layout,
x * pango_sys::PANGO_SCALE,
y * pango_sys::PANGO_SCALE,
);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMDrawImageString(
screen: *mut WINGsP::W_Screen,
d: x11::xlib::Drawable,
color: *mut WINGsP::W_Color,
background: *mut WINGsP::W_Color,
font: *mut WMFont,
x: c_int,
y: c_int,
text: *const c_char,
length: c_int,
) {
if screen.is_null() || color.is_null() || font.is_null() || text.is_null() {
return;
}
let screen = unsafe { &*screen };
let color = unsafe { &*color };
let background = unsafe { &*background };
let layout = unsafe { &mut *(**font).layout.as_ptr() };
let mut text_color = x11::xft::XftColor {
color: x11::xrender::XRenderColor {
red: color.color.red,
green: color.color.green,
blue: color.color.blue,
alpha: color.alpha,
},
pixel: color.color.pixel,
};
let background = x11::xft::XftColor {
color: x11::xrender::XRenderColor {
red: background.color.red,
green: background.color.green,
blue: background.color.blue,
alpha: background.alpha,
},
pixel: color.color.pixel,
};
unsafe {
let Ok(width) = u32::try_from(WMWidthOfString(font, text, length)) else {
// TODO: complain.
return;
};
x11::xft::XftDrawChange(screen.xftdraw.cast(), d);
x11::xft::XftDrawRect(
screen.xftdraw.cast(),
&background,
x,
y,
width,
(**font).height,
);
}
let previous_text = unsafe { pango_sys::pango_layout_get_text(layout) };
if previous_text.is_null() {
unsafe {
pango_sys::pango_layout_set_text(layout, text, length);
}
} else {
let text = unsafe { CStr::from_ptr(text) };
let previous_text = unsafe { CStr::from_ptr(previous_text) };
if text != previous_text {
unsafe {
pango_sys::pango_layout_set_text(layout, text.as_ptr(), length);
}
}
}
unsafe {
pango_extras::pango_xft_render_layout(
screen.xftdraw.cast(),
&mut text_color,
layout,
x * pango_sys::PANGO_SCALE,
y * pango_sys::PANGO_SCALE,
);
}
}
}

View File

@@ -0,0 +1,8 @@
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]
pub mod WINGsP;
pub mod configuration;
pub mod font;
pub(crate) mod pango_extras;
pub mod screen;

View File

@@ -0,0 +1,21 @@
use std::ffi::c_int;
unsafe extern "C" {
pub fn pango_xft_get_font_map(
display: *mut x11::xlib::Display,
screen: c_int,
) -> *mut pango_sys::PangoFontMap;
pub fn pango_fc_font_description_from_pattern(
pattern: *mut x11::xft::FcPattern,
include_size: c_int,
) -> *mut pango_sys::PangoFontDescription;
pub fn pango_xft_render_layout(
draw: *mut x11::xft::XftDraw,
color: *mut x11::xft::XftColor,
layout: *mut pango_sys::PangoLayout,
x: c_int,
y: c_int,
);
}

View File

@@ -0,0 +1,63 @@
use crate::{
WINGsP::W_Screen,
font::{Font, FontName},
};
use std::{
collections::{HashMap, hash_map::Entry},
ffi::c_void,
rc::Rc,
};
impl W_Screen {
fn font_cache_mut(&mut self) -> &mut HashMap<FontName, Rc<Font>> {
if self.fontCache.is_null() {
self.fontCache = (Box::leak(Box::new(HashMap::<FontName, Rc<Font>>::new()))
as *mut HashMap<_, _>)
.cast::<c_void>();
}
unsafe { &mut *self.fontCache.cast::<HashMap<FontName, Rc<Font>>>() }
}
pub fn font_cache_get_or_else(
&mut self,
name: FontName,
f: impl FnOnce(&FontName) -> Option<Font>,
) -> Option<Rc<Font>> {
match self.font_cache_mut().entry(name) {
Entry::Occupied(o) => Some(o.get().clone()),
Entry::Vacant(v) => {
if let Some(font) = f(v.key()) {
let font = Rc::new(font);
v.insert(font.clone());
Some(font)
} else {
None
}
}
}
}
/// Removes `font` from the font cache if it is not in use anywhere else
/// (i.e., the only live references are `font` and an entry in the cache).
pub fn font_cache_flush(&mut self, font: &Rc<Font>) {
if Rc::strong_count(font) > 2 {
return;
}
self.font_cache_mut().remove(&font.name());
}
}
#[cfg(test)]
mod test {
use crate::WINGsP::W_Screen;
use std::mem::MaybeUninit;
#[test]
fn font_cache_init() {
let mut screen: W_Screen = unsafe { MaybeUninit::zeroed().assume_init() };
let cache = screen.font_cache_mut();
assert!(cache.is_empty());
}
}

View File

@@ -109,7 +109,7 @@ void W_CreateIC(WMView * view)
// this really needs to be changed, but I don't know how yet -Dan
// it used to be like this with fontsets, but no longer applies to xft
preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot,
XNArea, &rect, XNFontInfo, scr->normalFont->font, NULL);
XNArea, &rect, XNFontInfo, WMFontXftFont(scr->normalFont), NULL);
}
view->xic = XCreateIC(scr->imctx->xim, XNInputStyle, scr->imctx->ximstyle,

View File

@@ -201,7 +201,7 @@ W_PaintText(W_View * view, Drawable d, WMFont * font, int x, int y,
}
void
W_PaintTextAndImage(W_View * view, int wrap, WMColor * textColor, W_Font * font,
W_PaintTextAndImage(W_View * view, int wrap, WMColor * textColor, WMFont * font,
WMReliefType relief, const char *text,
WMAlignment alignment, W_Pixmap * image,
WMImagePosition position, WMColor * backColor, int ofs)

View File

@@ -166,16 +166,6 @@ typedef struct W_Text {
WMArray *xdndDestinationTypes;
} Text;
/* not used */
#if 0
#define NOTIFY(T,C,N,A) {\
WMNotification *notif = WMCreateNotification(N,T,A);\
if ((T)->delegate && (T)->delegate->C)\
(*(T)->delegate->C)((T)->delegate,notif);\
WMPostNotification(notif);\
WMReleaseNotification(notif);}
#endif
#define TYPETEXT 0
#if 0
@@ -728,8 +718,8 @@ static void paintText(Text * tPtr)
if (!tPtr->flags.monoFont && tb->underlined) {
XDrawLine(dpy, tPtr->db, WMColorGC(color),
tb->sections[s].x - tPtr->hpos,
y + font->y + 1,
tb->sections[s].x + tb->sections[s].w - tPtr->hpos, y + font->y + 1);
y + WMFontAscent(font) + 1,
tb->sections[s].x + tb->sections[s].w - tPtr->hpos, y + WMFontAscent(font) + 1);
}
}
tb = (!done ? tb->next : NULL);
@@ -907,7 +897,7 @@ static void updateCursorPosition(Text * tPtr)
if (!(tb = tPtr->firstTextBlock)) {
WMFont *font = tPtr->dFont;
tPtr->tpos = 0;
tPtr->cursor.h = font->height + abs(font->height - font->y);
tPtr->cursor.h = WMFontHeight(font) + abs(WMFontHeight(font) - WMFontAscent(font));
tPtr->cursor.y = 2;
tPtr->cursor.x = 2;
@@ -989,7 +979,7 @@ static void cursorToTextPosition(Text * tPtr, int x, int y)
if (!(tb = tPtr->firstTextBlock)) {
WMFont *font = tPtr->dFont;
tPtr->tpos = 0;
tPtr->cursor.h = font->height + abs(font->height - font->y);
tPtr->cursor.h = WMFontHeight(font) + abs(WMFontHeight(font) - WMFontAscent(font));
tPtr->cursor.y = 2;
tPtr->cursor.x = 2;
return;
@@ -1388,9 +1378,9 @@ static int layOutLine(Text * tPtr, myLineItems * items, int nitems, int x, int y
} else {
font = (tPtr->flags.monoFont) ? tPtr->dFont : tb->d.font;
/*max_d = WMAX(max_d, abs(font->height-font->y)); */
/*max_d = WMAX(max_d, abs(WMFontHeight(font)-WMFontAscent(font))); */
max_d = 2;
line_height = WMAX(line_height, font->height + max_d);
line_height = WMAX(line_height, WMFontHeight(font) + max_d);
text = &(tb->text[items[i].begin]);
len = items[i].end - items[i].begin;
if (tPtr->flags.alignment != WALeft)
@@ -1440,7 +1430,7 @@ static int layOutLine(Text * tPtr, myLineItems * items, int nitems, int x, int y
len = items[i].end - items[i].begin;
text = &(tb->text[items[i].begin]);
tb->sections[n].y = y + line_height - font->y;
tb->sections[n].y = y + line_height - WMFontAscent(font);
tb->sections[n].w =
WMWidthOfString(font,
&(tb->text[tb->sections[n].begin]),

View File

@@ -68,12 +68,6 @@ typedef struct W_TextField {
} flags;
} TextField;
#define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
if ((T)->delegate && (T)->delegate->C)\
(*(T)->delegate->C)((T)->delegate,notif);\
WMPostNotification(notif);\
WMReleaseNotification(notif);}
#define MIN_TEXT_BUFFER 2
#define TEXT_BUFFER_INCR 8
@@ -404,7 +398,7 @@ void WMInsertTextFieldText(WMTextField * tPtr, const char *text, int position)
if (position < 0 || position >= tPtr->textLen) {
/* append the text at the end */
wstrlcat(tPtr->text, text, tPtr->bufferSize);
strlcat(tPtr->text, text, tPtr->bufferSize);
tPtr->textLen += len;
tPtr->cursorPosition += len;
incrToFit(tPtr);
@@ -473,7 +467,7 @@ void WMSetTextFieldText(WMTextField * tPtr, const char *text)
tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
}
wstrlcpy(tPtr->text, text, tPtr->bufferSize);
strlcpy(tPtr->text, text, tPtr->bufferSize);
}
tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
@@ -906,7 +900,10 @@ static void handleEvents(XEvent * event, void *data)
paintTextField(tPtr);
NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
if (tPtr->delegate && tPtr->delegate->didBeginEditing) {
(*tPtr->delegate->didBeginEditing)(tPtr->delegate, 0);
}
WMPostNotificationName(WMTextDidBeginEditingNotification, tPtr, NULL);
tPtr->flags.notIllegalMovement = 0;
break;
@@ -921,8 +918,10 @@ static void handleEvents(XEvent * event, void *data)
paintTextField(tPtr);
if (!tPtr->flags.notIllegalMovement) {
NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
(void *)WMIllegalTextMovement);
if (tPtr->delegate && tPtr->delegate->didEndEditing) {
(*tPtr->delegate->didEndEditing)(tPtr->delegate, WMIllegalTextMovement);
}
WMPostNotificationName(WMTextDidEndEditingNotification, tPtr, (void *)WMIllegalTextMovement);
}
break;
@@ -943,7 +942,8 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
char buffer[64];
KeySym ksym;
const char *textEvent = NULL;
void *data = NULL;
WMTextMovementType movement_type;
WMTextFieldSpecialEventType special_field_event_type;
int count, refresh = 0;
int control_pressed = 0;
int cancelSelection = 1;
@@ -975,14 +975,14 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
tPtr->view->prevFocusChain);
tPtr->flags.notIllegalMovement = 1;
}
data = (void *)WMBacktabTextMovement;
movement_type = WMBacktabTextMovement;
} else {
if (tPtr->view->nextFocusChain) {
W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
tPtr->view->nextFocusChain);
tPtr->flags.notIllegalMovement = 1;
}
data = (void *)WMTabTextMovement;
movement_type = WMTabTextMovement;
}
textEvent = WMTextDidEndEditingNotification;
@@ -994,7 +994,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
case XK_Escape:
if (!modified) {
data = (void *)WMEscapeTextMovement;
movement_type = WMEscapeTextMovement;
textEvent = WMTextDidEndEditingNotification;
relay = False;
@@ -1008,7 +1008,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
/* FALLTHRU */
case XK_Return:
if (!modified) {
data = (void *)WMReturnTextMovement;
movement_type = WMReturnTextMovement;
textEvent = WMTextDidEndEditingNotification;
relay = False;
@@ -1165,7 +1165,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
if (!modified) {
if (tPtr->selection.count) {
WMDeleteTextFieldRange(tPtr, tPtr->selection);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
} else if (tPtr->cursorPosition > 0) {
int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
@@ -1174,7 +1174,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
range.position = tPtr->cursorPosition + i;
range.count = -i;
WMDeleteTextFieldRange(tPtr, range);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
}
@@ -1197,7 +1197,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
if (!modified) {
if (tPtr->selection.count) {
WMDeleteTextFieldRange(tPtr, tPtr->selection);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
} else if (tPtr->cursorPosition < tPtr->textLen) {
WMRange range;
@@ -1205,7 +1205,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
tPtr->textLen - tPtr->cursorPosition);
WMDeleteTextFieldRange(tPtr, range);
data = (void *)WMDeleteTextEvent;
special_field_event_type = WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
}
@@ -1220,7 +1220,7 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
if (tPtr->selection.count)
WMDeleteTextFieldRange(tPtr, tPtr->selection);
WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
data = (void *)WMInsertTextEvent;
special_field_event_type = WMInsertTextEvent;
textEvent = WMTextDidChangeNotification;
relay = False;
@@ -1255,21 +1255,22 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
/*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
if (textEvent) {
WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
if (tPtr->delegate) {
if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
(*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
(*tPtr->delegate->didBeginEditing) (tPtr->delegate, movement_type);
else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
(*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
(*tPtr->delegate->didEndEditing) (tPtr->delegate, movement_type);
else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
(*tPtr->delegate->didChange) (tPtr->delegate, notif);
(*tPtr->delegate->didChange) (tPtr->delegate, special_field_event_type);
}
WMPostNotification(notif);
WMReleaseNotification(notif);
if (textEvent == WMTextDidBeginEditingNotification || textEvent == WMTextDidEndEditingNotification) {
WMPostNotificationName(textEvent, tPtr, (void *)movement_type);
} else if (textEvent == WMTextDidChangeNotification) {
WMPostNotificationName(textEvent, tPtr, (void *)special_field_event_type);
}
}
if (refresh)
@@ -1345,7 +1346,10 @@ static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp
str = (char *)WMDataBytes(data);
WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
if (tPtr->delegate && tPtr->delegate->didChange) {
(*tPtr->delegate->didChange)(tPtr->delegate, WMInsertTextEvent);
}
WMPostNotificationName(WMTextDidChangeNotification, tPtr, (void *)WMInsertTextEvent);
} else {
int n;
@@ -1355,7 +1359,10 @@ static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp
str[n] = 0;
WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
XFree(str);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
if (tPtr->delegate && tPtr->delegate->didChange) {
(*tPtr->delegate->didChange)(tPtr->delegate, WMInsertTextEvent);
}
WMPostNotificationName(WMTextDidChangeNotification, tPtr, (void *)WMInsertTextEvent);
}
}
}
@@ -1477,8 +1484,10 @@ static void handleTextFieldActionEvents(XEvent * event, void *data)
text[n] = 0;
WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
XFree(text);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void *)WMInsertTextEvent);
if (tPtr->delegate && tPtr->delegate->didChange) {
(*tPtr->delegate->didChange)(tPtr->delegate, WMInsertTextEvent);
}
WMPostNotificationName(WMTextDidChangeNotification, tPtr, (void *)WMInsertTextEvent);
}
} else {
tPtr->flags.waitingSelection = 1;

View File

@@ -129,7 +129,7 @@ static void autoDelayChanged(void *observerData, WMNotification *notification)
}
char *value = WMGetTextFieldText(anAutoDelayT);
adjustButtonSelectionBasedOnValue(panel, row, value);
free(value);
wfree(value);
return;
}
}

View File

@@ -138,7 +138,7 @@ static void storeData(_Panel * panel)
if (sscanf(str, "%i", &i) != 1)
i = 0;
SetIntegerForKey(i, "RaiseDelay");
free(str);
wfree(str);
SetBoolForKey(WMGetButtonSelected(panel->ignB), "IgnoreFocusClick");
SetBoolForKey(WMGetButtonSelected(panel->newB), "AutoFocus");

View File

@@ -147,7 +147,7 @@ static void storeData(_Panel * panel)
if (sscanf(str, "%i", &i) != 1)
i = 0;
SetIntegerForKey(i, "HotCornerDelay");
free(str);
wfree(str);
SetIntegerForKey(WMGetSliderValue(panel->hceS), "HotCornerEdge");

View File

@@ -365,7 +365,7 @@ char *capture_shortcut(Display *dpy, Bool *capturing, Bool convert_case)
if ((numlock_mask != Mod5Mask) && (ev.xkey.state & Mod5Mask))
strcat(buffer, "Mod5+");
wstrlcat(buffer, key, sizeof(buffer));
strlcat(buffer, key, sizeof(buffer));
return wstrdup(buffer);
}

View File

@@ -67,10 +67,11 @@ WPrefs_DEPENDENCIES = $(top_builddir)/WINGs/libWINGs.la
WPrefs_LDADD = \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a\
$(top_builddir)/wings-rs/target/debug/libwings_rs.la\
$(top_builddir)/WINGs/libWINGs.la\
$(top_builddir)/WINGs/wings-rs/target/debug/libwings_rs.a\
$(top_builddir)/WINGs/libWUtil.la\
$(top_builddir)/wrlib/libwraster.la \
@PANGO_LIBS@ \
@XLFLAGS@ @XLIBS@ \
@LIBM@ \
@FCLIBS@ \

View File

@@ -94,7 +94,7 @@ static void paintDoubleTest(_DoubleTest * dPtr)
if (dPtr->text) {
int y;
y = (dPtr->view->size.height - scr->normalFont->height) / 2;
y = (dPtr->view->size.height - WMFontHeight(scr->normalFont)) / 2;
W_PaintText(dPtr->view, dPtr->view->window, scr->normalFont,
dPtr->on, dPtr->on + y, dPtr->view->size.width, WACenter,
scr->black, False, dPtr->text, strlen(dPtr->text));

View File

@@ -824,18 +824,15 @@ static void stopEditItem(WEditMenu * menu, Bool apply)
menu->flags.isEditing = 0;
}
static void textEndedEditing(struct WMTextFieldDelegate *self, WMNotification * notif)
static void textEndedEditing(struct WMTextFieldDelegate *self, WMTextMovementType reason)
{
WEditMenu *menu = (WEditMenu *) self->data;
uintptr_t reason;
int i;
WEditMenuItem *item;
if (!menu->flags.isEditing)
return;
reason = (uintptr_t)WMGetNotificationClientData(notif);
switch (reason) {
case WMEscapeTextMovement:
stopEditItem(menu, False);

View File

@@ -60,6 +60,11 @@ AS_IF(test x$RUSTC = xno,
AC_MSG_ERROR([rustc is required. Please set the RUSTC environment variable or install the Rust toolchain from https://www.rust-lang.org/])
)
AC_SUBST(RUSTC, [rustc])
AC_CHECK_PROG(BINDGEN, [bindgen], [yes], [no])
AS_IF(test x$BINDGEN = xno,
AC_MSG_ERROR([bindgen is required. Please set the BINDGEN environment variable or install bindgen (maybe with `cargo install bindgen-cli`)])
)
AC_SUBST(BINDGEN, [bindgen])
dnl libtool library versioning
dnl ==========================
@@ -391,39 +396,6 @@ dnl the flag 'O_NOFOLLOW' for 'open' is used in WINGs
WM_FUNC_OPEN_NOFOLLOW
dnl Check for strlcat/strlcpy
dnl =========================
m4_divert_push([INIT_PREPARE])dnl
AC_ARG_WITH([libbsd],
[AS_HELP_STRING([--without-libbsd], [do not use libbsd for strlcat and strlcpy [default=check]])],
[AS_IF([test "x$with_libbsd" != "xno"],
[with_libbsd=bsd],
[with_libbsd=]
)],
[with_libbsd=bsd])
m4_divert_pop([INIT_PREPARE])dnl
tmp_libs=$LIBS
AC_SEARCH_LIBS([strlcat],[$with_libbsd],
[AC_DEFINE(HAVE_STRLCAT, 1, [Define if strlcat is available])],
[],
[]
)
AC_SEARCH_LIBS([strlcpy],[$with_libbsd],
[AC_DEFINE(HAVE_STRLCAT, 1, [Define if strlcpy is available])],
[],
[]
)
LIBS=$tmp_libs
LIBBSD=
AS_IF([test "x$ac_cv_search_strlcat" = "x-lbsd" -o "x$ac_cv_search_strlcpy" = "x-lbsd"],
[LIBBSD=-lbsd
AC_CHECK_HEADERS([bsd/string.h])]
)
AC_SUBST(LIBBSD)
dnl Check for OpenBSD kernel memory interface - kvm(3)
dnl ==================================================
AS_IF([test "x$WM_OSDEP" = "xbsd"],
@@ -957,11 +929,11 @@ AC_CONFIG_FILES(
wrlib/Makefile wrlib/po/Makefile
wrlib/tests/Makefile
dnl Rust implementation of WINGs libraries
dnl Rust implementation of WINGs utilities
wutil-rs/Makefile
dnl WINGs toolkit
WINGs/Makefile WINGs/WINGs/Makefile WINGs/po/Makefile
WINGs/Makefile WINGs/wings-rs/Makefile WINGs/WINGs/Makefile WINGs/po/Makefile
WINGs/Documentation/Makefile WINGs/Resources/Makefile WINGs/Extras/Makefile
WINGs/Examples/Makefile WINGs/Tests/Makefile

View File

@@ -161,7 +161,10 @@ wmaker_LDADD = \
$(top_builddir)/WINGs/libWUtil.la\
$(top_builddir)/wrlib/libwraster.la\
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a\
$(top_builddir)/WINGs/wings-rs/target/debug/libwings_rs.a\
$(top_builddir)/wmaker-rs/target/debug/libwmaker_rs.a\
@PANGO_LIBS@ \
@FCLIBS@ \
@XLFLAGS@ \
@LIBXRANDR@ \
@LIBXINERAMA@ \

View File

@@ -94,7 +94,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
wwarning(_("appmenu: bad menu entry \"%s\" in window %lx"), slist[*index], win);
return NULL;
}
if (wstrlcpy(title, &slist[*index][pos], sizeof(title)) >= sizeof(title)) {
if (strlcpy(title, &slist[*index][pos], sizeof(title)) >= sizeof(title)) {
wwarning(_("appmenu: menu command size exceeded in window %lx"), win);
return NULL;
}
@@ -127,7 +127,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
slist[*index], win);
return NULL;
}
wstrlcpy(title, &slist[*index][pos], sizeof(title));
strlcpy(title, &slist[*index][pos], sizeof(title));
rtext[0] = 0;
} else {
if (sscanf(slist[*index], "%i %i %i %i %s %n",
@@ -137,7 +137,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
slist[*index], win);
return NULL;
}
wstrlcpy(title, &slist[*index][pos], sizeof(title));
strlcpy(title, &slist[*index][pos], sizeof(title));
}
data = wmalloc(sizeof(WAppMenuData));
if (data == NULL) {
@@ -174,7 +174,7 @@ static WMenu *parseMenuCommand(WScreen * scr, Window win, char **slist, int coun
return NULL;
}
wstrlcpy(title, &slist[*index][pos], sizeof(title));
strlcpy(title, &slist[*index][pos], sizeof(title));
*index += 1;
submenu = parseMenuCommand(scr, win, slist, count, index);

View File

@@ -2205,7 +2205,7 @@ static int getKeybind(WScreen * scr, WDefaultEntry * entry, WMPropList * value,
return True;
}
wstrlcpy(buf, val, MAX_SHORTCUT_LENGTH);
strlcpy(buf, val, MAX_SHORTCUT_LENGTH);
b = (char *)buf;

View File

@@ -39,10 +39,6 @@
#include <time.h>
#include <sys/utsname.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <signal.h>
#ifdef __FreeBSD__
#include <sys/signal.h>
@@ -609,9 +605,9 @@ static void listPixmaps(WScreen *scr, WMList *lPtr, const char *path)
if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
continue;
if (wstrlcpy(pbuf, apath, sizeof(pbuf)) >= sizeof(pbuf) ||
wstrlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf) ||
wstrlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf)) {
if (strlcpy(pbuf, apath, sizeof(pbuf)) >= sizeof(pbuf) ||
strlcat(pbuf, "/", sizeof(pbuf)) >= sizeof(pbuf) ||
strlcat(pbuf, dentry->d_name, sizeof(pbuf)) >= sizeof(pbuf)) {
wwarning(_("full path for file \"%s\" in \"%s\" is longer than %d bytes, skipped"),
dentry->d_name, path, (int) (sizeof(pbuf) - 1) );
continue;
@@ -1361,7 +1357,7 @@ void wShowInfoPanel(WScreen *scr)
char *posn = getPrettyOSName();
if (posn) {
snprintf(buffer, sizeof(buffer), _("Running on: %s (%s)\n"), posn, uts.machine);
free(posn);
wfree(posn);
}
else
snprintf(buffer, sizeof(buffer), _("Running on: %s (%s)\n"), uts.sysname, uts.machine);
@@ -1393,31 +1389,6 @@ void wShowInfoPanel(WScreen *scr)
break;
}
#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO2)
{
struct mallinfo2 ma = mallinfo2();
snprintf(buffer, sizeof(buffer),
#ifdef DEBUG
_("Total memory allocated: %lu kB (in use: %lu kB, %lu free chunks)\n"),
#else
_("Total memory allocated: %lu kB (in use: %lu kB)\n"),
#endif
(ma.arena + ma.hblkhd) / 1024,
(ma.uordblks + ma.hblkhd) / 1024
#ifdef DEBUG
/*
* This information is representative of the memory
* fragmentation. In ideal case it should be 1, but
* that is never possible
*/
, ma.ordblks
#endif
);
strbuf = wstrappend(strbuf, buffer);
}
#endif
strbuf = wstrappend(strbuf, _("Image formats: "));
strl = RSupportedFileFormats();
separator = NULL;

View File

@@ -3196,7 +3196,7 @@ static pid_t execCommand(WAppIcon *btn, const char *command, WSavedState *state)
setsid();
#endif
args = malloc(sizeof(char *) * (argc + 1));
args = wmalloc(sizeof(char *) * (argc + 1));
if (!args)
exit(111);
@@ -3340,8 +3340,8 @@ void wDockTrackWindowLaunch(WDock *dock, Window window)
char *command = NULL;
if (!PropGetWMClass(window, &wm_class, &wm_instance)) {
free(wm_class);
free(wm_instance);
wfree(wm_class);
wfree(wm_instance);
return;
}
@@ -3421,10 +3421,8 @@ void wDockTrackWindowLaunch(WDock *dock, Window window)
if (command)
wfree(command);
if (wm_class)
free(wm_class);
if (wm_instance)
free(wm_instance);
wfree(wm_class);
wfree(wm_instance);
}
void wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)

View File

@@ -141,7 +141,7 @@ WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata)
{
DeathHandler *handler;
handler = malloc(sizeof(DeathHandler));
handler = wmalloc(sizeof(DeathHandler));
if (!handler)
return 0;
@@ -150,7 +150,7 @@ WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata)
handler->client_data = cdata;
if (!deathHandlers)
deathHandlers = WMCreateArrayWithDestructor(8, free);
deathHandlers = WMCreateArrayWithDestructor(8, wfree);
WMAddToArray(deathHandlers, handler);
@@ -164,7 +164,7 @@ static void wdelete_death_handler(WMagicNumber id)
if (!handler || !deathHandlers)
return;
/* array destructor will call free(handler) */
/* array destructor will call wfree(handler) */
WMRemoveFromArray(deathHandlers, handler);
}

View File

@@ -40,7 +40,7 @@ WGeometryView *WCreateGeometryView(WMScreen * scr)
widgetClass = W_RegisterUserWidget();
}
gview = malloc(sizeof(WGeometryView));
gview = wmalloc(sizeof(WGeometryView));
if (!gview) {
return NULL;
}

View File

@@ -131,7 +131,7 @@ static void setWVisualID(int screen, int val)
/* no array at all, alloc space for screen + 1 entries
* and init with default value */
wVisualID_len = screen + 1;
wVisualID = (int *)malloc(wVisualID_len * sizeof(int));
wVisualID = (int *)wmalloc(wVisualID_len * sizeof(int));
for (i = 0; i < wVisualID_len; i++) {
wVisualID[i] = -1;
}
@@ -156,7 +156,7 @@ static void setWVisualID(int screen, int val)
*/
static int initWVisualID(const char *user_str)
{
char *mystr = strdup(user_str);
char *mystr = wstrdup(user_str);
int cur_in_pos = 0;
int cur_out_pos = 0;
int cur_screen = 0;
@@ -191,7 +191,7 @@ static int initWVisualID(const char *user_str)
cur_in_pos++;
}
free(mystr);
wfree(mystr);
if (cur_screen == 0||error_found != 0)
return 1;
@@ -383,7 +383,7 @@ Bool RelaunchWindow(WWindow *wwin)
setsid();
#endif
/* argv is not null-terminated */
char **a = (char **) malloc(argc + 1);
char **a = (char **) wmalloc(argc + 1);
if (! a) {
werror("out of memory trying to relaunch the application");
Exit(-1);

View File

@@ -54,13 +54,13 @@ int PropGetWMClass(Window window, char **wm_class, char **wm_instance)
class_hint = XAllocClassHint();
if (XGetClassHint(dpy, window, class_hint) == 0) {
*wm_class = strdup("default");
*wm_instance = strdup("default");
*wm_class = wstrdup("default");
*wm_instance = wstrdup("default");
XFree(class_hint);
return False;
}
*wm_instance = strdup(class_hint->res_name);
*wm_class = strdup(class_hint->res_class);
*wm_instance = wstrdup(class_hint->res_name);
*wm_class = wstrdup(class_hint->res_class);
XFree(class_hint->res_name);
XFree(class_hint->res_class);

View File

@@ -406,7 +406,7 @@ static Bool addShortcut(const char *file, const char *shortcutDefinition, WMenu
ptr = wmalloc(sizeof(Shortcut));
wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
strlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
b = (char *)buf;
/* get modifiers */
@@ -1243,7 +1243,7 @@ static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, co
if (dentry->d_name[0] == '.')
continue;
buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
buffer = wmalloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
if (!buffer) {
werror(_("out of memory while constructing directory menu %s"), path[i]);
break;
@@ -1288,7 +1288,7 @@ static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, co
}
}
}
free(buffer);
wfree(buffer);
}
closedir(dir);
@@ -1315,7 +1315,7 @@ static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, co
length += 7;
if (command)
length += strlen(command) + 6;
buffer = malloc(length);
buffer = wmalloc(length);
if (!buffer) {
werror(_("out of memory while constructing directory menu %s"), path[data->index]);
break;
@@ -1353,7 +1353,7 @@ static WMenu *readMenuDirectory(WScreen *scr, const char *title, char **path, co
if (command)
length += strlen(command);
buffer = malloc(length);
buffer = wmalloc(length);
if (!buffer) {
werror(_("out of memory while constructing directory menu %s"), path[data->index]);
break;

View File

@@ -290,9 +290,9 @@ static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp)
}
if (instance)
free(instance);
wfree(instance);
if (class)
free(class);
wfree(class);
if (command)
wfree(command);
@@ -383,7 +383,7 @@ static pid_t execCommand(WScreen *scr, char *command)
SetupEnvironment(scr);
args = malloc(sizeof(char *) * (argc + 1));
args = wmalloc(sizeof(char *) * (argc + 1));
if (!args)
exit(111);
for (i = 0; i < argc; i++) {

View File

@@ -109,16 +109,16 @@ void Shutdown(WShutdownMode mode)
}
}
static void restoreWindows(WMBag * bag, WMBagIterator iter)
static void restoreWindows(WMBag * bag, WMBagIterator *iter)
{
WCoreWindow *next;
WCoreWindow *core;
WWindow *wwin;
if (iter == NULL) {
core = WMBagFirst(bag, &iter);
if (*iter < 0) {
core = WMBagFirst(bag, iter);
} else {
core = WMBagNext(bag, &iter);
core = WMBagNext(bag, iter);
}
if (core == NULL)
@@ -168,7 +168,8 @@ void RestoreDesktop(WScreen * scr)
wDestroyInspectorPanels();
/* reparent windows back to the root window, keeping the stacking order */
restoreWindows(scr->stacking_list, NULL);
WMBagIterator iter = -1;
restoreWindows(scr->stacking_list, &iter);
XUngrabServer(dpy);
XSetInputFocus(dpy, PointerRoot, RevertToParent, CurrentTime);

View File

@@ -244,7 +244,7 @@ reinit:
wApplicationSetBouncing(data->wapp, 0);
WMDeleteTimerHandler(data->timer);
wApplicationDestroy(data->wapp);
free(data);
wfree(data);
}
static int bounceDirection(WAppIcon *aicon)
@@ -324,7 +324,7 @@ void wAppBounce(WApplication *wapp)
wApplicationIncrementRefcount(wapp);
wApplicationSetBouncing(wapp, 1);
AppBouncerData *data = (AppBouncerData *)malloc(sizeof(AppBouncerData));
AppBouncerData *data = (AppBouncerData *)wmalloc(sizeof(AppBouncerData));
data->wapp = wapp;
data->count = data->pow = 0;
data->dir = bounceDirection(wApplicationGetAppIcon(wapp));

View File

@@ -357,8 +357,7 @@ static void drawTitle(WSwitchPanel *panel, int idecks, const char *title)
WMSetLabelText(panel->label, ntitle);
}
if (ntitle)
free(ntitle);
wfree(ntitle);
}
static WMArray *makeWindowListArray(WScreen *scr, int include_unmapped, Bool class_only)

View File

@@ -283,10 +283,10 @@ void wWindowDestroy(WWindow *wwin)
XFree(wwin->wm_hints);
if (wwin->wm_instance)
XFree(wwin->wm_instance);
wfree(wwin->wm_instance);
if (wwin->wm_class)
XFree(wwin->wm_class);
wfree(wwin->wm_class);
if (wwin->wm_gnustep_attr)
wfree(wwin->wm_gnustep_attr);
@@ -950,10 +950,10 @@ WWindow *wManageWindow(WScreen *scr, Window window)
}
if (instance)
free(instance);
wfree(instance);
if (class)
free(class);
wfree(class);
#undef ADEQUATE
}
@@ -2602,7 +2602,7 @@ void wWindowSetShape(WWindow * wwin)
if (!rects)
goto alt_code;
urec = malloc(sizeof(XRectangle) * (count + 2));
urec = wmalloc(sizeof(XRectangle) * (count + 2));
if (!urec) {
XFree(rects);
goto alt_code;
@@ -2779,7 +2779,7 @@ WMagicNumber wWindowAddSavedState(const char *instance, const char *class,
{
WWindowState *wstate;
wstate = malloc(sizeof(WWindowState));
wstate = wmalloc(sizeof(WWindowState));
if (!wstate)
return NULL;
@@ -2841,9 +2841,9 @@ WMagicNumber wWindowGetSavedState(Window win)
if (command)
wfree(command);
if (instance)
free(instance);
wfree(instance);
if (class)
free(class);
wfree(class);
return wstate;
}

View File

@@ -797,7 +797,7 @@ void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
ws = menu->entry_no - MC_WORKSPACE1;
while (i > 0) {
wstrlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
strlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
entry = wMenuAddCallback(menu, title, switchWSCommand, (void *)ws);
entry->flags.indicator = 1;

View File

@@ -256,7 +256,7 @@ static void wXDNDGetTypeList(Display *dpy, Window window)
return;
}
typelist = malloc((count + 1) * sizeof(Atom));
typelist = wmalloc((count + 1) * sizeof(Atom));
a = (Atom *) data;
for (i = 0; i < count; i++) {
typelist[i] = a[i];
@@ -267,7 +267,7 @@ static void wXDNDGetTypeList(Display *dpy, Window window)
}
typelist[count] = 0;
XFree(data);
free(typelist);
wfree(typelist);
}
Bool wXDNDProcessClientMessage(XClientMessageEvent *event)

View File

@@ -77,10 +77,20 @@ if [ -n "$1" -a -x "$WindowMaker$1" ] ; then
shift
fi
Xephyr -screen 1080x760 :1 &
for i in $(seq 5 10) ; do
if [ "x$DISPLAY" != "x:$i" ] ; then
xephyr_display=":$i"
break
fi
done
echo "Running Xephyr on display $xephyr_display"
Xephyr -screen 640x480 "$xephyr_display" &
xephyr_pid=$!
DISPLAY=:1 gdb \
DISPLAY="$xephyr_display" gdb \
--directory "$project_base" \
--directory "$HOME/src/libX11-1.5.0/build/src" \
--quiet \
--args "$WindowMaker" -display :1 --for-real "$@"
--fullname \
--args "$WindowMaker" -display "$xephyr_display" --for-real "$@"
kill $xephyr_pid

View File

@@ -28,7 +28,8 @@ wdread_LDADD = \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(liblist)
wxcopy_LDADD = @XLFLAGS@ @XLIBS@
wxcopy_LDADD = @XLFLAGS@ @XLIBS@ \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a
wxpaste_LDADD = @XLFLAGS@ @XLIBS@
@@ -66,15 +67,17 @@ geticonset_LDADD= \
wmagnify_LDADD = \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/WINGs/wings-rs/target/debug/libwings_rs.a \
$(top_builddir)/wrlib/libwraster.la \
@XLFLAGS@ @XLIBS@ @INTLIBS@
@XLFLAGS@ @XLIBS@ @INTLIBS@ @FCLIBS@ @PANGO_LIBS@
wmsetbg_LDADD = \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(top_builddir)/WINGs/wings-rs/target/debug/libwings_rs.a \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wrlib/libwraster.la \
@XLFLAGS@ @LIBXINERAMA@ @XLIBS@ @INTLIBS@
@XLFLAGS@ @LIBXINERAMA@ @XLIBS@ @INTLIBS@ @FCLIBS@ @PANGO_LIBS@
wmgenmenu_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
@@ -84,8 +87,8 @@ wmgenmenu_LDADD = \
wmgenmenu_SOURCES = wmgenmenu.c wmgenmenu.h
wmmenugen_LDADD = \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(top_builddir)/WINGs/libWUtil.la \
@INTLIBS@
wmmenugen_SOURCES = wmmenugen.c wmmenugen.h wmmenugen_misc.c \
@@ -97,7 +100,9 @@ wmiv_CFLAGS = @PANGO_CFLAGS@ @PTHREAD_CFLAGS@
wmiv_LDADD = \
$(top_builddir)/wrlib/libwraster.la \
$(top_builddir)/WINGs/libWINGs.la \
$(top_builddir)/WINGs/libWUtil.la \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a \
$(top_builddir)/WINGs/wings-rs/target/debug/libwings_rs.a \
@XLFLAGS@ @XLIBS@ @GFXLIBS@ \
@PANGO_LIBS@ @PTHREAD_LIBS@ @LIBEXIF@

View File

@@ -242,7 +242,7 @@ static void makeThemePack(WMPropList * style, const char *themeName)
WMDeleteFromPLArray(value, 1);
WMInsertInPLArray(value, 1, WMCreatePLString(newPath));
free(newPath);
wfree(newPath);
} else {
findCopyFile(themeDir, WMGetFromPLString(file));
}
@@ -262,7 +262,7 @@ static void makeThemePack(WMPropList * style, const char *themeName)
WMDeleteFromPLArray(value, 1);
WMInsertInPLArray(value, 1, WMCreatePLString(newPath));
free(newPath);
wfree(newPath);
} else {
findCopyFile(themeDir, WMGetFromPLString(file));
}
@@ -277,7 +277,7 @@ static void makeThemePack(WMPropList * style, const char *themeName)
WMDeleteFromPLArray(value, 2);
WMInsertInPLArray(value, 2, WMCreatePLString(newPath));
free(newPath);
wfree(newPath);
} else {
findCopyFile(themeDir, WMGetFromPLString(file));
}

View File

@@ -202,7 +202,7 @@ int change_title(XTextProperty *prop, char *filename)
XSetWMName(dpy, win, prop);
if (prop->value)
XFree(prop->value);
free(combined_title);
wfree(combined_title);
return EXIT_SUCCESS;
}
@@ -596,8 +596,8 @@ int linked_list_add(linked_list_t *list, const void *data)
{
link_t *link;
/* calloc sets the "next" field to zero. */
link = calloc(1, sizeof(link_t));
/* wmalloc zeros the buffer it returns, so the "next" is zero. */
link = wmalloc(sizeof(link_t));
if (!link) {
fprintf(stderr, "Error: memory allocation failed\n");
return EXIT_FAILURE;
@@ -627,8 +627,8 @@ void linked_list_free(linked_list_t *list)
/* Store the next value so that we don't access freed memory. */
next = link->next;
if (link->data)
free((char *)link->data);
free(link);
wfree((char *)link->data);
wfree(link);
}
}
@@ -651,7 +651,7 @@ link_t *connect_dir(char *dirpath, linked_list_t *li)
/* maybe it's a file */
struct stat stDirInfo;
if (lstat(dirpath, &stDirInfo) == 0) {
linked_list_add(li, strdup(dirpath));
linked_list_add(li, wstrdup(dirpath));
return li->first;
} else {
return NULL;
@@ -664,11 +664,11 @@ link_t *connect_dir(char *dirpath, linked_list_t *li)
else
snprintf(path, PATH_MAX, "%s%c%s", dirpath, FILE_SEPARATOR, dir[idx]->d_name);
free(dir[idx]);
wfree(dir[idx]);
if ((lstat(path, &stDirInfo) == 0) && !S_ISDIR(stDirInfo.st_mode))
linked_list_add(li, strdup(path));
linked_list_add(li, wstrdup(path));
}
free(dir);
wfree(dir);
return li->first;
}

View File

@@ -35,8 +35,8 @@
static void addWMMenuEntryCallback(WMMenuEntry *aEntry);
static void assemblePLMenuFunc(WMTreeNode *aNode, void *data);
static int dirParseFunc(const char *filename, const struct stat *st, int tflags, struct FTW *ftw);
static int menuSortFunc(const void *left, const void *right);
static int nodeFindSubMenuByNameFunc(const void *item, const void *cdata);
static int menuSortFunc(const WMTreeNode *left, const WMTreeNode *right);
static int nodeFindSubMenuByNameFunc(const WMTreeNode *tree, const void *cdata);
static WMTreeNode *findPositionInMenu(const char *submenu);
@@ -178,7 +178,7 @@ int main(int argc, char **argv)
}
WMSortTree(menu, menuSortFunc);
WMTreeWalk(menu, assemblePLMenuFunc, previousDepth, True);
WMTreeWalk(menu, assemblePLMenuFunc, previousDepth);
i = WMGetArrayItemCount(plMenuNodes);
if (i > 2) { /* more than one submenu unprocessed is almost certainly an error */
@@ -324,13 +324,13 @@ static void assemblePLMenuFunc(WMTreeNode *aNode, void *data)
/* sort the menu tree; callback for WMSortTree()
*/
static int menuSortFunc(const void *left, const void *right)
static int menuSortFunc(const WMTreeNode *left, const WMTreeNode *right)
{
WMMenuEntry *leftwm;
WMMenuEntry *rightwm;
leftwm = (WMMenuEntry *)WMGetDataForTreeNode(*(WMTreeNode **)left);
rightwm = (WMMenuEntry *)WMGetDataForTreeNode(*(WMTreeNode **)right);
leftwm = (WMMenuEntry *)WMGetDataForTreeNode(left);
rightwm = (WMMenuEntry *)WMGetDataForTreeNode(right);
/* submenus first */
if (!leftwm->CmdLine && rightwm->CmdLine)
@@ -380,11 +380,11 @@ static WMTreeNode *findPositionInMenu(const char *submenu)
/* find node where Name = cdata and node is a submenu
*/
static int nodeFindSubMenuByNameFunc(const void *item, const void *cdata)
static int nodeFindSubMenuByNameFunc(const WMTreeNode *tree, const void *cdata)
{
WMMenuEntry *wm;
wm = (WMMenuEntry *)item;
wm = (WMMenuEntry *)WMGetDataForTreeNode(tree);
if (wm->CmdLine) /* if it has a cmdline, it can't be a submenu */
return 0;

View File

@@ -121,7 +121,7 @@ void parse_locale(const char *what, char **language, char **country, char **enco
*language = wstrdup(e);
out:
free(e);
wfree(e);
return;
}

View File

@@ -398,7 +398,7 @@ static BackgroundTexture *parseTexture(RContext * rc, char *text)
RGradientStyle gtype;
int iwidth, iheight;
colors = malloc(sizeof(RColor *) * (count - 1));
colors = wmalloc(sizeof(RColor *) * (count - 1));
if (!colors) {
wwarning("out of memory while parsing texture");
goto error;
@@ -425,7 +425,7 @@ static BackgroundTexture *parseTexture(RContext * rc, char *text)
wfree(colors);
goto error;
}
if (!(colors[i - 2] = malloc(sizeof(RColor)))) {
if (!(colors[i - 2] = wmalloc(sizeof(RColor)))) {
wwarning("out of memory while parsing texture");
for (j = 0; colors[j] != NULL; j++)

View File

@@ -26,6 +26,8 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <WINGs/WUtil.h>
#include "../src/wconfig.h"
#define LINESIZE (4*1024)
@@ -197,7 +199,7 @@ int main(int argc, char **argv)
break;
}
if (buf_len == 0) {
nbuf = malloc(buf_len = l + nl + 1);
nbuf = wmalloc(buf_len = l + nl + 1);
} else if (buf_len < l + nl + 1) {
/*
* To avoid terrible performance on big input buffers,
@@ -205,12 +207,7 @@ int main(int argc, char **argv)
* current line.
*/
buf_len = 2 * buf_len + nl + 1;
/* some realloc implementations don't do malloc if buf==NULL */
if (buf == NULL) {
nbuf = malloc(buf_len);
} else {
nbuf = realloc(buf, buf_len);
}
nbuf = wrealloc(buf, buf_len);
} else {
nbuf = buf;
}

View File

@@ -6,7 +6,8 @@ lib_LTLIBRARIES = libWMaker.la
include_HEADERS = WMaker.h
AM_CPPFLAGS = $(DFLAGS) @XCFLAGS@
AM_CPPFLAGS = $(DFLAGS) @XCFLAGS@ \
-I$(top_srcdir)/WINGs -I$(top_builddir)/WINGs
libWMaker_la_LIBADD = @XLFLAGS@ @XLIBS@

View File

@@ -24,6 +24,8 @@
#include <stdlib.h>
#include <string.h>
#include <WINGs/WUtil.h>
#include "WMaker.h"
#include "app.h"
@@ -31,7 +33,7 @@ WMAppContext *WMAppCreateWithMain(Display * display, int screen_number, Window m
{
wmAppContext *ctx;
ctx = malloc(sizeof(wmAppContext));
ctx = wmalloc(sizeof(wmAppContext));
if (!ctx)
return NULL;
@@ -39,9 +41,9 @@ WMAppContext *WMAppCreateWithMain(Display * display, int screen_number, Window m
ctx->screen_number = screen_number;
ctx->our_leader_hint = False;
ctx->main_window = main_window;
ctx->windows = malloc(sizeof(Window));
ctx->windows = wmalloc(sizeof(Window));
if (!ctx->windows) {
free(ctx);
wfree(ctx);
return NULL;
}
ctx->win_count = 1;
@@ -58,13 +60,13 @@ int WMAppAddWindow(WMAppContext * app, Window window)
{
Window *win;
win = malloc(sizeof(Window) * (app->win_count + 1));
win = wmalloc(sizeof(Window) * (app->win_count + 1));
if (!win)
return False;
memcpy(win, app->windows, sizeof(Window) * app->win_count);
free(app->windows);
wfree(app->windows);
win[app->win_count] = window;
app->windows = win;

View File

@@ -26,6 +26,8 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <WINGs/WUtil.h>
#include "WMaker.h"
#include "app.h"
#include "menu.h"
@@ -37,7 +39,7 @@ WMMenu *WMMenuCreate(WMAppContext * app, char *title)
if (strlen(title) > 255)
return NULL;
menu = malloc(sizeof(wmMenu));
menu = wmalloc(sizeof(wmMenu));
if (!menu)
return NULL;
@@ -50,12 +52,12 @@ WMMenu *WMMenuCreate(WMAppContext * app, char *title)
menu->realized = False;
menu->code = app->last_menu_tag++;
menu->entryline = malloc(strlen(title) + 32);
menu->entryline2 = malloc(32);
menu->entryline = wmalloc(strlen(title) + 32);
menu->entryline2 = wmalloc(32);
if (!menu->entryline || !menu->entryline2) {
if (menu->entryline)
free(menu->entryline);
free(menu);
wfree(menu->entryline);
wfree(menu);
return NULL;
}
sprintf(menu->entryline, "%i %i %s", wmBeginMenu, menu->code, title);
@@ -77,13 +79,13 @@ WMMenuAddItem(WMMenu * menu, char *text, WMMenuAction action,
if (strlen(text) > 255)
return -1;
entry = malloc(sizeof(wmMenuEntry));
entry = wmalloc(sizeof(wmMenuEntry));
if (!entry)
return -1;
entry->entryline = malloc(strlen(text) + 100);
entry->entryline = wmalloc(strlen(text) + 100);
if (!entry->entryline) {
free(entry);
wfree(entry);
return -1;
}
@@ -125,13 +127,13 @@ int WMMenuAddSubmenu(WMMenu * menu, char *text, WMMenu * submenu)
if (strlen(text) > 255)
return -1;
entry = malloc(sizeof(wmMenuEntry));
entry = wmalloc(sizeof(wmMenuEntry));
if (!entry)
return -1;
entry->entryline = malloc(strlen(text) + 100);
entry->entryline = wmalloc(strlen(text) + 100);
if (!entry->entryline) {
free(entry);
wfree(entry);
return -1;
}
@@ -217,7 +219,7 @@ int WMRealizeMenus(WMAppContext * app)
return True;
count++;
slist = malloc(count * sizeof(char *));
slist = wmalloc(count * sizeof(char *));
if (!slist) {
return False;
}
@@ -227,10 +229,10 @@ int WMRealizeMenus(WMAppContext * app)
addItems(slist, &i, app->main_menu);
if (!XStringListToTextProperty(slist, i, &text_prop)) {
free(slist);
wfree(slist);
return False;
}
free(slist);
wfree(slist);
XSetTextProperty(app->dpy, app->main_window, &text_prop, getatom(app->dpy));
XFree(text_prop.value);

View File

@@ -82,7 +82,8 @@ libwraster_la_SOURCES += load_magick.c
endif
AM_CFLAGS = @MAGICKFLAGS@
AM_CPPFLAGS = $(DFLAGS) @HEADER_SEARCH_PATH@
AM_CPPFLAGS = $(DFLAGS) @HEADER_SEARCH_PATH@ \
-I$(top_srcdir)/WINGs -I$(top_builddir)/WINGs
libwraster_la_LIBADD = @LIBRARY_SEARCH_PATH@ @GFXLIBS@ @MAGICKLIBS@ @XLIBS@ @LIBXMU@ -lm

View File

@@ -37,6 +37,8 @@
#include <math.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "scale.h"
#include "wr_i18n.h"
@@ -104,17 +106,17 @@ static Bool allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdc
return False;
}
ctx->colors = malloc(sizeof(XColor) * ctx->ncolors);
ctx->colors = wmalloc(sizeof(XColor) * ctx->ncolors);
if (!ctx->colors) {
RErrorCode = RERR_NOMEMORY;
return False;
}
ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors);
ctx->pixels = wmalloc(sizeof(unsigned long) * ctx->ncolors);
if (!ctx->pixels) {
free(ctx->colors);
wfree(ctx->colors);
ctx->colors = NULL;
RErrorCode = RERR_NOMEMORY;
@@ -246,15 +248,15 @@ static Bool allocatePseudoColor(RContext *ctx)
assert(cpc >= 2 && ncolors <= (1 << ctx->depth));
colors = malloc(sizeof(XColor) * ncolors);
colors = wmalloc(sizeof(XColor) * ncolors);
if (!colors) {
RErrorCode = RERR_NOMEMORY;
return False;
}
ctx->pixels = malloc(sizeof(unsigned long) * ncolors);
ctx->pixels = wmalloc(sizeof(unsigned long) * ncolors);
if (!ctx->pixels) {
free(colors);
wfree(colors);
RErrorCode = RERR_NOMEMORY;
return False;
}
@@ -343,7 +345,7 @@ static XColor *allocateGrayScale(RContext * ctx)
ctx->attribs->render_mode = RBestMatchRendering;
}
colors = malloc(sizeof(XColor) * ncolors);
colors = wmalloc(sizeof(XColor) * ncolors);
if (!colors) {
RErrorCode = RERR_NOMEMORY;
return False;
@@ -526,7 +528,7 @@ RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttribu
RContext *context;
XGCValues gcv;
context = malloc(sizeof(RContext));
context = wmalloc(sizeof(RContext));
if (!context) {
RErrorCode = RERR_NOMEMORY;
return NULL;
@@ -537,9 +539,9 @@ RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttribu
context->screen_number = screen_number;
context->attribs = malloc(sizeof(RContextAttributes));
context->attribs = wmalloc(sizeof(RContextAttributes));
if (!context->attribs) {
free(context);
wfree(context);
RErrorCode = RERR_NOMEMORY;
return NULL;
}
@@ -568,7 +570,7 @@ RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttribu
templ.visualid = context->attribs->visualid;
vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret);
if (!vinfo || nret == 0) {
free(context);
wfree(context);
RErrorCode = RERR_BADVISUALID;
return NULL;
}
@@ -615,13 +617,13 @@ RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttribu
if (context->vclass == PseudoColor || context->vclass == StaticColor) {
if (!setupPseudoColorColormap(context)) {
free(context);
wfree(context);
return NULL;
}
} else if (context->vclass == GrayScale || context->vclass == StaticGray) {
context->colors = allocateGrayScale(context);
if (!context->colors) {
free(context);
wfree(context);
return NULL;
}
} else if (context->vclass == TrueColor) {
@@ -668,9 +670,9 @@ void RDestroyContext(RContext *context)
if ((context->attribs->flags & RC_VisualID) &&
!(context->attribs->flags & RC_DefaultVisual))
XDestroyWindow(context->dpy, context->drawable);
free(context->attribs);
wfree(context->attribs);
}
free(context);
wfree(context);
}
}

View File

@@ -33,13 +33,15 @@
#include <string.h>
#include <assert.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "convert.h"
#include "xutil.h"
#include "wr_i18n.h"
#define NFREE(n) if (n) free(n)
#define NFREE(n) wfree(n)
#define HAS_ALPHA(I) ((I)->format == RRGBAFormat)
@@ -70,7 +72,7 @@ static void release_conversion_table(void)
RConversionTable *tmp_to_delete = tmp;
tmp = tmp->next;
free(tmp_to_delete);
wfree(tmp_to_delete);
}
conversionTable = NULL;
}
@@ -83,7 +85,7 @@ static void release_std_conversion_table(void)
RStdConversionTable *tmp_to_delete = tmp;
tmp = tmp->next;
free(tmp_to_delete);
wfree(tmp_to_delete);
}
stdConversionTable = NULL;
}
@@ -108,7 +110,7 @@ static unsigned short *computeTable(unsigned short mask)
if (tmp)
return tmp->table;
tmp = (RConversionTable *) malloc(sizeof(RConversionTable));
tmp = (RConversionTable *) wmalloc(sizeof(RConversionTable));
if (tmp == NULL)
return NULL;
@@ -135,7 +137,7 @@ static unsigned int *computeStdTable(unsigned int mult, unsigned int max)
if (tmp)
return tmp->table;
tmp = (RStdConversionTable *) malloc(sizeof(RStdConversionTable));
tmp = (RStdConversionTable *) wmalloc(sizeof(RStdConversionTable));
if (tmp == NULL)
return NULL;
@@ -372,8 +374,8 @@ static RXImage *image2TrueColor(RContext * ctx, RImage * image)
signed char *nerr;
int ch = (HAS_ALPHA(image) ? 4 : 3);
err = malloc(ch * (image->width + 2));
nerr = malloc(ch * (image->width + 2));
err = wmalloc(ch * (image->width + 2));
nerr = wmalloc(ch * (image->width + 2));
if (!err || !nerr) {
NFREE(err);
NFREE(nerr);
@@ -387,8 +389,8 @@ static RXImage *image2TrueColor(RContext * ctx, RImage * image)
convertTrueColor_generic(ximg, image, err, nerr,
rtable, gtable, btable, dr, dg, db, roffs, goffs, boffs);
free(err);
free(nerr);
wfree(err);
wfree(nerr);
}
}
@@ -540,8 +542,8 @@ static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
#ifdef WRLIB_DEBUG
fprintf(stderr, "pseudo color dithering with %d colors per channel\n", cpc);
#endif
err = malloc(4 * (image->width + 3));
nerr = malloc(4 * (image->width + 3));
err = wmalloc(4 * (image->width + 3));
nerr = wmalloc(4 * (image->width + 3));
if (!err || !nerr) {
NFREE(err);
NFREE(nerr);
@@ -555,8 +557,8 @@ static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
convertPseudoColor_to_8(ximg, image, err + 4, nerr + 4,
rtable, gtable, btable, dr, dg, db, ctx->pixels, cpc);
free(err);
free(nerr);
wfree(err);
wfree(nerr);
}
return ximg;
@@ -618,8 +620,8 @@ static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
fprintf(stderr, "pseudo color dithering with %d colors per channel\n",
ctx->attribs->colors_per_channel);
#endif
err = (short *)malloc(3 * (image->width + 2) * sizeof(short));
nerr = (short *)malloc(3 * (image->width + 2) * sizeof(short));
err = (short *)wmalloc(3 * (image->width + 2) * sizeof(short));
nerr = (short *)wmalloc(3 * (image->width + 2) * sizeof(short));
if (!err || !nerr) {
NFREE(err);
NFREE(nerr);
@@ -707,8 +709,8 @@ static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
ofs += ximg->image->bytes_per_line - image->width;
}
free(err);
free(nerr);
wfree(err);
wfree(nerr);
}
ximg->image->data = (char *)data;
@@ -773,8 +775,8 @@ static RXImage *image2GrayScale(RContext * ctx, RImage * image)
#ifdef WRLIB_DEBUG
fprintf(stderr, "grayscale dither with %d colors per channel\n", cpc);
#endif
gerr = (short *)malloc((image->width + 2) * sizeof(short));
ngerr = (short *)malloc((image->width + 2) * sizeof(short));
gerr = (short *)wmalloc((image->width + 2) * sizeof(short));
ngerr = (short *)wmalloc((image->width + 2) * sizeof(short));
if (!gerr || !ngerr) {
NFREE(gerr);
NFREE(ngerr);
@@ -830,8 +832,8 @@ static RXImage *image2GrayScale(RContext * ctx, RImage * image)
gerr = ngerr;
ngerr = terr;
}
free(gerr);
free(ngerr);
wfree(gerr);
wfree(ngerr);
}
ximg->image->data = (char *)data;

View File

@@ -26,6 +26,8 @@
#include <string.h>
#include <X11/Xlib.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "wr_i18n.h"
@@ -46,7 +48,7 @@ int RBlurImage(RImage * image)
unsigned char *pptr = NULL, *tmpp;
int ch = image->format == RRGBAFormat ? 4 : 3;
pptr = malloc(image->width * ch);
pptr = wmalloc(image->width * ch);
if (!pptr) {
RErrorCode = RERR_NOMEMORY;
return False;
@@ -138,7 +140,7 @@ int RBlurImage(RImage * image)
}
}
free(tmpp);
wfree(tmpp);
return True;
}

View File

@@ -34,6 +34,8 @@
#include <time.h>
#include <assert.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -125,7 +127,7 @@ static void init_cache(void)
RImageCacheMaxImage = IMAGE_CACHE_MAXIMUM_MAXPIXELS;
if (RImageCacheSize > 0) {
RImageCache = malloc(sizeof(RCachedImage) * RImageCacheSize);
RImageCache = wmalloc(sizeof(RCachedImage) * RImageCacheSize);
if (RImageCache == NULL) {
fprintf(stderr, _("wrlib: out of memory for image cache\n"));
return;
@@ -142,10 +144,10 @@ void RReleaseCache(void)
for (i = 0; i < RImageCacheSize; i++) {
if (RImageCache[i].file) {
RReleaseImage(RImageCache[i].image);
free(RImageCache[i].file);
wfree(RImageCache[i].file);
}
}
free(RImageCache);
wfree(RImageCache);
RImageCache = NULL;
RImageCacheSize = -1;
}
@@ -173,7 +175,7 @@ RImage *RLoadImage(RContext *context, const char *file, int index)
return RCloneImage(RImageCache[i].image);
} else {
free(RImageCache[i].file);
wfree(RImageCache[i].file);
RImageCache[i].file = NULL;
RReleaseImage(RImageCache[i].image);
}
@@ -254,8 +256,7 @@ RImage *RLoadImage(RContext *context, const char *file, int index)
for (i = 0; i < RImageCacheSize; i++) {
if (!RImageCache[i].file) {
RImageCache[i].file = malloc(strlen(file) + 1);
strcpy(RImageCache[i].file, file);
RImageCache[i].file = wstrdup(file);
RImageCache[i].image = RCloneImage(image);
RImageCache[i].last_modif = st.st_mtime;
RImageCache[i].last_use = time(NULL);
@@ -271,10 +272,9 @@ RImage *RLoadImage(RContext *context, const char *file, int index)
/* if no slot available, dump least recently used one */
if (!done) {
free(RImageCache[oldest_idx].file);
wfree(RImageCache[oldest_idx].file);
RReleaseImage(RImageCache[oldest_idx].image);
RImageCache[oldest_idx].file = malloc(strlen(file) + 1);
strcpy(RImageCache[oldest_idx].file, file);
RImageCache[oldest_idx].file = wstrdup(file);
RImageCache[oldest_idx].image = RCloneImage(image);
RImageCache[oldest_idx].last_modif = st.st_mtime;
RImageCache[oldest_idx].last_use = time(NULL);

View File

@@ -28,6 +28,8 @@
#include <gif_lib.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -127,7 +129,7 @@ RImage *RLoadGIF(const char *file, int index)
}
}
buffer = malloc(width * sizeof(GifPixelType));
buffer = wmalloc(width * sizeof(GifPixelType));
if (!buffer) {
RErrorCode = RERR_NOMEMORY;
goto bye;
@@ -219,7 +221,7 @@ RImage *RLoadGIF(const char *file, int index)
did_not_get_any_errors:
if (buffer)
free(buffer);
wfree(buffer);
if (gif)
#if (USE_GIF == 5) && (GIFLIB_MINOR >= 1)

View File

@@ -35,6 +35,8 @@
#include <stdnoreturn.h>
#endif
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -119,7 +121,7 @@ static RImage *do_read_jpeg_file(struct jpeg_decompress_struct *cinfo, const cha
goto abort_and_release_resources;
}
buffer[0] = (JSAMPROW) malloc(cinfo->image_width * cinfo->num_components);
buffer[0] = (JSAMPROW) wmalloc(cinfo->image_width * cinfo->num_components);
if (!buffer[0]) {
RErrorCode = RERR_NOMEMORY;
goto abort_and_release_resources;
@@ -167,7 +169,7 @@ static RImage *do_read_jpeg_file(struct jpeg_decompress_struct *cinfo, const cha
jpeg_destroy_decompress(cinfo);
fclose(file);
if (buffer[0])
free(buffer[0]);
wfree(buffer[0]);
return image;
}

View File

@@ -28,6 +28,8 @@
#include <png.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -165,7 +167,7 @@ RImage *RLoadPNG(RContext *context, const char *file)
image->background.blue = bkcolor->blue >> 8;
}
png_rows = calloc(height, sizeof(png_bytep));
png_rows = wmalloc(height * sizeof(png_bytep));
if (!png_rows) {
RErrorCode = RERR_NOMEMORY;
fclose(f);
@@ -174,7 +176,7 @@ RImage *RLoadPNG(RContext *context, const char *file)
return NULL;
}
for (y = 0; y < height; y++) {
png_rows[y] = malloc(png_get_rowbytes(png, pinfo));
png_rows[y] = wmalloc(png_get_rowbytes(png, pinfo));
if (!png_rows[y]) {
RErrorCode = RERR_NOMEMORY;
fclose(f);
@@ -182,8 +184,8 @@ RImage *RLoadPNG(RContext *context, const char *file)
png_destroy_read_struct(&png, &pinfo, &einfo);
while (y-- > 0)
if (png_rows[y])
free(png_rows[y]);
free(png_rows);
wfree(png_rows[y]);
wfree(png_rows);
return NULL;
}
}
@@ -214,7 +216,7 @@ RImage *RLoadPNG(RContext *context, const char *file)
}
for (y = 0; y < height; y++)
if (png_rows[y])
free(png_rows[y]);
free(png_rows);
wfree(png_rows[y]);
wfree(png_rows);
return image;
}

View File

@@ -29,6 +29,8 @@
#include <string.h>
#include <limits.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -161,7 +163,7 @@ static RImage *load_graymap(FILE *file, int w, int h, int max, int raw, const ch
if (raw == '5') {
char *buf;
buf = malloc(w + 1);
buf = wmalloc(w + 1);
if (!buf) {
RErrorCode = RERR_NOMEMORY;
RReleaseImage(image);
@@ -169,7 +171,7 @@ static RImage *load_graymap(FILE *file, int w, int h, int max, int raw, const ch
}
for (y = 0; y < h; y++) {
if (!fread(buf, w, 1, file)) {
free(buf);
wfree(buf);
RErrorCode = RERR_BADIMAGEFILE;
RReleaseImage(image);
return NULL;
@@ -181,7 +183,7 @@ static RImage *load_graymap(FILE *file, int w, int h, int max, int raw, const ch
*(ptr++) = buf[x];
}
}
free(buf);
wfree(buf);
}
}
}

View File

@@ -29,6 +29,8 @@
#include <webp/decode.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -110,7 +112,7 @@ RImage *RLoadWEBP(const char *file_name)
return NULL;
}
raw_data = (uint8_t *) malloc(raw_data_size);
raw_data = (uint8_t *) wmalloc(raw_data_size);
if (!raw_data) {
RErrorCode = RERR_NOMEMORY;
@@ -124,7 +126,7 @@ RImage *RLoadWEBP(const char *file_name)
if (r != raw_data_size) {
RErrorCode = RERR_READ;
free(raw_data);
wfree(raw_data);
return NULL;
}
@@ -133,7 +135,7 @@ RImage *RLoadWEBP(const char *file_name)
fprintf(stderr, _("wrlib: could not get features from WebP file \"%s\", %s\n"),
file_name, webp_message_from_status(status));
RErrorCode = RERR_BADIMAGEFILE;
free(raw_data);
wfree(raw_data);
return NULL;
}
@@ -141,7 +143,7 @@ RImage *RLoadWEBP(const char *file_name)
image = RCreateImage(features.width, features.height, True);
if (!image) {
RErrorCode = RERR_NOMEMORY;
free(raw_data);
wfree(raw_data);
return NULL;
}
ret = WebPDecodeRGBAInto(raw_data, raw_data_size, image->data,
@@ -151,7 +153,7 @@ RImage *RLoadWEBP(const char *file_name)
image = RCreateImage(features.width, features.height, False);
if (!image) {
RErrorCode = RERR_NOMEMORY;
free(raw_data);
wfree(raw_data);
return NULL;
}
ret = WebPDecodeRGBInto(raw_data, raw_data_size, image->data,
@@ -159,7 +161,7 @@ RImage *RLoadWEBP(const char *file_name)
features.width * 3);
}
free(raw_data);
wfree(raw_data);
if (!ret) {
fprintf(stderr, _("wrlib: failed to decode WebP from file \"%s\"\n"), file_name);

View File

@@ -29,6 +29,8 @@
#include <string.h>
#include <X11/xpm.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -59,11 +61,11 @@ static RImage *create_rimage_from_xpm(RContext *context, XpmImage xpm)
/* make color table */
for (i = 0; i < 4; i++) {
color_table[i] = malloc(xpm.ncolors * sizeof(unsigned char));
color_table[i] = wmalloc(xpm.ncolors * sizeof(unsigned char));
if (!color_table[i]) {
for (i = i - 1; i >= 0; i--) {
if (color_table[i])
free(color_table[i]);
wfree(color_table[i]);
}
RReleaseImage(image);
RErrorCode = RERR_NOMEMORY;
@@ -123,7 +125,7 @@ static RImage *create_rimage_from_xpm(RContext *context, XpmImage xpm)
*(data++) = color_table[3][*p];
}
for (i = 0; i < 4; i++)
free(color_table[i]);
wfree(color_table[i]);
return image;
}

View File

@@ -59,15 +59,15 @@ static void free_color_symbol_table(unsigned char *color_table[],
unsigned short *symbol_table)
{
if (color_table[0])
free(color_table[0]);
wfree(color_table[0]);
if (color_table[1])
free(color_table[1]);
wfree(color_table[1]);
if (color_table[2])
free(color_table[2]);
wfree(color_table[2]);
if (color_table[3])
free(color_table[3]);
wfree(color_table[3]);
if (symbol_table)
free(symbol_table);
wfree(symbol_table);
}
RImage *RGetImageFromXPMData(RContext * context, char **data)
@@ -95,11 +95,11 @@ RImage *RGetImageFromXPMData(RContext * context, char **data)
if (csize != 1 && csize != 2)
goto bad_format;
color_table[0] = malloc(ccount);
color_table[1] = malloc(ccount);
color_table[2] = malloc(ccount);
color_table[3] = malloc(ccount);
symbol_table = malloc(ccount * sizeof(unsigned short));
color_table[0] = wmalloc(ccount);
color_table[1] = wmalloc(ccount);
color_table[2] = wmalloc(ccount);
color_table[3] = wmalloc(ccount);
symbol_table = wmalloc(ccount * sizeof(unsigned short));
bsize = csize * w + 16;
@@ -283,14 +283,14 @@ RImage *RLoadXPM(RContext * context, const char *file)
if (csize != 1 && csize != 2)
goto bad_format;
color_table[0] = malloc(ccount);
color_table[1] = malloc(ccount);
color_table[2] = malloc(ccount);
color_table[3] = malloc(ccount);
symbol_table = malloc(ccount * sizeof(unsigned short));
color_table[0] = wmalloc(ccount);
color_table[1] = wmalloc(ccount);
color_table[2] = wmalloc(ccount);
color_table[3] = wmalloc(ccount);
symbol_table = wmalloc(ccount * sizeof(unsigned short));
bsize = csize * w + 16;
buffer = malloc(bsize);
buffer = wmalloc(bsize);
if (!color_table[0] || !color_table[1] || !color_table[2] ||
!color_table[3] || !symbol_table || !bsize || !buffer) {
@@ -298,7 +298,7 @@ RImage *RLoadXPM(RContext * context, const char *file)
fclose(f);
free_color_symbol_table(color_table, symbol_table);
if (buffer)
free(buffer);
wfree(buffer);
return NULL;
}
@@ -355,7 +355,7 @@ RImage *RLoadXPM(RContext * context, const char *file)
fclose(f);
free_color_symbol_table(color_table, symbol_table);
if (buffer)
free(buffer);
wfree(buffer);
return NULL;
}
@@ -434,7 +434,7 @@ RImage *RLoadXPM(RContext * context, const char *file)
fclose(f);
free_color_symbol_table(color_table, symbol_table);
if (buffer)
free(buffer);
wfree(buffer);
return image;
bad_format:
@@ -442,7 +442,7 @@ RImage *RLoadXPM(RContext * context, const char *file)
fclose(f);
free_color_symbol_table(color_table, symbol_table);
if (buffer)
free(buffer);
wfree(buffer);
if (image)
RReleaseImage(image);
return NULL;
@@ -452,7 +452,7 @@ RImage *RLoadXPM(RContext * context, const char *file)
fclose(f);
free_color_symbol_table(color_table, symbol_table);
if (buffer)
free(buffer);
wfree(buffer);
if (image)
RReleaseImage(image);
return NULL;

View File

@@ -27,6 +27,8 @@
#include <string.h>
#include <X11/Xlib.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "wr_i18n.h"
@@ -53,7 +55,7 @@ RImage *RCreateImage(unsigned width, unsigned height, int alpha)
return NULL;
}
image = malloc(sizeof(RImage));
image = wmalloc(sizeof(RImage));
if (!image) {
RErrorCode = RERR_NOMEMORY;
return NULL;
@@ -68,10 +70,10 @@ RImage *RCreateImage(unsigned width, unsigned height, int alpha)
/* the +4 is to give extra bytes at the end of the buffer,
* so that we can optimize image conversion for MMX(tm).. see convert.c
*/
image->data = malloc(width * height * (alpha ? 4 : 3) + 4);
image->data = wmalloc(width * height * (alpha ? 4 : 3) + 4);
if (!image->data) {
RErrorCode = RERR_NOMEMORY;
free(image);
wfree(image);
image = NULL;
}
@@ -94,8 +96,8 @@ void RReleaseImage(RImage * image)
image->refCount--;
if (image->refCount < 1) {
free(image->data);
free(image);
wfree(image->data);
wfree(image);
}
}

View File

@@ -29,6 +29,8 @@
#include <errno.h>
#include <jpeglib.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -59,7 +61,7 @@ Bool RSaveJPEG(RImage *img, const char *filename, char *title)
img_depth = 3;
/* collect separate RGB values to a buffer */
buffer = malloc(sizeof(char) * 3 * img->width * img->height);
buffer = wmalloc(sizeof(char) * 3 * img->width * img->height);
for (y = 0; y < img->height; y++) {
for (x = 0; x < img->width; x++) {
RGetPixel(img, x, y, &pixel);
@@ -97,7 +99,7 @@ Bool RSaveJPEG(RImage *img, const char *filename, char *title)
jpeg_finish_compress(&cinfo);
/* Clean */
free(buffer);
wfree(buffer);
fclose(file);
return True;

View File

@@ -29,6 +29,8 @@
#include <errno.h>
#include <png.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -95,7 +97,7 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
png_write_info(png_ptr, png_info_ptr);
/* Allocate memory for one row (3 bytes per pixel - RGB) */
png_row = (png_bytep) malloc(3 * width * sizeof(png_byte));
png_row = (png_bytep) wmalloc(3 * width * sizeof(png_byte));
/* Write image data */
for (y = 0; y < height; y++) {
@@ -121,7 +123,7 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
if (png_ptr != NULL)
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
if (png_row != NULL)
free(png_row);
wfree(png_row);
return True;
}

View File

@@ -28,6 +28,8 @@
#include <assert.h>
#include <errno.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
@@ -95,7 +97,7 @@ static Bool addcolor(XPMColor ** list, unsigned r, unsigned g, unsigned b, int *
if (tmpc)
return True;
newc = malloc(sizeof(XPMColor));
newc = wmalloc(sizeof(XPMColor));
if (!newc) {
@@ -149,7 +151,7 @@ static void freecolormap(XPMColor * colormap)
while (colormap) {
tmp = colormap->next;
free(colormap);
wfree(colormap);
colormap = tmp;
}
}

View File

@@ -29,6 +29,8 @@
#include <math.h>
#include <assert.h>
#include <WINGs/WUtil.h>
#include "wraster.h"
#include "scale.h"
#include "wr_i18n.h"
@@ -295,7 +297,7 @@ typedef struct {
/* clamp the input to the specified range */
#define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
/* return of calloc is not checked if NULL in the function below! */
/* return of wmalloc is not checked if NULL in the function below! */
RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
{
CLIST *contrib; /* array of contribution lists */
@@ -319,13 +321,13 @@ RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
yscale = (double)new_height / (double)src->height;
/* pre-calculate filter contributions for a row */
contrib = (CLIST *) calloc(new_width, sizeof(CLIST));
contrib = (CLIST *) wmalloc(new_width * sizeof(CLIST));
if (xscale < 1.0) {
width = fwidth / xscale;
fscale = 1.0 / xscale;
for (i = 0; i < new_width; ++i) {
contrib[i].n = 0;
contrib[i].p = (CONTRIB *) calloc((int) ceil(width * 2 + 1), sizeof(CONTRIB));
contrib[i].p = (CONTRIB *) wmalloc(ceil(width * 2 + 1) * sizeof(CONTRIB));
center = (double)i / xscale;
left = ceil(center - width);
right = floor(center + width);
@@ -348,7 +350,7 @@ RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
for (i = 0; i < new_width; ++i) {
contrib[i].n = 0;
contrib[i].p = (CONTRIB *) calloc((int) ceil(fwidth * 2 + 1), sizeof(CONTRIB));
contrib[i].p = (CONTRIB *) wmalloc(ceil(fwidth * 2 + 1) * sizeof(CONTRIB));
center = (double)i / xscale;
left = ceil(center - fwidth);
right = floor(center + fwidth);
@@ -395,18 +397,18 @@ RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
/* free the memory allocated for horizontal filter weights */
for (i = 0; i < new_width; ++i) {
free(contrib[i].p);
wfree(contrib[i].p);
}
free(contrib);
wfree(contrib);
/* pre-calculate filter contributions for a column */
contrib = (CLIST *) calloc(dst->height, sizeof(CLIST));
contrib = (CLIST *) wmalloc(dst->height * sizeof(CLIST));
if (yscale < 1.0) {
width = fwidth / yscale;
fscale = 1.0 / yscale;
for (i = 0; i < dst->height; ++i) {
contrib[i].n = 0;
contrib[i].p = (CONTRIB *) calloc((int) ceil(width * 2 + 1), sizeof(CONTRIB));
contrib[i].p = (CONTRIB *) wmalloc(ceil(width * 2 + 1) * sizeof(CONTRIB));
center = (double)i / yscale;
left = ceil(center - width);
right = floor(center + width);
@@ -428,7 +430,7 @@ RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
} else {
for (i = 0; i < dst->height; ++i) {
contrib[i].n = 0;
contrib[i].p = (CONTRIB *) calloc((int) ceil(fwidth * 2 + 1), sizeof(CONTRIB));
contrib[i].p = (CONTRIB *) wmalloc(ceil(fwidth * 2 + 1) * sizeof(CONTRIB));
center = (double)i / yscale;
left = ceil(center - fwidth);
right = floor(center + fwidth);
@@ -450,7 +452,7 @@ RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
}
/* apply filter to zoom vertically from tmp to dst */
sp = malloc(tmp->height * 3);
sp = wmalloc(tmp->height * 3);
for (k = 0; k < new_width; ++k) {
CONTRIB *pp;
@@ -485,13 +487,13 @@ RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
p += new_width * 3;
}
}
free(sp);
wfree(sp);
/* free the memory allocated for vertical filter weights */
for (i = 0; i < dst->height; ++i) {
free(contrib[i].p);
wfree(contrib[i].p);
}
free(contrib);
wfree(contrib);
RReleaseImage(tmp);

View File

@@ -149,10 +149,10 @@ int main(int argc, char **argv)
getchar();
free(color_name);
wfree(color_name);
for (i = 0; i < ncolors + 1; i++)
free(colors[i]);
free(colors);
wfree(colors[i]);
wfree(colors);
RDestroyContext(ctx);
RShutdown();

View File

@@ -31,6 +31,8 @@
#include <assert.h>
#include <WINGs/WUtil.h>
#ifdef USE_XSHM
#include <sys/ipc.h>
#include <sys/shm.h>
@@ -63,7 +65,7 @@ RXImage *RCreateXImage(RContext * context, int depth, unsigned width, unsigned h
RXImage *rximg;
Visual *visual = context->visual;
rximg = malloc(sizeof(RXImage));
rximg = wmalloc(sizeof(RXImage));
if (!rximg) {
RErrorCode = RERR_NOMEMORY;
return NULL;
@@ -71,14 +73,14 @@ RXImage *RCreateXImage(RContext * context, int depth, unsigned width, unsigned h
#ifndef USE_XSHM
rximg->image = XCreateImage(context->dpy, visual, depth, ZPixmap, 0, NULL, width, height, 8, 0);
if (!rximg->image) {
free(rximg);
wfree(rximg);
RErrorCode = RERR_XERROR;
return NULL;
}
rximg->image->data = malloc(rximg->image->bytes_per_line * height);
rximg->image->data = wmalloc(rximg->image->bytes_per_line * height);
if (!rximg->image->data) {
XDestroyImage(rximg->image);
free(rximg);
wfree(rximg);
RErrorCode = RERR_NOMEMORY;
return NULL;
}
@@ -90,14 +92,14 @@ RXImage *RCreateXImage(RContext * context, int depth, unsigned width, unsigned h
rximg->is_shared = 0;
rximg->image = XCreateImage(context->dpy, visual, depth, ZPixmap, 0, NULL, width, height, 8, 0);
if (!rximg->image) {
free(rximg);
wfree(rximg);
RErrorCode = RERR_XERROR;
return NULL;
}
rximg->image->data = malloc(rximg->image->bytes_per_line * height);
rximg->image->data = wmalloc(rximg->image->bytes_per_line * height);
if (!rximg->image->data) {
XDestroyImage(rximg->image);
free(rximg);
wfree(rximg);
RErrorCode = RERR_NOMEMORY;
return NULL;
}
@@ -173,7 +175,7 @@ void RDestroyXImage(RContext * context, RXImage * rximage)
XDestroyImage(rximage->image);
}
#endif
free(rximage);
wfree(rximage);
}
static unsigned getDepth(Display * dpy, Drawable d)
@@ -205,7 +207,7 @@ RXImage *RGetXImage(RContext * context, Drawable d, int x, int y, unsigned width
}
}
if (!ximg) {
ximg = malloc(sizeof(RXImage));
ximg = wmalloc(sizeof(RXImage));
if (!ximg) {
RErrorCode = RERR_NOMEMORY;
return NULL;
@@ -214,7 +216,7 @@ RXImage *RGetXImage(RContext * context, Drawable d, int x, int y, unsigned width
ximg->image = XGetImage(context->dpy, d, x, y, width, height, AllPlanes, ZPixmap);
}
#else /* !USE_XSHM */
ximg = malloc(sizeof(RXImage));
ximg = wmalloc(sizeof(RXImage));
if (!ximg) {
RErrorCode = RERR_NOMEMORY;
return NULL;
@@ -224,7 +226,7 @@ RXImage *RGetXImage(RContext * context, Drawable d, int x, int y, unsigned width
#endif /* !USE_XSHM */
if (ximg->image == NULL) {
free(ximg);
wfree(ximg);
return NULL;
}

View File

@@ -12,7 +12,6 @@ cc = "1.0"
[dependencies]
atomic-write-file = "0.3"
hashbrown = "0.16.0"
libc = "0.2.175"
nom = "8.0"
nom-language = "0.1"
x11 = "2.21.0"

View File

@@ -2,6 +2,7 @@ AUTOMAKE_OPTIONS =
RUST_SOURCES = \
src/array.rs \
src/bag.rs \
src/data.rs \
src/defines.c \
src/defines.rs \
@@ -9,7 +10,10 @@ RUST_SOURCES = \
src/hash_table.rs \
src/lib.rs \
src/memory.rs \
src/prop_list.rs
src/notification.rs \
src/prop_list.rs \
src/string.rs
src/tree.rs
RUST_EXTRA = \
Cargo.lock \

View File

@@ -1,7 +1,7 @@
use std::{ffi::c_void, ptr::NonNull};
use std::ffi::c_void;
pub struct Array {
items: Vec<NonNull<c_void>>,
items: Vec<usize>,
destructor: Option<unsafe extern "C" fn(x: *mut c_void)>,
}
@@ -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
}
@@ -204,10 +196,10 @@ pub mod ffi {
return ptr::null_mut();
}
unsafe {
(*array)
(&(*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 {

372
wutil-rs/src/bag.rs Normal file
View File

@@ -0,0 +1,372 @@
//! Simple sorted set.
//!
//! ## Rust rewrite notes
//!
//! This was originally a full-blown red-black tree, but it was really only used
//! to keep a sorted list. It has been rewritten as a sorted `Vec`. We
//! technically no longer have O(log(n)) insertion time. Given set sizes and
//! actual execution time, that shouldn't matter.
//!
//! Prefer a proper Rust collection over this for new code.
use std::{
ffi::{c_int, c_void},
ptr::NonNull,
};
#[derive(Default)]
pub struct Bag {
inner: Vec<(c_int, NonNull<c_void>)>,
}
/// Sentinel iterator value. In C, just check for negative values.
pub const NOT_FOUND: c_int = -1;
impl Bag {
/// Returns `Ok(index)` if an item keyed by `key` is in `self`, else
/// `Err(index)` if an item keyed by `key` could be inserted at `index`.
fn search(&self, key: c_int) -> Result<usize, usize> {
self.inner.binary_search_by_key(&key, |(key, _)| *key)
}
/// Sets a value associated with `key` to `value`. Clobbers any extant value.
fn set(&mut self, key: c_int, value: NonNull<c_void>) {
match self.search(key) {
Ok(i) => self.inner[i] = (key, value),
Err(i) => self.inner.insert(i, (key, value)),
}
}
}
pub mod ffi {
use super::{Bag, NOT_FOUND};
use std::{
ffi::{c_int, c_void},
ptr::{self, NonNull},
};
/// Basic constructor. Free with [`WMFreeBag`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateTreeBag() -> *mut Bag {
Box::leak(Box::new(Bag::default()))
}
/// Retrieves the value associated with `key`, or null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromBag(bag: *mut Bag, key: c_int) -> *mut c_void {
if bag.is_null() {
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
bag
.search(key)
.ok()
.and_then(|index| bag.inner.get(index).copied())
.map(|(_, value)| value.as_ptr())
.unwrap_or(ptr::null_mut())
}
/// Sets the value associated with `key` to `item`. If `item` is null,
/// removes any extant value.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSetInBag(bag: *mut Bag, key: c_int, item: *mut c_void) {
if bag.is_null() {
return;
}
let bag = unsafe { &mut *bag };
if let Some(item) = NonNull::new(item) {
bag.set(key, item);
} else {
if let Some(i) = bag.search(key).ok() {
bag.inner.remove(i);
}
}
}
/// Clears the contents of `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMEmptyBag(bag: *mut Bag) {
if bag.is_null() {
return;
}
unsafe {
(*bag).inner.clear();
}
}
/// Deletes `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFreeBag(bag: *mut Bag) {
if bag.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(bag);
}
}
/// Initializes `ptr` to the first element of `bag` and returns that
/// element. Sets `ptr` to [`NOT_FOUND`] and returns null if `bag` is empty.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagFirst(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if let Some((_, value)) = bag.inner.first() {
*ptr = 0;
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Initializes `ptr` to the last element of `bag` and returns that
/// element. Sets `ptr` to [`NOT_FOUND`] and returns null if `bag` is empty.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagLast(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if let Some((_, value)) = bag.inner.last() {
*ptr = (bag.inner.len() - 1) as c_int;
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Advances `ptr` to the next element of `bag` and returns it. Sets `ptr`
/// to [`NOT_FOUND`] and returns null if `ptr` is already at the end of
/// `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagNext(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if *ptr < 0 {
return ptr::null_mut();
}
*ptr += 1;
if let Some((_, value)) = bag.inner.get(*ptr as usize) {
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Decrements `ptr` to the previous element of `bag` and returns it. Sets `ptr`
/// to [`NOT_FOUND`] and returns null if `ptr` is already at the beginning of
/// `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagPrevious(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if *ptr <= 0 {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
*ptr -= 1;
if let Some((_, value)) = bag.inner.get(*ptr as usize) {
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Sets `ptr` to the element of `bag` with `key` and returns the associated
/// value. Sets `ptr` to [`NOT_FOUND`] and returns null if there is no such
/// element.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagIteratorAtIndex(
bag: *mut Bag,
key: c_int,
ptr: *mut c_int,
) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if key < 0 {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
if let Some(index) = bag.search(key).ok() {
*ptr = index as c_int;
bag.inner[index].1.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
}
#[cfg(test)]
mod test {
use super::{ffi, Bag, NOT_FOUND};
use std::{
ffi::{c_int, c_void},
ptr::NonNull,
};
fn void_of<T>(t: &mut T) -> NonNull<c_void> {
NonNull::new(t as *mut _ as *mut c_void).unwrap()
}
#[test]
fn insert_out_of_order() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
assert_eq!(
b.inner,
&[
(3, void_of(&mut y)),
(5, void_of(&mut x)),
(7, void_of(&mut z))
]
);
}
#[test]
fn iterate_forward() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagFirst(&mut b, &mut iterator),
void_of(&mut y).as_ptr()
);
assert_eq!(iterator, 0);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
assert!(ffi::WMBagNext(&mut b, &mut iterator).is_null());
assert_eq!(iterator, NOT_FOUND);
}
}
#[test]
fn iterate_backward() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagLast(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
assert_eq!(
ffi::WMBagPrevious(&mut b, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagPrevious(&mut b, &mut iterator),
void_of(&mut y).as_ptr()
);
assert_eq!(iterator, 0);
assert!(ffi::WMBagPrevious(&mut b, &mut iterator).is_null());
assert_eq!(iterator, NOT_FOUND);
}
}
#[test]
fn find_iterate() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagIteratorAtIndex(&mut b, 5, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
}
}
}

View File

@@ -1,15 +1,14 @@
use hashbrown::hash_map::{self, HashMap};
use std::{
borrow::Borrow,
ffi::{CStr, c_void},
ffi::CStr,
hash::{Hash, Hasher},
mem,
};
pub enum HashTable {
PointerKeyed(HashMap<VoidPointer, VoidPointer>),
StringKeyed(HashMap<StringPointer, VoidPointer>),
PointerKeyed(HashMap<*mut u8, *mut u8>),
StringKeyed(HashMap<StringKey, *mut u8>),
}
impl HashTable {
@@ -35,36 +34,34 @@ impl HashTable {
}
}
pub unsafe fn get(&self, key: *const i8) -> Option<*mut i8> {
pub unsafe fn get(&self, key: *const u8) -> Option<*mut u8> {
match self {
HashTable::PointerKeyed(m) => {
let key = key.cast_mut();
m.get(&key).map(|x| x.0)
m.get(&key).copied()
}
HashTable::StringKeyed(m) => {
let key = StringPointer(key.cast_mut());
let v = m.get(&key).map(|x| x.0);
mem::forget(key);
v
let key = StringKey(key.cast_mut());
m.get(&key).copied()
}
}
}
pub unsafe fn insert(&mut self, key: *mut i8, data: VoidPointer) -> Option<VoidPointer> {
pub unsafe fn insert(&mut self, key: *mut u8, data: *mut u8) -> Option<*mut u8> {
match self {
HashTable::PointerKeyed(m) => m.insert(VoidPointer(key), data),
HashTable::StringKeyed(m) => m.insert(StringPointer(key), data),
HashTable::PointerKeyed(m) => m.insert(key, data),
HashTable::StringKeyed(m) => m.insert(StringKey(key), data),
}
}
pub unsafe fn remove(&mut self, key: *const i8) {
pub unsafe fn remove(&mut self, key: *const u8) {
match self {
HashTable::PointerKeyed(m) => {
let key = key.cast_mut();
m.remove(&key);
}
HashTable::StringKeyed(m) => {
let key = StringPointer(key.cast_mut());
let key = StringKey(key.cast_mut());
m.remove(&key);
mem::forget(key);
}
@@ -72,60 +69,40 @@ impl HashTable {
}
}
#[derive(Debug, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct VoidPointer(*mut i8);
pub struct StringKey(*const u8);
impl Drop for VoidPointer {
fn drop(&mut self) {
unsafe { libc::free(self.0.cast::<c_void>()) }
}
}
impl Borrow<*mut i8> for VoidPointer {
fn borrow(&self) -> &*mut i8 {
&self.0
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct StringPointer(*mut i8);
impl PartialEq for StringPointer {
impl PartialEq for StringKey {
fn eq(&self, other: &Self) -> bool {
match (self.0.is_null(), other.0.is_null()) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => unsafe { CStr::from_ptr(self.0) == CStr::from_ptr(other.0) },
(false, false) => unsafe { CStr::from_ptr(self.0.cast()) == CStr::from_ptr(other.0.cast()) },
_ => false,
}
}
}
impl Eq for StringPointer {}
impl Eq for StringKey {}
impl Hash for StringPointer {
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<H: Hasher>(&self, h: &mut H) {
if self.0.is_null() {
h.write_usize(0)
} else {
unsafe { CStr::from_ptr(self.0).hash(h) }
}
}
}
impl Drop for StringPointer {
fn drop(&mut self) {
unsafe {
libc::free(self.0.cast::<c_void>());
unsafe { CStr::from_ptr(self.0.cast()).hash(h) }
}
}
}
pub enum Enumerator<'a> {
PointerKeyed(hash_map::IterMut<'a, VoidPointer, VoidPointer>),
StringKeyed(hash_map::IterMut<'a, StringPointer, VoidPointer>),
PointerKeyed(hash_map::IterMut<'a, *mut u8, *mut u8>),
StringKeyed(hash_map::IterMut<'a, StringKey, *mut u8>),
}
pub mod ffi {
@@ -134,7 +111,7 @@ pub mod ffi {
mem, ptr,
};
use super::{Enumerator, HashTable, StringPointer, VoidPointer};
use super::{Enumerator, HashTable, StringKey};
#[unsafe(no_mangle)]
#[allow(non_snake_case)]
@@ -182,7 +159,7 @@ pub mod ffi {
if table.is_null() {
return ptr::null_mut();
}
let key = key.cast::<i8>();
let key = key.cast::<u8>();
(unsafe { (*table).get(key) })
.map(|v| v.cast::<c_void>())
.unwrap_or(ptr::null_mut())
@@ -201,27 +178,25 @@ pub mod ffi {
let table = unsafe { &mut *table };
match table {
HashTable::PointerKeyed(m) => {
let key = VoidPointer(key.cast::<i8>().cast_mut());
let result = match m.get_key_value_mut(&key) {
let result = match m.get_key_value_mut(&key.cast::<u8>().cast_mut()) {
Some((k, v)) => {
unsafe {
*key_dest = k.0.cast::<c_void>();
*value_dest = v.0.cast::<c_void>();
*key_dest = k.cast::<c_void>();
*value_dest = v.cast::<c_void>();
}
1
}
None => 0,
};
mem::forget(key);
result
}
HashTable::StringKeyed(m) => {
let key = StringPointer(key.cast::<i8>().cast_mut());
let key = StringKey(key.cast::<u8>().cast_mut());
let result = match m.get_key_value_mut(&key) {
Some((k, v)) => {
unsafe {
*key_dest = k.0.cast::<c_void>();
*value_dest = v.0.cast::<c_void>();
*value_dest = v.cast::<c_void>();
}
1
}
@@ -243,12 +218,8 @@ pub mod ffi {
if table.is_null() {
return ptr::null_mut();
}
match unsafe { (*table).insert(key.cast::<i8>(), VoidPointer(data.cast::<i8>())) } {
Some(v) => {
let raw = v.0;
mem::forget(v);
raw.cast::<c_void>()
}
match unsafe { (*table).insert(key.cast::<u8>(), data.cast::<u8>()) } {
Some(v) => v.cast::<c_void>(),
None => ptr::null_mut(),
}
}
@@ -260,7 +231,7 @@ pub mod ffi {
return;
}
unsafe {
(*table).remove(key.cast::<i8>());
(*table).remove(key.cast::<u8>());
}
}
@@ -298,25 +269,25 @@ pub mod ffi {
let e = unsafe { &mut *e };
match e {
Enumerator::PointerKeyed(i) => match i.next() {
Some((_, v)) => v.0.cast::<c_void>(),
Some((_, v)) => v.cast::<c_void>(),
None => ptr::null_mut(),
},
Enumerator::StringKeyed(i) => match i.next() {
Some((_, v)) => v.0.cast::<c_void>(),
Some((_, v)) => v.cast::<c_void>(),
None => ptr::null_mut(),
},
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMNextHashEnumeratorKey(e: *mut Enumerator<'static>) -> *mut c_void {
pub unsafe extern "C" fn WMNextHashEnumeratorKey(e: *mut Enumerator<'static>) -> *const c_void {
if e.is_null() {
return ptr::null_mut();
}
let e = unsafe { &mut *e };
match e {
Enumerator::PointerKeyed(i) => match i.next() {
Some((k, _)) => k.0.cast::<c_void>(),
Some((k, _)) => k.cast::<c_void>(),
None => ptr::null_mut(),
},
Enumerator::StringKeyed(i) => match i.next() {
@@ -325,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);
}
}
}
}

View File

@@ -1,7 +1,11 @@
pub mod array;
pub mod bag;
pub mod data;
pub mod defines;
pub mod find_file;
pub mod hash_table;
pub mod memory;
pub mod notification;
pub mod prop_list;
pub mod string;
pub mod tree;

View File

@@ -114,7 +114,7 @@ pub mod ffi {
use std::{ffi::c_void, ptr};
/// Allocates `size` bytes. Returns null if `size` is 0. Data will be
/// initialized but have an arbitrary value.
/// initialized to 0.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wmalloc(size: usize) -> *mut c_void {
alloc_bytes(size).cast::<c_void>()

View File

@@ -0,0 +1,360 @@
use std::{
collections::{btree_map::Entry, BTreeMap},
ffi::{c_void, CStr},
ptr::{self, NonNull},
sync::Mutex,
};
// Helper function for adding the entry `(key, (observer, action))` to `map`.
fn register<K: Eq + Ord>(
map: &mut BTreeMap<K, Vec<(Option<Sendable>, Action)>>,
key: K,
observer: Option<Sendable>,
action: Action,
) {
match map.entry(key) {
Entry::Occupied(mut o) => {
o.get_mut().push((observer, action));
}
Entry::Vacant(v) => {
v.insert(vec![(observer, action)]);
}
}
}
/// Lightweight message from object to another. When a notification is sent,
/// registered [`Action`]s are called.
///
/// Use [`ffi::WMAddNotificationObserver`] or [`NotificationCenter::register`]
/// to request notifications.
///
/// ## Safety
///
/// `Notification` encapsulates two data pointers. The Rust implementation
/// explicitly supports notifications across threads. To uphold Rust's safety
/// rules, `Notification`s must only be created with pointers that can be sent
/// across threads. The [`Sendable`] struct is provided to guarantee this.
///
/// ## Rust rewrite notes
///
/// This was originally a reference-counted structure, but it is so lightweight
/// (consisting of three pointers) that the Rust version is `Copy`. This
/// simplifies things --- each Rust recipient of a `Notification` owns it, and
/// there is no need to coordinate how it is cleaned up.
///
/// In unported C code, a notificaton's `name` may be compared against a string
/// constant using pointer equality rather than string equality. This has
/// negative implications for sending notifications across the Rust/C
/// boundary. For the time being, notifications are generated and received by
/// code written in C, but care should be taken that notification names are
/// checked properly when porting notification registration in the future. (We
/// will ideally move to a different data type for identifying notifications,
/// too.)
#[derive(Clone, Copy)]
pub struct Notification {
name: &'static CStr,
/// The object that generated the notification. This may be `None` for
/// notifications that are about global state.
source: Option<Sendable>,
/// Optional side-channel data provided to notification listeners.
client_data: Option<Sendable>,
}
/// Callback that notifies `observer` (which may be null) of `notification` (which won't be).
pub type Action = unsafe extern "C" fn(observer: *mut c_void, notification: *const Notification);
/// Wraps a type-erased pointer (which it does not own) and marks it as `Send`.
///
/// The `Send`-ability of the wrapped pointer must be guaranteed by code that
/// instantiates a `Sendable`.
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct Sendable {
ptr: NonNull<c_void>,
}
impl Sendable {
/// Creates a `Sendable` wrapping `ptr`.
///
/// ## Safety
///
/// `ptr` must be safe to send across threads.
pub unsafe fn new(ptr: NonNull<c_void>) -> Self {
Sendable { ptr }
}
}
// Guaranteed by `Sendable::new`.
unsafe impl Send for Sendable {}
pub struct NotificationCenter {
/// Notification subscriptions that match on name and source.
exact: BTreeMap<(&'static CStr, Sendable), Vec<(Option<Sendable>, Action)>>,
/// Notification subscriptions that match on name.
by_name: BTreeMap<&'static CStr, Vec<(Option<Sendable>, Action)>>,
/// Notification subscriptions that match on source.
by_source: BTreeMap<Sendable, Vec<(Option<Sendable>, Action)>>,
/// Notification subscriptions that match all notifications.
universal: Vec<(Option<Sendable>, Action)>,
}
// It is safe to send NotificationCenter across threads as long as the contract
// on Sendable is respected.
unsafe impl Send for NotificationCenter {}
impl NotificationCenter {
/// Creates a new `NotificationCenter`.
pub const fn new() -> Self {
NotificationCenter {
exact: BTreeMap::new(),
by_name: BTreeMap::new(),
by_source: BTreeMap::new(),
universal: Vec::new(),
}
}
/// Provides access to the default, process-wide notification center. The
/// FFI C API uses this notification center. This is protected behind a
/// mutex that is held while `f` is run, so panicking inside of `f` should
/// be avoided.
pub fn with_global_default<R>(f: impl FnOnce(&mut Self) -> R) -> R {
static INSTANCE: Mutex<NotificationCenter> = Mutex::new(NotificationCenter::new());
f(&mut INSTANCE
.try_lock()
.unwrap())
}
/// Registers `action` to be invoked and invoked on `observer` when
/// notifications named `name` are fired from `source`.
pub fn register_exact(
&mut self,
name: &'static CStr,
source: Sendable,
observer: Option<Sendable>,
action: Action,
) {
register(&mut self.exact, (name, source), observer, action);
}
/// Registers `action` to be invoked on `observer` when notifications are
/// fired by `source` (regardless of the notification name).
pub fn register_by_source(
&mut self,
source: Sendable,
observer: Option<Sendable>,
action: Action,
) {
register(&mut self.by_source, source, observer, action);
}
/// Registers `action` to be invoked on `observer` for all notifications
/// named `name`.
pub fn register_by_name(
&mut self,
name: &'static CStr,
observer: Option<Sendable>,
action: Action,
) {
register(&mut self.by_name, name, observer, action);
}
/// Registers `action` to be invoked on `observer` for all notifications,
/// regardless of the notification's name or source.
pub fn register_universal(&mut self, observer: Option<Sendable>, action: Action) {
self.universal.push((observer, action));
}
/// Dispatches `notification` with registered actions.
pub fn dispatch(&mut self, notification: Notification) {
if let Some(observers) = self.by_name.get_mut(notification.name) {
for (observer, action) in observers {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
if let Some(source) = notification.source {
if let Some(observers) = self.exact.get_mut(&(notification.name, source)) {
for (observer, action) in observers {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
if let Some(observers) = self.by_source.get_mut(&source) {
for (observer, action) in observers {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
}
for (observer, action) in &mut self.universal {
let observer = observer.map(|x| x.ptr.as_ptr()).unwrap_or(ptr::null_mut());
unsafe {
(action)(observer, &notification);
}
}
}
/// Removes all notification subscriptions that would notify `observer` if they fired.
pub fn remove_observer(&mut self, observer: Sendable) {
self.exact.retain(|_, values| {
values.retain(|(o, _)| *o != Some(observer));
!values.is_empty()
});
self.by_name.retain(|_, values| {
values.retain(|(o, _)| *o != Some(observer));
!values.is_empty()
});
self.by_source.retain(|_, values| {
values.retain(|(o, _)| *o != Some(observer));
!values.is_empty()
});
self.universal.retain(|(o, _)| *o != Some(observer));
}
/// Clears all registered notification listeners and resets `self` to its
/// default state.
pub fn clear(&mut self) {
*self = Self::new();
}
}
pub mod ffi {
use super::{Action, Notification, NotificationCenter, Sendable};
use std::{
ffi::{c_char, c_void, CStr},
ptr::{self, NonNull},
};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetNotificationClientData(
notification: *mut Notification,
) -> *mut c_void {
if notification.is_null() {
return ptr::null_mut();
}
unsafe {
(*notification)
.client_data
.map(|x| x.ptr.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetNotificationObject(
notification: *mut Notification,
) -> *mut c_void {
if notification.is_null() {
return ptr::null_mut();
}
unsafe {
(*notification)
.source
.map(|x| x.ptr.as_ptr())
.unwrap_or(ptr::null_mut())
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetNotificationName(
notification: *mut Notification,
) -> *const c_char {
if notification.is_null() {
return ptr::null_mut();
}
unsafe { (*notification).name.as_ptr() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMAddNotificationObserver(
action: Option<Action>,
observer: *mut c_void,
name: *const c_char,
object: *mut c_void,
) {
let Some(action) = action else {
return;
};
let observer = NonNull::new(observer).map(|x| unsafe { Sendable::new(x) });
let source = NonNull::new(object);
NotificationCenter::with_global_default(|c| {
if name.is_null() {
match source {
Some(source) => {
c.register_by_source(unsafe { Sendable::new(source) }, observer, action);
}
None => c.register_universal(observer, action),
}
} else {
let name = unsafe { CStr::from_ptr(name) };
match source {
Some(source) => {
c.register_exact(name, unsafe { Sendable::new(source) }, observer, action);
}
None => c.register_by_name(name, observer, action),
}
}
});
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMRemoveNotificationObserver(observer: *mut c_void) {
let Some(observer) = NonNull::new(observer) else {
return;
};
NotificationCenter::with_global_default(|c| {
c.remove_observer(unsafe { Sendable::new(observer) })
});
}
/// Posts a notification from `object` with the given `name` and `client_data`.
///
/// ## Safety
///
/// `name` must be a non-null string constant or some other pointer with a
/// static lifetime.
///
/// `object` and `client_data` must be safe to send across threads (per the
/// contract of [`Sendable`]).
///
/// ## Rust rewrite notes
///
/// This originally took a heap-allocated `*mut Notification`, but now the
/// constructed `Notification` parameters are passed directly.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMPostNotificationName(
name: *const c_char,
object: *mut c_void,
client_data: *mut c_void,
) {
if name.is_null() {
return;
}
let name = unsafe { CStr::from_ptr(name) };
let source = NonNull::new(object).map(|x| unsafe { Sendable::new(x) });
let client_data = NonNull::new(client_data).map(|x| unsafe { Sendable::new(x) });
NotificationCenter::with_global_default(|c| {
c.dispatch(Notification {
name,
source,
client_data,
})
});
}
/// Resets all notifications for the global notification center. Used by
/// `WApplication` teardown code. This is a private, WINGs-internal API.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn W_ClearNotificationCenter() {
NotificationCenter::with_global_default(|c| c.clear());
}
}

439
wutil-rs/src/string.rs Normal file
View File

@@ -0,0 +1,439 @@
//! String manipulation utilities.
//!
//! ## Rust rewrite notes
//!
//! These are more or less bug-for-bug reimplementations of the original WUtils
//! functions. Avoid using these functions in new code.
use std::{
ffi::{c_char, c_int, CStr},
mem, ptr, slice,
};
use crate::memory::{alloc_bytes, alloc_string, ffi::wrealloc, free_bytes};
/// Returns a `wmalloc`-managed C-style string that duplicates `s`, which cannot
/// be null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrdup(s: *const c_char) -> *mut c_char {
assert!(!s.is_null());
alloc_string(unsafe { CStr::from_ptr(s) })
}
/// Returns a `wmalloc`-managed C-style string of the first `len` bytes of `s`,
/// which cannot be null.
///
/// If `len` exceeds the length of `s`, uses the lesser value.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrndup(s: *const c_char, len: usize) -> *mut c_char {
assert!(!s.is_null());
let len = unsafe {
slice::from_raw_parts(s, len)
.into_iter()
.position(|p| *p == 0)
.unwrap_or(len)
};
let copy: *mut c_char = alloc_bytes(len + 1).cast(); // Implicitly zeroed.
unsafe {
ptr::copy_nonoverlapping(s, copy, len);
}
copy.cast::<c_char>()
}
/// Concatenates `s1` and `s2` into a `wmalloc`-managed C-style string.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrconcat(s1: *const c_char, s2: *const c_char) -> *mut c_char {
match (s1.is_null(), s2.is_null()) {
(true, true) => ptr::null_mut(),
(true, false) => unsafe { wstrdup(s1) },
(false, true) => unsafe { wstrdup(s2) },
(false, false) => unsafe {
let s1 = CStr::from_ptr(s1);
let l1 = s1.count_bytes();
let s2 = CStr::from_ptr(s2);
let l2 = s2.count_bytes();
let s: *mut c_char = alloc_bytes(l1 + l2 + 1).cast(); // Implicitly zeroed.
ptr::copy_nonoverlapping(s1.as_ptr(), s, l1);
ptr::copy_nonoverlapping(s2.as_ptr(), s.offset(l1 as isize), l2);
s
},
}
}
/// Appends `src` to `dest`, destructively reallocating `dest` and returning a
/// new `wmalloc`-managed pointer where the result is stored. `dst` must come
/// from `wmalloc` or `wrealloc`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wstrappend(dst: *mut c_char, src: *const c_char) -> *mut c_char {
if src.is_null() {
return dst;
}
let src = unsafe { CStr::from_ptr(src) };
let src_len = src.count_bytes();
if src_len == 0 {
return dst;
}
if dst.is_null() {
return unsafe { wstrdup(src.as_ptr()) };
}
let dst_len = unsafe { CStr::from_ptr(dst).count_bytes() + 1 };
let len = dst_len + src_len + 1;
let result: *mut c_char = unsafe { wrealloc(dst.cast(), len).cast() };
unsafe {
ptr::copy_nonoverlapping(
src.as_ptr(),
result.offset(dst_len.try_into().unwrap()),
src_len,
);
}
result
}
/// Strips leading and trailing whitespace from `s`, returning a
/// `wmalloc`-managed C-style string holding the result.
///
/// ## Rust rewite notes
///
/// This uses a slightly different notion of "space character" than the original
/// C implementation did. `s` must be valid UTF-8.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtrimspace(s: *const c_char) -> *mut c_char {
if s.is_null() {
return ptr::null_mut();
}
let Ok(s) = (unsafe { CStr::from_ptr(s).to_str() }) else {
// TODO: complain.
return ptr::null_mut();
};
let trimmed = s.trim();
let ptr = trimmed.as_ptr();
let len = trimmed.len();
unsafe { wstrndup(ptr, len) }
}
/// Splits `command` into tokens with approximately the same rules as used in
/// the shell for splitting up a command into program arguments.
fn tokensplit(command: &[u8]) -> Vec<Vec<u8>> {
enum Mode {
Start,
Unquoted(Vec<u8>),
Escape(Context),
Quoted { token: Vec<u8>, delimiter: u8, },
}
enum Context {
Unquoted(Vec<u8>),
Quoted { token: Vec<u8>, delimiter: u8, },
}
let mut out = Vec::new();
let mut mode = Mode::Start;
for &b in command {
mode = match (mode, b) {
(Mode::Start, b'\\') => Mode::Escape(Context::Unquoted(vec![])),
(Mode::Start, b'\'' | b'"') => Mode::Quoted { token: vec![], delimiter: b },
(Mode::Start, b' ' | b'\t') => Mode::Start,
(Mode::Start, _) => Mode::Unquoted(vec![b]),
(Mode::Unquoted(token), b'\\') => Mode::Escape(Context::Unquoted(token)),
(Mode::Unquoted(token), b'\'' | b'"') => Mode::Quoted { token, delimiter: b },
(Mode::Unquoted(token), b' ' | b'\t') => {
out.push(token);
Mode::Start
}
(Mode::Unquoted(mut token), _) => {
token.push(b);
Mode::Unquoted(token)
}
(Mode::Escape(Context::Unquoted(mut token)), _) => {
token.push(b);
Mode::Unquoted(token)
}
(Mode::Escape(Context::Quoted { mut token, delimiter }), _) => {
token.push(b);
Mode::Quoted { token, delimiter }
}
(Mode::Quoted { token, delimiter }, _) if b == delimiter => {
Mode::Unquoted(token)
}
(Mode::Quoted { token, delimiter }, _) if b == b'\\' => {
Mode::Escape(Context::Quoted { token, delimiter })
}
(Mode::Quoted { mut token, delimiter }, _) => {
token.push(b);
Mode::Quoted { token, delimiter }
}
}
}
match mode {
Mode::Start => (),
Mode::Unquoted(token) => out.push(token),
Mode::Quoted { token, .. } => out.push(token),
Mode::Escape(Context::Unquoted(token)) => out.push(token),
Mode::Escape(Context::Quoted { token, .. }) => out.push(token),
}
out
}
/// Splits `command` into tokens, storing the number of tokens in `argc` and
/// `wmalloc`-managed C-style strings for each token in `argv`. Call
/// [`wtokenfree`] to free `argv`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtokensplit(
command: *const c_char,
argv: *mut *mut *mut c_char,
argc: *mut c_int,
) {
if argv.is_null() || argc.is_null() {
return;
}
unsafe {
*argv = ptr::null_mut();
*argc = 0;
}
if command.is_null() {
return;
}
let command = unsafe { CStr::from_ptr(command) };
let Ok(command) = command.to_str() else {
return;
};
let tokens = tokensplit(command.as_bytes());
if tokens.is_empty() {
return;
}
let argv = unsafe {
*argv = alloc_bytes(mem::size_of::<*mut c_char>() * tokens.len()).cast::<*mut c_char>();
*argc = tokens.len() as c_int;
slice::from_raw_parts_mut(*argv, tokens.len())
};
for (dest, mut token) in argv.iter_mut().zip(tokens.into_iter()) {
token.push(b'\0');
*dest = alloc_string(unsafe { CStr::from_bytes_with_nul_unchecked(&token) });
}
}
/// Frees an `argv` populated by [`wtokensplit`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtokenfree(tokens: *mut *mut c_char, count: c_int) {
if tokens.is_null() {
return;
}
if count > 0 {
let tokens = unsafe { slice::from_raw_parts_mut(tokens, count as usize) };
for token in tokens {
unsafe {
free_bytes(*token);
}
}
}
unsafe {
free_bytes(tokens.cast::<u8>());
}
}
/// Joins the tokens of the `count` elements of `list` into a single string,
/// enclosing them in double quotes (`"`) if necessary. Returns a
/// `wmalloc`-managed C-style string with the result, or null on failure.
///
/// ## Rust rewrite notes
///
/// `list` must consist of valid UTF-8 strings. This reimplementation does not
/// attempt to improve on the original, so it has bad failure modes (like not
/// escaping quotes in the input). Do not use this function in new code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wtokenjoin(list: *const *const char, count: c_int) -> *mut c_char {
if list.is_null() || count <= 0 {
return alloc_string(c"");
}
let list = unsafe {
slice::from_raw_parts(list.cast::<*const u8>(), count as usize)
};
let mut buffer = Vec::new();
for term in list {
if term.is_null() {
continue;
}
let term = unsafe { CStr::from_ptr(*term) };
if term.is_empty() {
continue;
}
if !buffer.is_empty() {
buffer.push(b' ');
}
let term = term.to_bytes();
if term.iter().find(|&&x| x == b' ' || x == b'\t').is_some() {
buffer.push(b'"');
buffer.extend_from_slice(term);
buffer.push(b'"');
} else {
buffer.extend_from_slice(term);
}
}
buffer.push(b'\0');
if let Ok(buffer) = CStr::from_bytes_until_nul(&buffer) {
alloc_string(buffer)
} else {
return ptr::null_mut();
}
}
#[cfg(test)]
mod test {
use super::{wtokenfree, wtokensplit, wtokenjoin};
use std::{ffi::{c_char, c_int, CStr}, ptr, slice};
#[test]
fn split_empty_whitespace() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c" ".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c" \t ".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c" \t ".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
unsafe { wtokensplit(c"\t\t".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 0);
assert_eq!(argv, ptr::null_mut());
unsafe { wtokenfree(argv, argc); }
}
fn args_of(argv: *mut *mut u8, argc: usize) -> Vec<String> {
let mut v = Vec::with_capacity(argc);
for s in unsafe { slice::from_raw_parts(argv, argc) } {
if s.is_null() {
return v;
}
v.push(String::from(unsafe { CStr::from_ptr(*s) }.to_str().unwrap()));
}
v
}
#[test]
fn split_empty_quoted() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"\"\"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
unsafe { wtokenfree(argv, argc); }
argv = ptr::null_mut();
argc = 0;
unsafe { wtokensplit(c"''".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
unsafe { wtokenfree(argv, argc); }
argv = ptr::null_mut();
argc = 0;
unsafe { wtokensplit(c" ''\t".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("")]);
unsafe { wtokenfree(argv, argc); }
}
#[test]
fn split_one_unquoted() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"hello".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello")]);
unsafe { wtokensplit(c"hello\\\\".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello\\")]);
}
#[test]
fn split_one_quoted() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe { wtokensplit(c"\"hello\"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello")]);
unsafe { wtokensplit(c"\"hello world\"".as_ptr(), &mut argv, &mut argc); }
assert_eq!(argc, 1);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello world")]);
}
#[test]
fn split_multi() {
let mut argv = ptr::null_mut();
let mut argc = 0;
unsafe {
wtokensplit(
c"\"hello world\" what\\'s happening' here it\\'s weird'".as_ptr(),
&mut argv,
&mut argc,
);
}
assert_eq!(argc, 3);
assert_eq!(args_of(argv, argc as usize), vec![String::from("hello world"),
String::from("what's"),
String::from("happening here it's weird")]);
}
/// Calls `f(wtokenjoin(list))` transparently.
fn with_list<R>(list: &[&CStr], f: impl FnOnce(Option<&CStr>) -> R) -> R {
let list: Vec<_> = list.iter().map(|s| s.as_ptr().cast::<c_char>()).collect();
let joined = unsafe { wtokenjoin(list.as_slice().as_ptr().cast(), list.len() as c_int) };
let result = f(if joined.is_null() { None } else { unsafe { Some(CStr::from_ptr(joined)) } });
unsafe { crate::memory::free_bytes(joined.cast()); }
result
}
#[test]
fn join_nothing() {
with_list(&[], |joined| {
assert_eq!(joined.unwrap(), c"");
});
with_list(&[c"", c""], |joined| {
assert_eq!(joined.unwrap(), c"");
});
}
#[test]
fn join_basic() {
with_list(&[c"hello", c"world"], |joined| {
assert_eq!(joined.unwrap(), c"hello world");
});
}
#[test]
fn join_quoted() {
with_list(&[c"hello", c"there world"], |joined| {
assert_eq!(joined.unwrap(), c"hello \"there world\"");
});
}
}

252
wutil-rs/src/tree.rs Normal file
View File

@@ -0,0 +1,252 @@
//! N-ary tree structure.
//!
//! ## Rust rewrite notes
//!
//! This is only used in one place (`util/wmmenugen.c`), and it should probably
//! be moved out of wutil-rs (if not deleted entirely) once we migrate that
//! utility.
//!
//! The FFI functions provided here assume that they will be used as they are in
//! `wmmenugen.c`. (For example, the C interface may allow for some callback
//! parameters to be null, but they aren't in practice, so the Rust layer
//! doesn't check for that.)
//!
//! The original C library had a larger surface area and tracked parent
//! pointers, but the Rust version only has the API used by wmmenugen. Case in
//! point: `WMTreeNode` originally had an optional destructor function pointer
//! for freeing its `data` field, but wmmenugen never actually deleted a tree
//! node, so the destructor was never called. This Rust rewrite doesn't track a
//! `data` destructor and doesn't even provide a way to delete tree nodes.
//!
//! If a generic tree really becomes necessary, [`Tree`] may be adapted, but a
//! different design is probably warranted. (At the very least, `Tree` should be
//! generic over the type of `data` so that we're not using `c_void`. It is also
//! probably better to index into an owned `Vec` or use an external
//! arena. `GhostCell` or one of its ilk may even make sense.)
use std::ffi::c_void;
pub struct Tree {
depth: u32,
children: Vec<Box<Tree>>,
data: *mut c_void,
}
pub mod ffi {
use super::Tree;
use std::{
ffi::{c_int, c_void},
ptr,
};
/// Creates the root of a new tree with the data `data`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateTreeNode(data: *mut c_void) -> *mut Tree {
Box::leak(Box::new(Tree {
depth: 0,
children: Vec::new(),
data,
}))
}
/// Creates a tree node as the `index`th child of `parent`, with the data
/// `item`. If `index` is negative, the node is added as the last child of
/// `parent`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMInsertItemInTree(
parent: *mut Tree,
index: c_int,
item: *mut c_void,
) -> *mut Tree {
if parent.is_null() {
return ptr::null_mut();
}
let parent = unsafe { &mut *parent };
let child = Tree {
depth: parent.depth + 1,
children: Vec::new(),
data: item,
};
if index < 0 {
parent.children.push(Box::new(child));
parent.children.last_mut().unwrap().as_mut() as *mut _
} else {
let index = index as usize;
parent.children.insert(index, Box::new(child));
parent.children[index].as_mut() as *mut _
}
}
/// Inserts `tree` as the `index`th child of `parent`. If `index` is
/// negative, `tree` is added as the last child of `parent`.
///
/// This eagerly updates the depth of the entire subtree rooted at `tree`,
/// so it has a O(N) cost rather than O(1).
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMInsertNodeInTree(
parent: *mut Tree,
index: c_int,
tree: *mut Tree,
) -> *mut Tree {
if parent.is_null() {
return ptr::null_mut();
}
let parent = unsafe { &mut *parent };
if tree.is_null() {
return ptr::null_mut();
}
let mut stack = vec![(unsafe { &mut *tree }, parent.depth + 1)];
while let Some((tree, depth)) = stack.pop() {
tree.depth = depth;
for child in &mut tree.children {
stack.push((child, depth + 1));
}
}
if index < 0 {
parent.children.push(unsafe { Box::from_raw(tree) });
tree
} else {
let index = index as usize;
parent
.children
.insert(index, unsafe { Box::from_raw(tree) });
tree
}
}
/// Returns the data field of `tree`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetDataForTreeNode(tree: *mut Tree) -> *mut c_void {
if tree.is_null() {
return ptr::null_mut();
}
unsafe { (*tree).data }
}
/// Returns the depth of `tree` (0 if `tree` is a root, 1 if it is an
/// immediate child of the root, etc.). This is an `O(1)` operation.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetTreeNodeDepth(tree: *mut Tree) -> c_int {
if tree.is_null() {
return 0;
}
unsafe { (*tree).depth as c_int }
}
/// Recursively sorts the children of each node of the subtree rooted at
/// `tree`, according to `comparer`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSortTree(
tree: *mut Tree,
comparer: unsafe extern "C" fn(*const Tree, *const Tree) -> c_int,
) {
use std::cmp::Ordering;
if tree.is_null() {
return;
}
let comparer = |a: &Box<Tree>, b: &Box<Tree>| {
let a = a.as_ref() as *const Tree as *const _;
let b = b.as_ref() as *const Tree as *const _;
match unsafe { comparer(a, b) }.signum() {
-1 => Ordering::Less,
0 => Ordering::Equal,
1 => Ordering::Greater,
_ => unreachable!(),
}
};
let mut stack = vec![unsafe { &mut *tree }];
while let Some(tree) = stack.pop() {
tree.children.sort_by(comparer);
stack.extend(tree.children.iter_mut().map(|c| c.as_mut()));
}
}
/// Returns the first tree node in the subtree rooted at `tree` that matches
/// `match_p`, or null if none is found. If `match_p` is `None`, returns
/// the first node whose `data` field is equal to `cdata`. Search is
/// recursive, up to `limit` depth (which must be non-negative).
///
/// ## Rust rewrite notes
///
/// This was originally a DFS with a `depthFirst` parameter which actually
/// controlled whether tree traversal was pre- or post-order (and not
/// whether the search was depth-first or breadth-first, which the name
/// might seem to suggest). Since this was only ever called with a non-zero
/// `depthFirst`, the Rust version is hard-coded as a pre-order DFS.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFindInTreeWithDepthLimit(
tree: *mut Tree,
match_p: Option<unsafe extern "C" fn(*const Tree, *const c_void) -> c_int>,
cdata: *mut c_void,
limit: c_int,
) -> *mut Tree {
if tree.is_null() {
return ptr::null_mut();
}
let safe_tree = unsafe { &mut *tree };
let match_p = |t: &Tree| -> bool {
if let Some(p) = match_p {
(unsafe { (p)(t, cdata) }) != 0
} else {
t.data == cdata
}
};
let mut stack = vec![(safe_tree, 0)];
while let Some((tree, depth)) = stack.pop() {
if depth > limit {
continue;
}
if match_p(tree) {
return tree as *mut Tree;
}
for child in tree.children.iter_mut() {
stack.push((child.as_mut(), depth + 1));
}
}
ptr::null_mut()
}
/// Traverses `tree` recursively, invoking `walk` on each tree node with
/// `cdata` passed in to provide a side channel.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMTreeWalk(
tree: *mut Tree,
walk: unsafe extern "C" fn(*mut Tree, *mut c_void),
cdata: *mut c_void,
) {
if tree.is_null() {
return;
}
let tree = unsafe { &mut *tree };
let walk_fn = |t: &mut Tree| unsafe {
walk(t as *mut Tree, cdata);
};
let mut stack = vec![tree];
while let Some(tree) = stack.pop() {
walk_fn(tree);
for child in &mut tree.children {
stack.push(child);
}
}
}
}