24 Commits

Author SHA1 Message Date
fa67563c2b Break out Sendable into its own utility module. 2025-12-20 13:09:32 -05:00
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
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
33 changed files with 1002 additions and 572 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@
@@ -41,7 +41,6 @@ libWINGs_la_SOURCES = \
wevent.c \
wfilepanel.c \
wframe.c \
wfont.c \
wfontpanel.c \
widgets.c \
winputmethod.c \

View File

@@ -778,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);
@@ -792,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);
@@ -815,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,6 +378,8 @@ void W_InitNotificationCenter(void);
void W_ReleaseNotificationCenter(void);
void W_ClearNotificationCenter(void);
/* ---[ selection.c ]----------------------------------------------------- */
@@ -441,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
@@ -504,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 {
@@ -329,7 +324,7 @@ void WMDeleteInputHandler(WMHandlerID handlerID);
void WHandleEvents(void);
/* ---[ WINGs/hashtable.c ]----------------------------------------------- */
/* ---[ wutil-rs/src/hash_table.rs ]----------------------------------------------- */
WMHashTable* WMCreateIdentityHashTable();
@@ -361,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);
@@ -376,7 +371,7 @@ Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator *enumerator,
void **item, void **key);
void WMFreeHashEnumerator(WMHashEnumerator *enumerator);
/* some predefined callback sets */

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

