Merge refactor/wings-rs into refactor/riir #13
3
.gitignore
vendored
3
.gitignore
vendored
@@ -142,4 +142,5 @@ WPrefs.app/WPrefs.desktop
|
||||
.pc
|
||||
|
||||
# Rust stuff.
|
||||
/*/target/**
|
||||
/**/target/**
|
||||
WINGs/wings-rs/src/WINGsP.rs
|
||||
|
||||
@@ -39,7 +39,7 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-silent-rules LINGUAS='*'
|
||||
|
||||
|
||||
SUBDIRS = wrlib wutil-rs wings-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc
|
||||
SUBDIRS = wrlib wutil-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc
|
||||
DIST_SUBDIRS = $(SUBDIRS) test
|
||||
|
||||
EXTRA_DIST = TODO BUGS BUGFORM FAQ INSTALL \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -441,24 +441,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 +486,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);
|
||||
|
||||
|
||||
433
WINGs/wfont.c
433
WINGs/wfont.c
@@ -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;
|
||||
}
|
||||
@@ -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
14
WINGs/wings-rs/Cargo.toml
Normal 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"
|
||||
28
WINGs/wings-rs/Makefile.am
Normal file
28
WINGs/wings-rs/Makefile.am
Normal 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
22
WINGs/wings-rs/patch_WINGsP.sh
Executable 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"
|
||||
66
WINGs/wings-rs/src/configuration.rs
Normal file
66
WINGs/wings-rs/src/configuration.rs
Normal 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
586
WINGs/wings-rs/src/font.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
WINGs/wings-rs/src/lib.rs
Normal file
8
WINGs/wings-rs/src/lib.rs
Normal 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;
|
||||
21
WINGs/wings-rs/src/pango_extras.rs
Normal file
21
WINGs/wings-rs/src/pango_extras.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
63
WINGs/wings-rs/src/screen.rs
Normal file
63
WINGs/wings-rs/src/screen.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -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@ \
|
||||
|
||||
@@ -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));
|
||||
|
||||
10
configure.ac
10
configure.ac
@@ -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,12 +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
|
||||
wings-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
|
||||
|
||||
|
||||
@@ -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@ \
|
||||
|
||||
@@ -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@
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "wings-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
wutil-rs = { path = "../wutil-rs" }
|
||||
@@ -1,19 +0,0 @@
|
||||
AUTOMAKE_OPTIONS =
|
||||
|
||||
RUST_SOURCES = \
|
||||
src/lib.rs
|
||||
|
||||
RUST_EXTRA = \
|
||||
Cargo.lock \
|
||||
Cargo.toml
|
||||
|
||||
target/debug/libwings_rs.a: $(RUST_SOURCES) $(RUST_EXTRA)
|
||||
$(CARGO) build
|
||||
|
||||
check-local:
|
||||
$(CARGO) test
|
||||
|
||||
clean-local:
|
||||
$(CARGO) clean
|
||||
|
||||
all: target/debug/libwings_rs.a
|
||||
@@ -1,14 +0,0 @@
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user