@@ -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,7 +551,8 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel)
enumer = WMEnumerateHashTable(families);
while ((array = WMNextHashEnumeratorItem(&enumer))) {
while ((array = WMNextHashEnumeratorItem(enumer))) {
printf("listFamilies: got family with %d items\n", WMGetArrayItemCount(array));
WMArrayIterator i;
Family *fam;
char buffer[256];

View File

@@ -629,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);
@@ -909,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

@@ -718,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);
@@ -897,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;
@@ -979,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;
@@ -1378,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)
@@ -1430,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,8 +68,10 @@ WPrefs_DEPENDENCIES = $(top_builddir)/WINGs/libWINGs.la
WPrefs_LDADD = \
$(top_builddir)/wutil-rs/target/debug/libwutil_rs.a\
$(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

@@ -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 ==========================
@@ -924,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

@@ -89,6 +89,8 @@ Xephyr -screen 640x480 "$xephyr_display" &
xephyr_pid=$!
DISPLAY="$xephyr_display" gdb \
--directory "$project_base" \
--directory "$HOME/src/libX11-1.5.0/build/src" \
--quiet \
--fullname \
--args "$WindowMaker" -display "$xephyr_display" --for-real "$@"
kill $xephyr_pid

View File

@@ -67,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 \
@@ -100,6 +102,7 @@ wmiv_LDADD = \
$(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

@@ -12,7 +12,8 @@ RUST_SOURCES = \
src/memory.rs \
src/notification.rs \
src/prop_list.rs \
src/string.rs
src/sendable.rs \
src/string.rs \
src/tree.rs
RUST_EXTRA = \

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
}
@@ -207,7 +199,7 @@ pub mod ffi {
(&(*array))
.items
.get(index as usize)
.map(|p| p.as_ptr())
.map(|p| *p as *mut c_void)
.unwrap_or(ptr::null_mut())
}
}
@@ -226,7 +218,7 @@ pub mod ffi {
(*array)
.items
.pop()
.map(|p| p.as_ptr())
.map(|p| p as *mut c_void)
.unwrap_or(ptr::null_mut())
}
}
@@ -246,7 +238,7 @@ pub mod ffi {
.items
.iter()
.enumerate()
.find(|(_, item)| unsafe { f(item.as_ptr(), cdata) != 0 })
.find(|(_, item)| unsafe { f(**item as *const c_void, cdata) != 0 })
.map(|(i, _)| i as c_int)
.unwrap_or(NOT_FOUND)
} else {
@@ -254,7 +246,7 @@ pub mod ffi {
.items
.iter()
.enumerate()
.find(|(_, item)| ptr::eq(item.as_ptr(), cdata))
.find(|(_, item)| ptr::eq(**item as *const c_void, cdata))
.map(|(i, _)| i as c_int)
.unwrap_or(NOT_FOUND)
}
@@ -269,7 +261,7 @@ pub mod ffi {
array
.items
.iter()
.filter(|x| ptr::eq(x.as_ptr(), item))
.filter(|x| ptr::eq(**x as *const c_void, item))
.count() as c_int
}
@@ -284,13 +276,17 @@ pub mod ffi {
unsafe {
(*array)
.items
.sort_by(|&a, &b| match comparator(a.as_ptr(), b.as_ptr()).signum() {
-1 => std::cmp::Ordering::Less,
0 => std::cmp::Ordering::Equal,
1 => std::cmp::Ordering::Greater,
_ => unreachable!(),
.sort_by(|a, b| {
let a = a as *const _ as *const c_void;
let b = b as *const _ as *const c_void;
match comparator(a, b).signum() {
-1 => std::cmp::Ordering::Less,
0 => std::cmp::Ordering::Equal,
1 => std::cmp::Ordering::Greater,
_ => unreachable!(),
}
})
}
}
}
#[unsafe(no_mangle)]
@@ -304,7 +300,7 @@ pub mod ffi {
}
unsafe {
for a in &mut (*array).items {
(f)(a.as_ptr(), data);
(f)(*a as *mut c_void, data);
}
}
}
@@ -326,7 +322,7 @@ pub mod ffi {
unsafe {
*iter = 0;
}
x.as_ptr()
*x as *mut c_void
}
}
}
@@ -348,7 +344,7 @@ pub mod ffi {
unsafe {
*iter = (array.items.len() - 1) as c_int;
}
x.as_ptr()
*x as *mut c_void
}
}
}
@@ -368,7 +364,7 @@ pub mod ffi {
unsafe {
*iter += 1;
}
i.as_ptr()
*i as *mut c_void
}
None => {
unsafe {
@@ -394,7 +390,7 @@ pub mod ffi {
unsafe {
*iter -= 1;
}
i.as_ptr()
*i as *mut c_void
}
None => {
unsafe {

View File

@@ -69,7 +69,6 @@ impl HashTable {
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct StringKey(*const u8);
@@ -85,6 +84,12 @@ impl PartialEq for StringKey {
impl Eq for StringKey {}
impl std::fmt::Debug for StringKey {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "StringKey({:?})", unsafe { std::ffi::CStr::from_ptr(self.0) })
}
}
impl Hash for StringKey {
fn hash<H: Hasher>(&self, h: &mut H) {
if self.0.is_null() {
@@ -291,4 +296,43 @@ pub mod ffi {
},
}
}
#[cfg(test)]
mod test {
use super::*;
use std::{ffi::CString, ptr};
#[test]
fn enumerate_nonempty() {
unsafe {
let table = WMCreateStringHashTable();
let k1 = CString::new("hello").unwrap().into_raw();
let v1 = CString::new("world").unwrap().into_raw();
let k2 = CString::new("foo").unwrap().into_raw();
let v2 = CString::new("bar").unwrap().into_raw();
WMHashInsert(table, k1.cast(), v1.cast());
WMHashInsert(table, k2.cast(), v2.cast());
let i = WMEnumerateHashTable(table);
let v = WMNextHashEnumeratorItem(i);
assert_ne!(v, ptr::null_mut());
assert!(v == v1.cast() || v == v2.cast());
let v = WMNextHashEnumeratorItem(i);
assert_ne!(v, ptr::null_mut());
assert!(v == v1.cast() || v == v2.cast());
let v = WMNextHashEnumeratorItem(i);
assert_eq!(v, ptr::null_mut());
WMFreeHashEnumerator(i);
let _ = CString::from_raw(k1);
let _ = CString::from_raw(v1);
let _ = CString::from_raw(k2);
let _ = CString::from_raw(v2);
}
}
}
}

View File

@@ -7,5 +7,6 @@ pub mod hash_table;
pub mod memory;
pub mod notification;
pub mod prop_list;
pub mod sendable;
pub mod string;
pub mod tree;

View File

@@ -1,9 +1,10 @@
use std::{
collections::{btree_map::Entry, BTreeMap},
ffi::{c_void, CStr},
ptr::{self, NonNull},
ptr,
sync::Mutex,
};
use crate::sendable::Sendable;
// Helper function for adding the entry `(key, (observer, action))` to `map`.
fn register<K: Eq + Ord>(
@@ -63,29 +64,6 @@ pub struct Notification {
/// 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)>>,
@@ -283,7 +261,7 @@ pub mod ffi {
let Some(action) = action else {
return;
};
let observer = NonNull::new(observer).map(|x| unsafe { Sendable::new(x) });
let observer = unsafe { Sendable::from_nullable(observer) };
let source = NonNull::new(object);
NotificationCenter::with_global_default(|c| {
if name.is_null() {
@@ -340,8 +318,8 @@ pub mod ffi {
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) });
let source = unsafe { Sendable::from_nullable(object) };
let client_data = unsafe {Sendable::from_nullable(client_data) };
NotificationCenter::with_global_default(|c| {
c.dispatch(Notification {
name,

41
wutil-rs/src/sendable.rs Normal file
View File

@@ -0,0 +1,41 @@
use std::{ffi::c_void, ptr::{NonNull, self}};
/// 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 {
pub(crate) ptr: NonNull<c_void>,
}
impl Sendable {
/// Creates a `Sendable` wrapping `ptr`.
///
/// ## Safety
///
/// `ptr` must be safe to send across threads.
pub const unsafe fn new(ptr: NonNull<c_void>) -> Self {
Sendable { ptr }
}
/// Creates a `Sendable` if `ptr` is not null, returning `None` if it is.
/// ## Safety
///
/// `ptr` must be safe to send across threads.
pub unsafe fn from_nullable(ptr: *mut c_void) -> Option<Self> {
unsafe {
NonNull::new(ptr)
.map(|p| Sendable::new(p))
}
}
/// Retrieves the pointer wrapped by `s`, or a null pointer if `s` is
/// `None`.
pub fn as_ptr(s: Option<Sendable>) -> *mut c_void {
s.map(|s| s.ptr.as_ptr()).unwrap_or(ptr::null_mut())
}
}
// Guaranteed by `Sendable::new`.
unsafe impl Send for Sendable {}

View File

@@ -7,7 +7,7 @@
use std::{
ffi::{c_char, c_int, CStr},
iter, mem, ptr, slice,
mem, ptr, slice,
};
use crate::memory::{alloc_bytes, alloc_string, ffi::wrealloc, free_bytes};