Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56e08a5966 | ||
|
|
6a4cb9977e | ||
|
|
0d5986bb89 | ||
|
|
b7d40eeac7 | ||
|
|
6097395d45 | ||
|
|
c527a9f007 | ||
|
|
b4a91efedc | ||
|
|
49e2a9071d | ||
|
|
e71673e46b | ||
|
|
386e3ca307 | ||
|
|
36e5eb1bde | ||
|
|
5784b9cbda | ||
|
|
a5908610e9 | ||
|
|
18e54ecdfc | ||
|
|
01cb6645e0 | ||
|
|
84510b84b4 | ||
|
|
2da6de7593 | ||
|
|
6c17cc3a12 | ||
|
|
c24a199243 | ||
|
|
1ea3c1b8db | ||
|
|
39efd22bed | ||
|
|
52fc9eec43 | ||
|
|
d2a42971c8 | ||
|
|
d02d67ec61 | ||
|
|
b1eca604b2 | ||
|
|
797ef1ace8 | ||
|
|
b5e94932c9 | ||
|
|
efec1e8d1e | ||
|
|
2aa0b0bf53 | ||
|
|
d4c8f986a6 | ||
|
|
ec1761f4f3 | ||
|
|
32cf67b012 | ||
|
|
ee28e9d0a2 | ||
|
|
e882fd1f13 | ||
|
|
eaabf7e20c | ||
|
|
7c7b9aa97c | ||
|
|
223a64286f | ||
|
|
ce97a3b39f | ||
|
|
5075c877fa | ||
|
|
fa67563c2b | ||
|
|
cd711ba52b | ||
|
|
d46810291b | ||
|
|
7c875284dc | ||
|
|
52db12fbf2 | ||
|
|
b2481cf657 | ||
|
|
c371e26d05 | ||
|
|
98421afc38 | ||
|
|
9e49ed98a2 | ||
|
|
a466f17c35 | ||
|
|
d33c05ef08 | ||
|
|
79873413db | ||
|
|
57ac6b8178 | ||
|
|
d30fe6182d | ||
|
|
e20009a880 | ||
|
|
ee71db6693 | ||
|
|
01c7eb7275 | ||
|
|
fa99c12fd7 | ||
|
|
d7e815010b | ||
|
|
a31fa582bd | ||
|
|
e0fc92bf51 | ||
|
|
f8df6447ea | ||
|
|
bd18e0c600 | ||
|
|
50caed30c0 |
@@ -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,13 @@ WPrefs.app/WPrefs.desktop
|
||||
.pc
|
||||
|
||||
# Rust stuff.
|
||||
/*/target/**
|
||||
/**/target/**
|
||||
WINGs/wings-rs-tests/Cargo.lock
|
||||
WINGs/wings-rs/Cargo.lock
|
||||
WINGs/wings-rs/src/WINGsP.rs
|
||||
WINGs/wings-rs/src/WINGsP/constants.rs
|
||||
WINGs/wings-rs/src/WINGsP/mod.rs
|
||||
wmaker-rs/Cargo.lock
|
||||
wrlib-rs/src/ffi.rs
|
||||
wrlib-rs/Cargo.lock
|
||||
wutil-rs/Cargo.lock
|
||||
|
||||
@@ -39,7 +39,7 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-silent-rules LINGUAS='*'
|
||||
|
||||
|
||||
SUBDIRS = wrlib wutil-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc
|
||||
SUBDIRS = wrlib wrlib-rs 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 wings-rs-tests . po Documentation Resources
|
||||
DIST_SUBDIRS = $(SUBDIRS) Tests Examples Extras
|
||||
|
||||
libWINGs_la_LDFLAGS = -version-info @WINGS_VERSION@
|
||||
@@ -10,12 +10,10 @@ libWUtil_la_LDFLAGS = -version-info @WUTIL_VERSION@
|
||||
|
||||
lib_LTLIBRARIES = libWUtil.la libWINGs.la
|
||||
|
||||
wutilrs = $(top_builddir)/wutil-rs/target/debug/libwutil_rs.a
|
||||
wraster = $(top_builddir)/wrlib/libwraster.la
|
||||
|
||||
LDADD= libWUtil.la libWINGs.la $(wraster) $(wutilrs) @INTLIBS@
|
||||
libWINGs_la_LIBADD = libWUtil.la $(wraster) $(wutilrs) @XLIBS@ @XFT_LIBS@ @FCLIBS@ @LIBM@ @PANGO_LIBS@
|
||||
libWUtil_la_LIBADD = $(wutilrs)
|
||||
LDADD= libWUtil.la libWINGs.la $(wraster) @INTLIBS@
|
||||
libWINGs_la_LIBADD = libWUtil.la $(wraster) @XLIBS@ @XFT_LIBS@ @FCLIBS@ @LIBM@ @PANGO_LIBS@
|
||||
|
||||
EXTRA_DIST = BUGS make-rgb Examples Extras Tests
|
||||
|
||||
@@ -34,15 +32,12 @@ libWINGs_la_SOURCES = \
|
||||
wbox.c \
|
||||
wbrowser.c \
|
||||
wbutton.c \
|
||||
wcolor.c \
|
||||
wcolorpanel.c \
|
||||
wcolorwell.c \
|
||||
wconfig.h \
|
||||
wevent.c \
|
||||
wfilepanel.c \
|
||||
wframe.c \
|
||||
wfont.c \
|
||||
wfontpanel.c \
|
||||
widgets.c \
|
||||
winputmethod.c \
|
||||
wlabel.c \
|
||||
@@ -50,7 +45,6 @@ libWINGs_la_SOURCES = \
|
||||
wmenuitem.c \
|
||||
wmisc.c \
|
||||
wpanel.c \
|
||||
wpixmap.c \
|
||||
wpopupbutton.c \
|
||||
wprogressindicator.c \
|
||||
wruler.c \
|
||||
@@ -68,7 +62,6 @@ libWUtil_la_SOURCES = \
|
||||
error.c \
|
||||
error.h \
|
||||
findfile.c \
|
||||
handlers.c \
|
||||
menuparser.c \
|
||||
menuparser.h \
|
||||
menuparser_macros.c \
|
||||
@@ -78,8 +71,7 @@ libWUtil_la_SOURCES = \
|
||||
userdefaults.h \
|
||||
usleep.c \
|
||||
wapplication.c \
|
||||
wconfig.h \
|
||||
wutil.c
|
||||
wconfig.h
|
||||
|
||||
|
||||
AM_CFLAGS = @PANGO_CFLAGS@
|
||||
|
||||
@@ -4,14 +4,17 @@ AUTOMAKE_OPTIONS =
|
||||
|
||||
noinst_PROGRAMS = wtest wmquery wmfile testmywidget
|
||||
|
||||
LDADD= $(top_builddir)/WINGs/libWINGs.la $(top_builddir)/wrlib/libwraster.la \
|
||||
LDADD= $(top_builddir)/WINGs/libWINGs.la \
|
||||
$(top_builddir)/wrlib/libwraster.la \
|
||||
$(top_builddir)/WINGs/libWUtil.la \
|
||||
@XFT_LIBS@ @INTLIBS@ @XLIBS@
|
||||
$(top_builddir)/WINGs/wings-rs/target/debug/libwings_rs.a \
|
||||
@XFT_LIBS@ @INTLIBS@ @XLIBS@ @FCLIBS@ @PANGO_LIBS@
|
||||
|
||||
|
||||
testmywidget_SOURCES = testmywidget.c mywidget.c mywidget.h
|
||||
|
||||
wtest_DEPENDENCIES = $(top_builddir)/WINGs/libWINGs.la
|
||||
wtest_DEPENDENCIES = $(top_builddir)/WINGs/libWINGs.la \
|
||||
$(top_builddir)/WINGs/wings-rs/target/debug/libwings_rs.a
|
||||
|
||||
|
||||
EXTRA_DIST = logo.xpm upbtn.xpm wm.html wm.png
|
||||
|
||||
@@ -132,7 +132,7 @@ typedef enum {
|
||||
} WMButtonType;
|
||||
|
||||
/* button behaviour masks */
|
||||
enum {
|
||||
typedef enum {
|
||||
WBBSpringLoadedMask = (1 << 0),
|
||||
WBBPushInMask = (1 << 1),
|
||||
WBBPushChangeMask = (1 << 2),
|
||||
@@ -140,7 +140,7 @@ enum {
|
||||
WBBStateLightMask = (1 << 5),
|
||||
WBBStateChangeMask = (1 << 6),
|
||||
WBBStatePushMask = (1 << 7)
|
||||
};
|
||||
} WMButtonBehaviorMask;
|
||||
|
||||
|
||||
/* frame title positions */
|
||||
@@ -688,8 +688,7 @@ void WMSetApplicationIconPixmap(WMScreen *app, WMPixmap *icon);
|
||||
|
||||
WMPixmap* WMGetApplicationIconPixmap(WMScreen *app);
|
||||
|
||||
/* If color==NULL it will use the default color for panels: ae/aa/ae */
|
||||
WMPixmap* WMCreateApplicationIconBlendedPixmap(WMScreen *scr, const RColor *color);
|
||||
WMPixmap* WMCreateApplicationIconGreyedPixmap(WMScreen *scr);
|
||||
|
||||
void WMSetApplicationIconWindow(WMScreen *scr, Window window);
|
||||
|
||||
@@ -778,12 +777,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 +787,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 +808,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);
|
||||
@@ -837,26 +832,34 @@ WMSize WMGetPixmapSize(WMPixmap *pixmap);
|
||||
|
||||
WMPixmap* WMCreatePixmapFromFile(WMScreen *scrPtr, const char *fileName);
|
||||
|
||||
WMPixmap* WMCreateBlendedPixmapFromRImage(WMScreen *scrPtr, RImage *image,
|
||||
const RColor *color);
|
||||
WMPixmap* WMCreateGreyedPixmapFromRImage(WMScreen *scrPtr, RImage *image);
|
||||
|
||||
WMPixmap* WMCreateBlendedPixmapFromFile(WMScreen *scrPtr, const char *fileName,
|
||||
const RColor *color);
|
||||
WMPixmap* WMCreateGreyedPixmapFromFile(WMScreen *scrPtr, const char *fileName);
|
||||
|
||||
WMPixmap* WMCreateScaledBlendedPixmapFromFile(WMScreen *scrPtr, const char *fileName,
|
||||
const RColor *color,
|
||||
unsigned int width,
|
||||
unsigned int height);
|
||||
WMPixmap* WMCreateScaledPixmapFromFile(WMScreen *scrPtr, const char *fileName,
|
||||
unsigned int width,
|
||||
unsigned int height);
|
||||
|
||||
WMPixmap* WMCreateScaledGreyedPixmapFromFile(WMScreen *scrPtr, const char *fileName,
|
||||
unsigned int width,
|
||||
unsigned int height);
|
||||
|
||||
void WMDrawPixmap(WMPixmap *pixmap, Drawable d, int x, int y);
|
||||
|
||||
Pixmap WMGetPixmapXID(WMPixmap *pixmap);
|
||||
|
||||
/* Deliberately leaks the internal pixmap resource. */
|
||||
void WMLeakPixmapXID(WMPixmap *pixmap);
|
||||
|
||||
Pixmap WMGetPixmapMaskXID(WMPixmap *pixmap);
|
||||
|
||||
void WMSetPixmapMaskXID(WMPixmap *pixmap, Pixmap mask);
|
||||
|
||||
int WMGetPixmapDepth(WMPixmap *pixmap);
|
||||
|
||||
WMPixmap* WMGetSystemPixmap(WMScreen *scr, int image);
|
||||
|
||||
/* ---[ WINGs/wcolor.c ]-------------------------------------------------- */
|
||||
/* ---[ WINGs/wings-rs/src/color.rs ]------------------------------------- */
|
||||
|
||||
|
||||
WMColor* WMDarkGrayColor(WMScreen *scr);
|
||||
@@ -1043,11 +1046,7 @@ void WMCloseWindow(WMWindow *win);
|
||||
|
||||
void WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData);
|
||||
|
||||
#define WMCreateCommandButton(parent) \
|
||||
WMCreateCustomButton((parent), WBBSpringLoadedMask\
|
||||
|WBBPushInMask\
|
||||
|WBBPushLightMask\
|
||||
|WBBPushChangeMask)
|
||||
WMButton* WMCreateCommandButton(WMWidget *parent);
|
||||
|
||||
#define WMCreateRadioButton(parent) \
|
||||
WMCreateButton((parent), WBTRadio)
|
||||
|
||||
@@ -67,9 +67,11 @@ typedef struct W_DraggingInfo {
|
||||
|
||||
/* ---[ Structures from WINGs.h ]----------------------------------------- */
|
||||
|
||||
/* Pre-definition of internal structs */
|
||||
/* Opaque primitive types defined in Rust. */
|
||||
typedef struct W_Color W_Color;
|
||||
typedef struct W_Pixmap W_Pixmap;
|
||||
|
||||
/* Pre-definition of internal structs */
|
||||
typedef struct W_Pixmap WMPixmap;
|
||||
typedef struct W_View W_View;
|
||||
|
||||
typedef struct W_FocusInfo {
|
||||
@@ -104,7 +106,7 @@ typedef struct W_Screen {
|
||||
W_FocusInfo *focusInfo;
|
||||
|
||||
RImage *applicationIconImage; /* image (can have alpha channel) */
|
||||
W_Pixmap *applicationIconPixmap; /* pixmap - no alpha channel */
|
||||
WMPixmap *applicationIconPixmap; /* pixmap - no alpha channel */
|
||||
Window applicationIconWindow;
|
||||
|
||||
struct W_Window *windowList; /* list of windows in the app */
|
||||
@@ -122,8 +124,6 @@ typedef struct W_Screen {
|
||||
WMOpenPanel *sharedOpenPanel;
|
||||
WMSavePanel *sharedSavePanel;
|
||||
|
||||
struct W_FontPanel *sharedFontPanel;
|
||||
|
||||
struct W_ColorPanel *sharedColorPanel;
|
||||
|
||||
Pixmap stipple;
|
||||
@@ -132,10 +132,10 @@ typedef struct W_Screen {
|
||||
W_DraggingInfo dragInfo;
|
||||
|
||||
/* colors */
|
||||
W_Color *white;
|
||||
W_Color *black;
|
||||
W_Color *gray;
|
||||
W_Color *darkGray;
|
||||
WMColor *white;
|
||||
WMColor *black;
|
||||
WMColor *gray;
|
||||
WMColor *darkGray;
|
||||
|
||||
GC stippleGC;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -165,57 +165,57 @@ typedef struct W_Screen {
|
||||
struct W_Balloon *balloon;
|
||||
|
||||
|
||||
W_Pixmap *checkButtonImageOn;
|
||||
W_Pixmap *checkButtonImageOff;
|
||||
WMPixmap *checkButtonImageOn;
|
||||
WMPixmap *checkButtonImageOff;
|
||||
|
||||
W_Pixmap *radioButtonImageOn;
|
||||
W_Pixmap *radioButtonImageOff;
|
||||
WMPixmap *radioButtonImageOn;
|
||||
WMPixmap *radioButtonImageOff;
|
||||
|
||||
W_Pixmap *buttonArrow;
|
||||
W_Pixmap *pushedButtonArrow;
|
||||
WMPixmap *buttonArrow;
|
||||
WMPixmap *pushedButtonArrow;
|
||||
|
||||
W_Pixmap *scrollerDimple;
|
||||
WMPixmap *scrollerDimple;
|
||||
|
||||
W_Pixmap *upArrow;
|
||||
W_Pixmap *downArrow;
|
||||
W_Pixmap *leftArrow;
|
||||
W_Pixmap *rightArrow;
|
||||
WMPixmap *upArrow;
|
||||
WMPixmap *downArrow;
|
||||
WMPixmap *leftArrow;
|
||||
WMPixmap *rightArrow;
|
||||
|
||||
W_Pixmap *hiUpArrow;
|
||||
W_Pixmap *hiDownArrow;
|
||||
W_Pixmap *hiLeftArrow;
|
||||
W_Pixmap *hiRightArrow;
|
||||
WMPixmap *hiUpArrow;
|
||||
WMPixmap *hiDownArrow;
|
||||
WMPixmap *hiLeftArrow;
|
||||
WMPixmap *hiRightArrow;
|
||||
|
||||
W_Pixmap *pullDownIndicator;
|
||||
W_Pixmap *popUpIndicator;
|
||||
WMPixmap *pullDownIndicator;
|
||||
WMPixmap *popUpIndicator;
|
||||
|
||||
W_Pixmap *checkMark;
|
||||
WMPixmap *checkMark;
|
||||
|
||||
W_Pixmap *homeIcon;
|
||||
W_Pixmap *altHomeIcon;
|
||||
WMPixmap *homeIcon;
|
||||
WMPixmap *altHomeIcon;
|
||||
|
||||
W_Pixmap *trashcanIcon;
|
||||
W_Pixmap *altTrashcanIcon;
|
||||
WMPixmap *trashcanIcon;
|
||||
WMPixmap *altTrashcanIcon;
|
||||
|
||||
W_Pixmap *createDirIcon;
|
||||
W_Pixmap *altCreateDirIcon;
|
||||
WMPixmap *createDirIcon;
|
||||
WMPixmap *altCreateDirIcon;
|
||||
|
||||
W_Pixmap *disketteIcon;
|
||||
W_Pixmap *altDisketteIcon;
|
||||
W_Pixmap *unmountIcon;
|
||||
W_Pixmap *altUnmountIcon;
|
||||
WMPixmap *disketteIcon;
|
||||
WMPixmap *altDisketteIcon;
|
||||
WMPixmap *unmountIcon;
|
||||
WMPixmap *altUnmountIcon;
|
||||
|
||||
W_Pixmap *magnifyIcon;
|
||||
/*W_Pixmap *altMagnifyIcon;*/
|
||||
W_Pixmap *wheelIcon;
|
||||
W_Pixmap *grayIcon;
|
||||
W_Pixmap *rgbIcon;
|
||||
W_Pixmap *cmykIcon;
|
||||
W_Pixmap *hsbIcon;
|
||||
W_Pixmap *customPaletteIcon;
|
||||
W_Pixmap *colorListIcon;
|
||||
WMPixmap *magnifyIcon;
|
||||
/*WMPixmap *altMagnifyIcon;*/
|
||||
WMPixmap *wheelIcon;
|
||||
WMPixmap *grayIcon;
|
||||
WMPixmap *rgbIcon;
|
||||
WMPixmap *cmykIcon;
|
||||
WMPixmap *hsbIcon;
|
||||
WMPixmap *customPaletteIcon;
|
||||
WMPixmap *colorListIcon;
|
||||
|
||||
W_Pixmap *defaultObjectIcon;
|
||||
WMPixmap *defaultObjectIcon;
|
||||
|
||||
Cursor defaultCursor;
|
||||
|
||||
@@ -272,9 +272,9 @@ typedef struct W_Screen {
|
||||
* Added at the end of the structure to avoid breaking binary compatibility
|
||||
* with previous versions of the toolkit
|
||||
*/
|
||||
W_Pixmap *tristateButtonImageOn;
|
||||
W_Pixmap *tristateButtonImageOff;
|
||||
W_Pixmap *tristateButtonImageTri;
|
||||
WMPixmap *tristateButtonImageOn;
|
||||
WMPixmap *tristateButtonImageOff;
|
||||
WMPixmap *tristateButtonImageTri;
|
||||
|
||||
} W_Screen;
|
||||
|
||||
@@ -369,8 +369,12 @@ Bool W_CheckIdleHandlers(void);
|
||||
|
||||
void W_CheckTimerHandlers(void);
|
||||
|
||||
Bool W_HandleInputEvents(Bool waitForInput, int inputfd);
|
||||
|
||||
/*
|
||||
* Returns the duration in milliseconds until the next timer event should go off
|
||||
* (saturating at INT_MAX). If there is no such timer event, returns a negative
|
||||
* value.
|
||||
*/
|
||||
int W_DelayUntilNextTimerEvent_millis();
|
||||
|
||||
/* ---[ notification.c ]-------------------------------------------------- */
|
||||
|
||||
@@ -378,6 +382,8 @@ void W_InitNotificationCenter(void);
|
||||
|
||||
void W_ReleaseNotificationCenter(void);
|
||||
|
||||
void W_ClearNotificationCenter(void);
|
||||
|
||||
|
||||
/* ---[ selection.c ]----------------------------------------------------- */
|
||||
|
||||
@@ -409,23 +415,6 @@ void W_BalloonHandleEnterView(WMView *view);
|
||||
void W_BalloonHandleLeaveView(WMView *view);
|
||||
|
||||
|
||||
/* ---[ wcolor.c ]-------------------------------------------------------- */
|
||||
|
||||
struct W_Color {
|
||||
struct W_Screen *screen;
|
||||
|
||||
XColor color;
|
||||
unsigned short alpha;
|
||||
short refCount;
|
||||
GC gc;
|
||||
struct {
|
||||
unsigned int exact:1;
|
||||
} flags;
|
||||
};
|
||||
|
||||
#define W_PIXEL(c) (c)->color.pixel
|
||||
|
||||
|
||||
/* ---[ wevent.c ]-------------------------------------------------------- */
|
||||
|
||||
typedef struct W_EventHandler {
|
||||
@@ -441,24 +430,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,8 +475,8 @@ 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,
|
||||
WMAlignment alignment, W_Pixmap *image,
|
||||
WMFont *font, WMReliefType relief, const char *text,
|
||||
WMAlignment alignment, WMPixmap *image,
|
||||
WMImagePosition position, WMColor *backColor, int ofs);
|
||||
|
||||
void W_PaintText(W_View *view, Drawable d, WMFont *font, int x, int y,
|
||||
@@ -515,19 +486,6 @@ void W_PaintText(W_View *view, Drawable d, WMFont *font, int x, int y,
|
||||
int W_GetTextHeight(WMFont *font, const char *text, int width, int wrap);
|
||||
|
||||
|
||||
/* ---[ wpixmap.c ]------------------------------------------------------- */
|
||||
|
||||
struct W_Pixmap {
|
||||
struct W_Screen *screen;
|
||||
Pixmap pixmap;
|
||||
Pixmap mask;
|
||||
unsigned short width;
|
||||
unsigned short height;
|
||||
short depth;
|
||||
short refCount;
|
||||
};
|
||||
|
||||
|
||||
/* ---[ wview.c ]--------------------------------------------------------- */
|
||||
|
||||
typedef struct W_ViewDelegate {
|
||||
|
||||
@@ -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 {
|
||||
@@ -286,7 +281,7 @@ char* wtrimspace(const char *s);
|
||||
*/
|
||||
char *wshellquote(const char *s);
|
||||
|
||||
/* ---[ WINGs/misc.c ]--------------------------------------------------- */
|
||||
/* ---[ wutil-rs/src/range.rs ]------------------------------------------ */
|
||||
|
||||
WMRange wmkrange(int start, int count);
|
||||
|
||||
@@ -302,34 +297,20 @@ void wusleep(unsigned int usec);
|
||||
|
||||
/* Event handlers: timer, idle, input */
|
||||
|
||||
WMHandlerID WMAddTimerHandler(int milliseconds, WMCallback *callback,
|
||||
WMHandlerID WMAddTimerHandler(unsigned milliseconds, WMCallback *callback,
|
||||
void *cdata);
|
||||
|
||||
WMHandlerID WMAddPersistentTimerHandler(int milliseconds, WMCallback *callback,
|
||||
WMHandlerID WMAddPersistentTimerHandler(unsigned milliseconds, WMCallback *callback,
|
||||
void *cdata);
|
||||
|
||||
void WMDeleteTimerWithClientData(void *cdata);
|
||||
|
||||
void WMDeleteTimerHandler(WMHandlerID handlerID);
|
||||
|
||||
WMHandlerID WMAddIdleHandler(WMCallback *callback, void *cdata);
|
||||
|
||||
void WMDeleteIdleHandler(WMHandlerID handlerID);
|
||||
|
||||
WMHandlerID WMAddInputHandler(int fd, int condition, WMInputProc *proc,
|
||||
void *clientData);
|
||||
|
||||
void WMDeleteInputHandler(WMHandlerID handlerID);
|
||||
|
||||
|
||||
/* This function is used _only_ if you create a non-GUI program.
|
||||
* For GUI based programs use WMNextEvent()/WMHandleEvent() instead.
|
||||
* This function will handle all input/timer/idle events, then return.
|
||||
*/
|
||||
|
||||
void WHandleEvents(void);
|
||||
|
||||
/* ---[ WINGs/hashtable.c ]----------------------------------------------- */
|
||||
/* ---[ wutil-rs/src/hash_table.rs ]----------------------------------------------- */
|
||||
|
||||
|
||||
WMHashTable* WMCreateIdentityHashTable();
|
||||
@@ -361,7 +342,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 +357,7 @@ Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator *enumerator,
|
||||
void **item, void **key);
|
||||
|
||||
|
||||
|
||||
void WMFreeHashEnumerator(WMHashEnumerator *enumerator);
|
||||
|
||||
/* some predefined callback sets */
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ static Window makeDragIcon(WMScreen * scr, WMPixmap * pixmap)
|
||||
|
||||
flags = CWSaveUnder | CWBackPixmap | CWOverrideRedirect | CWColormap;
|
||||
attribs.save_under = True;
|
||||
attribs.background_pixmap = pixmap->pixmap;
|
||||
attribs.background_pixmap = WMGetPixmapXID(pixmap);
|
||||
attribs.override_redirect = True;
|
||||
attribs.colormap = scr->colormap;
|
||||
|
||||
@@ -203,8 +203,8 @@ static Window makeDragIcon(WMScreen * scr, WMPixmap * pixmap)
|
||||
size.height, 0, scr->depth, InputOutput, scr->visual, flags, &attribs);
|
||||
|
||||
#ifdef USE_XSHAPE
|
||||
if (pixmap->mask) {
|
||||
XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, pixmap->mask, ShapeSet);
|
||||
if (WMGetPixmapMaskXID(pixmap)) {
|
||||
XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, WMGetPixmapMaskXID(pixmap), ShapeSet);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,554 +0,0 @@
|
||||
|
||||
/*
|
||||
* WINGs internal handlers: timer, idle and input handlers
|
||||
*/
|
||||
|
||||
#include "wconfig.h"
|
||||
#include "WINGsP.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <X11/Xos.h>
|
||||
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
# include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifndef X_GETTIMEOFDAY
|
||||
#define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
|
||||
#endif
|
||||
|
||||
typedef struct TimerHandler {
|
||||
WMCallback *callback; /* procedure to call */
|
||||
struct timeval when; /* when to call the callback */
|
||||
void *clientData;
|
||||
struct TimerHandler *next;
|
||||
int nextDelay; /* 0 if it's one-shot */
|
||||
} TimerHandler;
|
||||
|
||||
typedef struct IdleHandler {
|
||||
WMCallback *callback;
|
||||
void *clientData;
|
||||
} IdleHandler;
|
||||
|
||||
typedef struct InputHandler {
|
||||
WMInputProc *callback;
|
||||
void *clientData;
|
||||
int fd;
|
||||
int mask;
|
||||
} InputHandler;
|
||||
|
||||
/* queue of timer event handlers */
|
||||
static TimerHandler *timerHandler = NULL;
|
||||
|
||||
static WMArray *idleHandler = NULL;
|
||||
|
||||
static WMArray *inputHandler = NULL;
|
||||
|
||||
#define timerPending() (timerHandler)
|
||||
|
||||
static void rightNow(struct timeval *tv)
|
||||
{
|
||||
X_GETTIMEOFDAY(tv);
|
||||
}
|
||||
|
||||
/* is t1 after t2 ? */
|
||||
#define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
|
||||
(((t1).tv_sec == (t2).tv_sec) \
|
||||
&& ((t1).tv_usec > (t2).tv_usec)))
|
||||
|
||||
#define IS_ZERO(tv) (tv.tv_sec == 0 && tv.tv_usec == 0)
|
||||
|
||||
#define SET_ZERO(tv) tv.tv_sec = 0, tv.tv_usec = 0
|
||||
|
||||
static void addmillisecs(struct timeval *tv, int milliseconds)
|
||||
{
|
||||
tv->tv_usec += milliseconds * 1000;
|
||||
|
||||
tv->tv_sec += tv->tv_usec / 1000000;
|
||||
tv->tv_usec = tv->tv_usec % 1000000;
|
||||
}
|
||||
|
||||
static void enqueueTimerHandler(TimerHandler * handler)
|
||||
{
|
||||
TimerHandler *tmp;
|
||||
|
||||
/* insert callback in queue, sorted by time left */
|
||||
if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
|
||||
/* first in the queue */
|
||||
handler->next = timerHandler;
|
||||
timerHandler = handler;
|
||||
} else {
|
||||
tmp = timerHandler;
|
||||
while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
|
||||
tmp = tmp->next;
|
||||
}
|
||||
handler->next = tmp->next;
|
||||
tmp->next = handler;
|
||||
}
|
||||
}
|
||||
|
||||
static void delayUntilNextTimerEvent(struct timeval *delay)
|
||||
{
|
||||
struct timeval now;
|
||||
TimerHandler *handler;
|
||||
|
||||
handler = timerHandler;
|
||||
while (handler && IS_ZERO(handler->when))
|
||||
handler = handler->next;
|
||||
|
||||
if (!handler) {
|
||||
/* The return value of this function is only valid if there _are_
|
||||
timers active. */
|
||||
delay->tv_sec = 0;
|
||||
delay->tv_usec = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
rightNow(&now);
|
||||
if (IS_AFTER(now, handler->when)) {
|
||||
delay->tv_sec = 0;
|
||||
delay->tv_usec = 0;
|
||||
} else {
|
||||
delay->tv_sec = handler->when.tv_sec - now.tv_sec;
|
||||
delay->tv_usec = handler->when.tv_usec - now.tv_usec;
|
||||
if (delay->tv_usec < 0) {
|
||||
delay->tv_usec += 1000000;
|
||||
delay->tv_sec--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WMHandlerID WMAddTimerHandler(int milliseconds, WMCallback * callback, void *cdata)
|
||||
{
|
||||
TimerHandler *handler;
|
||||
|
||||
handler = wmalloc(sizeof(TimerHandler));
|
||||
if (!handler)
|
||||
return NULL;
|
||||
|
||||
rightNow(&handler->when);
|
||||
addmillisecs(&handler->when, milliseconds);
|
||||
handler->callback = callback;
|
||||
handler->clientData = cdata;
|
||||
handler->nextDelay = 0;
|
||||
|
||||
enqueueTimerHandler(handler);
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
WMHandlerID WMAddPersistentTimerHandler(int milliseconds, WMCallback * callback, void *cdata)
|
||||
{
|
||||
TimerHandler *handler = WMAddTimerHandler(milliseconds, callback, cdata);
|
||||
|
||||
if (handler != NULL)
|
||||
handler->nextDelay = milliseconds;
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
void WMDeleteTimerWithClientData(void *cdata)
|
||||
{
|
||||
TimerHandler *handler, *tmp;
|
||||
|
||||
if (!cdata || !timerHandler)
|
||||
return;
|
||||
|
||||
tmp = timerHandler;
|
||||
if (tmp->clientData == cdata) {
|
||||
tmp->nextDelay = 0;
|
||||
if (!IS_ZERO(tmp->when)) {
|
||||
timerHandler = tmp->next;
|
||||
wfree(tmp);
|
||||
}
|
||||
} else {
|
||||
while (tmp->next) {
|
||||
if (tmp->next->clientData == cdata) {
|
||||
handler = tmp->next;
|
||||
handler->nextDelay = 0;
|
||||
if (IS_ZERO(handler->when))
|
||||
break;
|
||||
tmp->next = handler->next;
|
||||
wfree(handler);
|
||||
break;
|
||||
}
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WMDeleteTimerHandler(WMHandlerID handlerID)
|
||||
{
|
||||
TimerHandler *tmp, *handler = (TimerHandler *) handlerID;
|
||||
|
||||
if (!handler || !timerHandler)
|
||||
return;
|
||||
|
||||
tmp = timerHandler;
|
||||
|
||||
handler->nextDelay = 0;
|
||||
|
||||
if (IS_ZERO(handler->when))
|
||||
return;
|
||||
|
||||
if (tmp == handler) {
|
||||
timerHandler = handler->next;
|
||||
wfree(handler);
|
||||
} else {
|
||||
while (tmp->next) {
|
||||
if (tmp->next == handler) {
|
||||
tmp->next = handler->next;
|
||||
wfree(handler);
|
||||
break;
|
||||
}
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WMHandlerID WMAddIdleHandler(WMCallback * callback, void *cdata)
|
||||
{
|
||||
IdleHandler *handler;
|
||||
|
||||
handler = wmalloc(sizeof(IdleHandler));
|
||||
if (!handler)
|
||||
return NULL;
|
||||
|
||||
handler->callback = callback;
|
||||
handler->clientData = cdata;
|
||||
/* add handler at end of queue */
|
||||
if (!idleHandler) {
|
||||
idleHandler = WMCreateArrayWithDestructor(16, wfree);
|
||||
}
|
||||
WMAddToArray(idleHandler, handler);
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
void WMDeleteIdleHandler(WMHandlerID handlerID)
|
||||
{
|
||||
IdleHandler *handler = (IdleHandler *) handlerID;
|
||||
|
||||
if (!handler || !idleHandler)
|
||||
return;
|
||||
|
||||
WMRemoveFromArray(idleHandler, handler);
|
||||
}
|
||||
|
||||
WMHandlerID WMAddInputHandler(int fd, int condition, WMInputProc * proc, void *clientData)
|
||||
{
|
||||
InputHandler *handler;
|
||||
|
||||
handler = wmalloc(sizeof(InputHandler));
|
||||
|
||||
handler->fd = fd;
|
||||
handler->mask = condition;
|
||||
handler->callback = proc;
|
||||
handler->clientData = clientData;
|
||||
|
||||
if (!inputHandler)
|
||||
inputHandler = WMCreateArrayWithDestructor(16, wfree);
|
||||
WMAddToArray(inputHandler, handler);
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
void WMDeleteInputHandler(WMHandlerID handlerID)
|
||||
{
|
||||
InputHandler *handler = (InputHandler *) handlerID;
|
||||
|
||||
if (!handler || !inputHandler)
|
||||
return;
|
||||
|
||||
WMRemoveFromArray(inputHandler, handler);
|
||||
}
|
||||
|
||||
Bool W_CheckIdleHandlers(void)
|
||||
{
|
||||
IdleHandler *handler;
|
||||
WMArray *handlerCopy;
|
||||
WMArrayIterator iter;
|
||||
|
||||
if (!idleHandler || WMGetArrayItemCount(idleHandler) == 0) {
|
||||
/* make sure an observer in queue didn't added an idle handler */
|
||||
return (idleHandler != NULL && WMGetArrayItemCount(idleHandler) > 0);
|
||||
}
|
||||
|
||||
handlerCopy = WMCreateArrayWithArray(idleHandler);
|
||||
|
||||
WM_ITERATE_ARRAY(handlerCopy, handler, iter) {
|
||||
/* check if the handler still exist or was removed by a callback */
|
||||
if (WMGetFirstInArray(idleHandler, handler) == WANotFound)
|
||||
continue;
|
||||
|
||||
(*handler->callback) (handler->clientData);
|
||||
WMDeleteIdleHandler(handler);
|
||||
}
|
||||
|
||||
WMFreeArray(handlerCopy);
|
||||
|
||||
/* this is not necesarrily False, because one handler can re-add itself */
|
||||
return (WMGetArrayItemCount(idleHandler) > 0);
|
||||
}
|
||||
|
||||
void W_CheckTimerHandlers(void)
|
||||
{
|
||||
TimerHandler *handler;
|
||||
struct timeval now;
|
||||
|
||||
if (!timerHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
rightNow(&now);
|
||||
|
||||
handler = timerHandler;
|
||||
while (handler && IS_AFTER(now, handler->when)) {
|
||||
if (!IS_ZERO(handler->when)) {
|
||||
SET_ZERO(handler->when);
|
||||
(*handler->callback) (handler->clientData);
|
||||
}
|
||||
handler = handler->next;
|
||||
}
|
||||
|
||||
while (timerHandler && IS_ZERO(timerHandler->when)) {
|
||||
handler = timerHandler;
|
||||
timerHandler = timerHandler->next;
|
||||
|
||||
if (handler->nextDelay > 0) {
|
||||
handler->when = now;
|
||||
addmillisecs(&handler->when, handler->nextDelay);
|
||||
enqueueTimerHandler(handler);
|
||||
} else {
|
||||
wfree(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This functions will handle input events on all registered file descriptors.
|
||||
* Input:
|
||||
* - waitForInput - True if we want the function to wait until an event
|
||||
* appears on a file descriptor we watch, False if we
|
||||
* want the function to immediately return if there is
|
||||
* no data available on the file descriptors we watch.
|
||||
* - inputfd - Extra input file descriptor to watch for input.
|
||||
* This is only used when called from wevent.c to watch
|
||||
* on ConnectionNumber(dpy) to avoid blocking of X events
|
||||
* if we wait for input from other file handlers.
|
||||
* Output:
|
||||
* if waitForInput is False, the function will return False if there are no
|
||||
* input handlers registered, or if there is no data
|
||||
* available on the registered ones, and will return True
|
||||
* if there is at least one input handler that has data
|
||||
* available.
|
||||
* if waitForInput is True, the function will return False if there are no
|
||||
* input handlers registered, else it will block until an
|
||||
* event appears on one of the file descriptors it watches
|
||||
* and then it will return True.
|
||||
*
|
||||
* If the retured value is True, the input handlers for the corresponding file
|
||||
* descriptors are also called.
|
||||
*
|
||||
* Parametersshould be passed like this:
|
||||
* - from wevent.c:
|
||||
* waitForInput - apropriate value passed by the function who called us
|
||||
* inputfd = ConnectionNumber(dpy)
|
||||
* - from wutil.c:
|
||||
* waitForInput - apropriate value passed by the function who called us
|
||||
* inputfd = -1
|
||||
*
|
||||
*/
|
||||
Bool W_HandleInputEvents(Bool waitForInput, int inputfd)
|
||||
{
|
||||
#if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
|
||||
struct poll fd *fds;
|
||||
InputHandler *handler;
|
||||
int count, timeout, nfds, i, extrafd;
|
||||
|
||||
extrafd = (inputfd < 0) ? 0 : 1;
|
||||
|
||||
if (inputHandler)
|
||||
nfds = WMGetArrayItemCount(inputHandler);
|
||||
else
|
||||
nfds = 0;
|
||||
|
||||
if (!extrafd && nfds == 0) {
|
||||
return False;
|
||||
}
|
||||
|
||||
fds = wmalloc((nfds + extrafd) * sizeof(struct pollfd));
|
||||
if (extrafd) {
|
||||
/* put this to the end of array to avoid using ranges from 1 to nfds+1 */
|
||||
fds[nfds].fd = inputfd;
|
||||
fds[nfds].events = POLLIN;
|
||||
}
|
||||
|
||||
/* use WM_ITERATE_ARRAY() here */
|
||||
for (i = 0; i < nfds; i++) {
|
||||
handler = WMGetFromArray(inputHandler, i);
|
||||
fds[i].fd = handler->fd;
|
||||
fds[i].events = 0;
|
||||
if (handler->mask & WIReadMask)
|
||||
fds[i].events |= POLLIN;
|
||||
|
||||
if (handler->mask & WIWriteMask)
|
||||
fds[i].events |= POLLOUT;
|
||||
|
||||
#if 0 /* FIXME */
|
||||
if (handler->mask & WIExceptMask)
|
||||
FD_SET(handler->fd, &eset);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the timeout to the estimated time until the
|
||||
* next timer expires.
|
||||
*/
|
||||
if (!waitForInput) {
|
||||
timeout = 0;
|
||||
} else if (timerPending()) {
|
||||
struct timeval tv;
|
||||
delayUntilNextTimerEvent(&tv);
|
||||
timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
} else {
|
||||
timeout = -1;
|
||||
}
|
||||
|
||||
count = poll(fds, nfds + extrafd, timeout);
|
||||
|
||||
if (count > 0 && nfds > 0) {
|
||||
WMArray *handlerCopy = WMCreateArrayWithArray(inputHandler);
|
||||
int mask;
|
||||
|
||||
/* use WM_ITERATE_ARRAY() here */
|
||||
for (i = 0; i < nfds; i++) {
|
||||
handler = WMGetFromArray(handlerCopy, i);
|
||||
/* check if the handler still exist or was removed by a callback */
|
||||
if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
|
||||
continue;
|
||||
|
||||
mask = 0;
|
||||
|
||||
if ((handler->mask & WIReadMask) &&
|
||||
(fds[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)))
|
||||
mask |= WIReadMask;
|
||||
|
||||
if ((handler->mask & WIWriteMask) && (fds[i].revents & (POLLOUT | POLLWRBAND)))
|
||||
mask |= WIWriteMask;
|
||||
|
||||
if ((handler->mask & WIExceptMask) && (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
|
||||
mask |= WIExceptMask;
|
||||
|
||||
if (mask != 0 && handler->callback) {
|
||||
(*handler->callback) (handler->fd, mask, handler->clientData);
|
||||
}
|
||||
}
|
||||
|
||||
WMFreeArray(handlerCopy);
|
||||
}
|
||||
|
||||
wfree(fds);
|
||||
|
||||
return (count > 0);
|
||||
#else
|
||||
#ifdef HAVE_SELECT
|
||||
struct timeval timeout;
|
||||
struct timeval *timeoutPtr;
|
||||
fd_set rset, wset, eset;
|
||||
int maxfd, nfds, i;
|
||||
int count;
|
||||
InputHandler *handler;
|
||||
|
||||
if (inputHandler)
|
||||
nfds = WMGetArrayItemCount(inputHandler);
|
||||
else
|
||||
nfds = 0;
|
||||
|
||||
if (inputfd < 0 && nfds == 0) {
|
||||
return False;
|
||||
}
|
||||
|
||||
FD_ZERO(&rset);
|
||||
FD_ZERO(&wset);
|
||||
FD_ZERO(&eset);
|
||||
|
||||
if (inputfd < 0) {
|
||||
maxfd = 0;
|
||||
} else {
|
||||
FD_SET(inputfd, &rset);
|
||||
maxfd = inputfd;
|
||||
}
|
||||
|
||||
/* use WM_ITERATE_ARRAY() here */
|
||||
for (i = 0; i < nfds; i++) {
|
||||
handler = WMGetFromArray(inputHandler, i);
|
||||
if (handler->mask & WIReadMask)
|
||||
FD_SET(handler->fd, &rset);
|
||||
|
||||
if (handler->mask & WIWriteMask)
|
||||
FD_SET(handler->fd, &wset);
|
||||
|
||||
if (handler->mask & WIExceptMask)
|
||||
FD_SET(handler->fd, &eset);
|
||||
|
||||
if (maxfd < handler->fd)
|
||||
maxfd = handler->fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the timeout to the estimated time until the
|
||||
* next timer expires.
|
||||
*/
|
||||
if (!waitForInput) {
|
||||
SET_ZERO(timeout);
|
||||
timeoutPtr = &timeout;
|
||||
} else if (timerPending()) {
|
||||
delayUntilNextTimerEvent(&timeout);
|
||||
timeoutPtr = &timeout;
|
||||
} else {
|
||||
timeoutPtr = (struct timeval *)0;
|
||||
}
|
||||
|
||||
count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
|
||||
|
||||
if (count > 0 && nfds > 0) {
|
||||
WMArray *handlerCopy = WMCreateArrayWithArray(inputHandler);
|
||||
int mask;
|
||||
|
||||
/* use WM_ITERATE_ARRAY() here */
|
||||
for (i = 0; i < nfds; i++) {
|
||||
handler = WMGetFromArray(handlerCopy, i);
|
||||
/* check if the handler still exist or was removed by a callback */
|
||||
if (WMGetFirstInArray(inputHandler, handler) == WANotFound)
|
||||
continue;
|
||||
|
||||
mask = 0;
|
||||
|
||||
if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
|
||||
mask |= WIReadMask;
|
||||
|
||||
if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
|
||||
mask |= WIWriteMask;
|
||||
|
||||
if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
|
||||
mask |= WIExceptMask;
|
||||
|
||||
if (mask != 0 && handler->callback) {
|
||||
(*handler->callback) (handler->fd, mask, handler->clientData);
|
||||
}
|
||||
}
|
||||
|
||||
WMFreeArray(handlerCopy);
|
||||
}
|
||||
|
||||
return (count > 0);
|
||||
#else /* not HAVE_SELECT, not HAVE_POLL */
|
||||
# error Neither select nor poll. You lose.
|
||||
#endif /* HAVE_SELECT */
|
||||
#endif /* HAVE_POLL */
|
||||
}
|
||||
@@ -24,16 +24,6 @@
|
||||
|
||||
#include "error.h"
|
||||
|
||||
WMRange wmkrange(int start, int count)
|
||||
{
|
||||
WMRange range;
|
||||
|
||||
range.position = start;
|
||||
range.count = count;
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
/*
|
||||
* wutil_shutdown - cleanup in WUtil when user program wants to exit
|
||||
*/
|
||||
|
||||
@@ -63,8 +63,8 @@ void WMSetApplicationIconPixmap(WMScreen * scr, WMPixmap * icon)
|
||||
|
||||
hints = XGetWMHints(scr->display, scr->groupLeader);
|
||||
hints->flags |= IconPixmapHint | IconMaskHint;
|
||||
hints->icon_pixmap = (icon != NULL ? icon->pixmap : None);
|
||||
hints->icon_mask = (icon != NULL ? icon->mask : None);
|
||||
hints->icon_pixmap = (icon != NULL ? WMGetPixmapXID(icon) : None);
|
||||
hints->icon_mask = (icon != NULL ? WMGetPixmapMaskXID(icon) : None);
|
||||
|
||||
XSetWMHints(scr->display, scr->groupLeader, hints);
|
||||
XFree(hints);
|
||||
@@ -76,22 +76,12 @@ WMPixmap *WMGetApplicationIconPixmap(WMScreen * scr)
|
||||
return scr->applicationIconPixmap;
|
||||
}
|
||||
|
||||
WMPixmap *WMCreateApplicationIconBlendedPixmap(WMScreen * scr, const RColor * color)
|
||||
WMPixmap *WMCreateApplicationIconGreyedPixmap(WMScreen * scr)
|
||||
{
|
||||
WMPixmap *pix;
|
||||
|
||||
if (scr->applicationIconImage) {
|
||||
static const RColor gray = {
|
||||
/* red */ 0xAE,
|
||||
/* green */ 0xAA,
|
||||
/* blue */ 0xAE,
|
||||
/* alpha */ 0xFF
|
||||
};
|
||||
|
||||
if (!color)
|
||||
color = &gray;
|
||||
|
||||
pix = WMCreateBlendedPixmapFromRImage(scr, scr->applicationIconImage, color);
|
||||
pix = WMCreateGreyedPixmapFromRImage(scr, scr->applicationIconImage);
|
||||
} else {
|
||||
pix = NULL;
|
||||
}
|
||||
@@ -133,10 +123,10 @@ void W_InitApplication(WMScreen * scr)
|
||||
*/
|
||||
if (scr->applicationIconPixmap) {
|
||||
hints->flags |= IconPixmapHint;
|
||||
hints->icon_pixmap = scr->applicationIconPixmap->pixmap;
|
||||
if (scr->applicationIconPixmap->mask) {
|
||||
hints->icon_pixmap = WMGetPixmapXID(scr->applicationIconPixmap);
|
||||
if (WMGetPixmapMaskXID(scr->applicationIconPixmap)) {
|
||||
hints->flags |= IconMaskHint;
|
||||
hints->icon_mask = scr->applicationIconPixmap->mask;
|
||||
hints->icon_mask = WMGetPixmapMaskXID(scr->applicationIconPixmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ typedef struct W_Button {
|
||||
WMColor *altTextColor;
|
||||
WMColor *disTextColor;
|
||||
|
||||
W_Pixmap *image;
|
||||
W_Pixmap *altImage;
|
||||
W_Pixmap *tsImage;
|
||||
WMPixmap *image;
|
||||
WMPixmap *altImage;
|
||||
WMPixmap *tsImage;
|
||||
|
||||
W_Pixmap *dimage;
|
||||
WMPixmap *dimage;
|
||||
|
||||
void *clientData;
|
||||
WMAction *action;
|
||||
@@ -216,6 +216,14 @@ WMButton *WMCreateButton(WMWidget * parent, WMButtonType type)
|
||||
return bPtr;
|
||||
}
|
||||
|
||||
WMButton *WMCreateCommandButton(WMWidget *parent)
|
||||
{
|
||||
return WMCreateCustomButton(
|
||||
parent,
|
||||
WBBSpringLoadedMask|WBBPushInMask|WBBPushLightMask|WBBPushChangeMask
|
||||
);
|
||||
}
|
||||
|
||||
static void updateDisabledMask(WMButton * bPtr)
|
||||
{
|
||||
WMScreen *scr = WMWidgetScreen(bPtr);
|
||||
@@ -224,32 +232,32 @@ static void updateDisabledMask(WMButton * bPtr)
|
||||
if (bPtr->image) {
|
||||
XGCValues gcv;
|
||||
|
||||
if (bPtr->dimage->mask) {
|
||||
XFreePixmap(dpy, bPtr->dimage->mask);
|
||||
bPtr->dimage->mask = None;
|
||||
if (WMGetPixmapMaskXID(bPtr->dimage)) {
|
||||
XFreePixmap(dpy, WMGetPixmapMaskXID(bPtr->dimage));
|
||||
WMSetPixmapMaskXID(bPtr->dimage, 0);
|
||||
}
|
||||
|
||||
if (bPtr->flags.dimsWhenDisabled) {
|
||||
bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
|
||||
bPtr->dimage->width, bPtr->dimage->height, 1);
|
||||
WMSize size = WMGetPixmapSize(bPtr->dimage);
|
||||
WMSetPixmapMaskXID(bPtr->dimage, XCreatePixmap(dpy, scr->stipple, size.width, size.height, 1));
|
||||
|
||||
XSetForeground(dpy, scr->monoGC, 0);
|
||||
XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
|
||||
bPtr->dimage->width, bPtr->dimage->height);
|
||||
XFillRectangle(dpy, WMGetPixmapMaskXID(bPtr->dimage), scr->monoGC, 0, 0,
|
||||
size.width, size.height);
|
||||
|
||||
gcv.foreground = 1;
|
||||
gcv.background = 0;
|
||||
gcv.stipple = scr->stipple;
|
||||
gcv.fill_style = FillStippled;
|
||||
gcv.clip_mask = bPtr->image->mask;
|
||||
gcv.clip_mask = WMGetPixmapMaskXID(bPtr->image);
|
||||
gcv.clip_x_origin = 0;
|
||||
gcv.clip_y_origin = 0;
|
||||
|
||||
XChangeGC(dpy, scr->monoGC, GCForeground | GCBackground | GCStipple
|
||||
| GCFillStyle | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
|
||||
|
||||
XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
|
||||
bPtr->dimage->width, bPtr->dimage->height);
|
||||
XFillRectangle(dpy, WMGetPixmapMaskXID(bPtr->dimage), scr->monoGC, 0, 0,
|
||||
size.width, size.height);
|
||||
|
||||
gcv.fill_style = FillSolid;
|
||||
gcv.clip_mask = None;
|
||||
@@ -271,15 +279,17 @@ void WMSetButtonImage(WMButton * bPtr, WMPixmap * image)
|
||||
bPtr->image = WMRetainPixmap(image);
|
||||
|
||||
if (bPtr->dimage) {
|
||||
bPtr->dimage->pixmap = None;
|
||||
WMLeakPixmapXID(bPtr->dimage);
|
||||
WMReleasePixmap(bPtr->dimage);
|
||||
bPtr->dimage = NULL;
|
||||
}
|
||||
|
||||
if (image) {
|
||||
WMSize size = WMGetPixmapSize(image);
|
||||
bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
|
||||
image->pixmap, None,
|
||||
image->width, image->height, image->depth);
|
||||
WMGetPixmapXID(image), None,
|
||||
size.width, size.height,
|
||||
WMGetPixmapDepth(image));
|
||||
updateDisabledMask(bPtr);
|
||||
}
|
||||
|
||||
@@ -763,7 +773,7 @@ static void destroyButton(Button * bPtr)
|
||||
|
||||
if (bPtr->dimage) {
|
||||
/* yuck.. kluge */
|
||||
bPtr->dimage->pixmap = None;
|
||||
WMLeakPixmapXID(bPtr->dimage);
|
||||
|
||||
WMReleasePixmap(bPtr->dimage);
|
||||
}
|
||||
|
||||
@@ -560,7 +560,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->grayBrightnessS), pixmap->pixmap,
|
||||
W_PaintText(W_VIEW(panel->grayBrightnessS), WMGetPixmapXID(pixmap),
|
||||
panel->font12, 2, 0, 100, WALeft, scrPtr->white,
|
||||
False, _("Brightness"), strlen(_("Brightness")));
|
||||
else
|
||||
@@ -581,7 +581,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
|
||||
graybuttoncolor = WMCreateRGBColor(scrPtr, (255 / 6) * i << 8,
|
||||
(255 / 6) * i << 8, (255 / 6) * i << 8, True);
|
||||
WMPaintColorSwatch(graybuttoncolor, pixmap->pixmap, 0, 0, 15, 15);
|
||||
WMPaintColorSwatch(graybuttoncolor, WMGetPixmapXID(pixmap), 0, 0, 15, 15);
|
||||
WMReleaseColor(graybuttoncolor);
|
||||
|
||||
panel->grayPresetBtn[i] = WMCreateCommandButton(panel->grayFrm);
|
||||
@@ -635,7 +635,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->rgbRedS), pixmap->pixmap, panel->font12,
|
||||
W_PaintText(W_VIEW(panel->rgbRedS), WMGetPixmapXID(pixmap), panel->font12,
|
||||
2, 0, 100, WALeft, scrPtr->white, False, _("Red"), strlen(_("Red")));
|
||||
else
|
||||
wwarning(_("Color Panel: Could not allocate memory"));
|
||||
@@ -666,7 +666,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->rgbGreenS), pixmap->pixmap, panel->font12,
|
||||
W_PaintText(W_VIEW(panel->rgbGreenS), WMGetPixmapXID(pixmap), panel->font12,
|
||||
2, 0, 100, WALeft, scrPtr->white, False, _("Green"), strlen(_("Green")));
|
||||
else
|
||||
wwarning(_("Color Panel: Could not allocate memory"));
|
||||
@@ -697,7 +697,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->rgbBlueS), pixmap->pixmap, panel->font12,
|
||||
W_PaintText(W_VIEW(panel->rgbBlueS), WMGetPixmapXID(pixmap), panel->font12,
|
||||
2, 0, 100, WALeft, scrPtr->white, False, _("Blue"), strlen(_("Blue")));
|
||||
else
|
||||
wwarning(_("Color Panel: Could not allocate memory"));
|
||||
@@ -772,7 +772,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->cmykCyanS), pixmap->pixmap, panel->font12,
|
||||
W_PaintText(W_VIEW(panel->cmykCyanS), WMGetPixmapXID(pixmap), panel->font12,
|
||||
2, 0, 100, WALeft, scrPtr->black, False, _("Cyan"), strlen(_("Cyan")));
|
||||
else
|
||||
wwarning(_("Color Panel: Could not allocate memory"));
|
||||
@@ -803,7 +803,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->cmykMagentaS), pixmap->pixmap, panel->font12,
|
||||
W_PaintText(W_VIEW(panel->cmykMagentaS), WMGetPixmapXID(pixmap), panel->font12,
|
||||
2, 0, 100, WALeft, scrPtr->black, False, _("Magenta"), strlen(_("Magenta")));
|
||||
else
|
||||
wwarning(_("Color Panel: Could not allocate memory"));
|
||||
@@ -835,7 +835,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->cmykYellowS), pixmap->pixmap, panel->font12,
|
||||
W_PaintText(W_VIEW(panel->cmykYellowS), WMGetPixmapXID(pixmap), panel->font12,
|
||||
2, 0, 100, WALeft, scrPtr->black, False, _("Yellow"), strlen(_("Yellow")));
|
||||
else
|
||||
wwarning(_("Color Panel: Could not allocate memory"));
|
||||
@@ -868,7 +868,7 @@ static WMColorPanel *makeColorPanel(WMScreen * scrPtr, const char *name)
|
||||
RReleaseImage(image);
|
||||
|
||||
if (pixmap)
|
||||
W_PaintText(W_VIEW(panel->cmykBlackS), pixmap->pixmap, panel->font12,
|
||||
W_PaintText(W_VIEW(panel->cmykBlackS), WMGetPixmapXID(pixmap), panel->font12,
|
||||
2, 0, 100, WALeft, scrPtr->black, False, _("Black"), strlen(_("Black")));
|
||||
else
|
||||
wwarning(_("Color Panel: Could not allocate memory"));
|
||||
@@ -1292,7 +1292,7 @@ void WMSetColorPanelPickerMode(WMColorPanel * panel, WMColorPanelMode mode)
|
||||
panel->mode = mode;
|
||||
}
|
||||
|
||||
WMColor *WMGetColorPanelColor(WMColorPanel * panel)
|
||||
WMColor* WMGetColorPanelColor(WMColorPanel * panel)
|
||||
{
|
||||
return WMGetColorWellColor(panel->colorWell);
|
||||
}
|
||||
@@ -1301,9 +1301,9 @@ void WMSetColorPanelColor(WMColorPanel * panel, WMColor * color)
|
||||
{
|
||||
WMSetColorWellColor(panel->colorWell, color);
|
||||
|
||||
panel->color.rgb.red = color->color.red >> 8;
|
||||
panel->color.rgb.green = color->color.green >> 8;
|
||||
panel->color.rgb.blue = color->color.blue >> 8;
|
||||
panel->color.rgb.red = (WMRedComponentOfColor(color) >> 8);
|
||||
panel->color.rgb.green = (WMGreenComponentOfColor(color) >> 8);
|
||||
panel->color.rgb.blue = (WMBlueComponentOfColor(color) >> 8);
|
||||
panel->color.set = cpRGB;
|
||||
|
||||
if (panel->mode == panel->lastChanged)
|
||||
@@ -2711,7 +2711,7 @@ static void hsbUpdateBrightnessGradient(W_ColorPanel * panel)
|
||||
RReleaseImage(sliderImg);
|
||||
|
||||
if (sliderPxmp)
|
||||
W_PaintText(W_VIEW(panel->hsbBrightnessS), sliderPxmp->pixmap,
|
||||
W_PaintText(W_VIEW(panel->hsbBrightnessS), WMGetPixmapXID(sliderPxmp),
|
||||
panel->font12, 2, 0, 100, WALeft, scr->white,
|
||||
False, _("Brightness"), strlen(_("Brightness")));
|
||||
else
|
||||
@@ -2744,7 +2744,7 @@ static void hsbUpdateSaturationGradient(W_ColorPanel * panel)
|
||||
RReleaseImage(sliderImg);
|
||||
|
||||
if (sliderPxmp)
|
||||
W_PaintText(W_VIEW(panel->hsbSaturationS), sliderPxmp->pixmap,
|
||||
W_PaintText(W_VIEW(panel->hsbSaturationS), WMGetPixmapXID(sliderPxmp),
|
||||
panel->font12, 2, 0, 100, WALeft,
|
||||
from.hsv.value < 128 ? scr->white : scr->black, False,
|
||||
_("Saturation"), strlen(_("Saturation")));
|
||||
@@ -2779,7 +2779,7 @@ static void hsbUpdateHueGradient(W_ColorPanel * panel)
|
||||
RReleaseImage(sliderImg);
|
||||
|
||||
if (sliderPxmp)
|
||||
W_PaintText(W_VIEW(panel->hsbHueS), sliderPxmp->pixmap,
|
||||
W_PaintText(W_VIEW(panel->hsbHueS), WMGetPixmapXID(sliderPxmp),
|
||||
panel->font12, 2, 0, 100, WALeft,
|
||||
hsvcolor.value < 128 ? scr->white : scr->black, False, _("Hue"), strlen(_("Hue")));
|
||||
else
|
||||
|
||||
@@ -3,8 +3,17 @@
|
||||
* This event handling stuff was inspired on Tk.
|
||||
*/
|
||||
|
||||
#include "wconfig.h"
|
||||
#include "WINGsP.h"
|
||||
|
||||
#if !HAVE_POLL
|
||||
# error poll(2) is not present on this system
|
||||
#endif
|
||||
|
||||
#if HAVE_POLL_H
|
||||
# include <poll.h>
|
||||
#endif
|
||||
|
||||
/* table to map event types to event masks */
|
||||
static const unsigned long eventMasks[] = {
|
||||
0,
|
||||
@@ -349,6 +358,9 @@ int WMIsDoubleClick(XEvent * event)
|
||||
*/
|
||||
static Bool waitForEvent(Display * dpy, unsigned long xeventmask, Bool waitForInput)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
int timeout, inputfd;
|
||||
|
||||
XSync(dpy, False);
|
||||
if (xeventmask == 0) {
|
||||
if (XPending(dpy))
|
||||
@@ -361,7 +373,18 @@ static Bool waitForEvent(Display * dpy, unsigned long xeventmask, Bool waitForIn
|
||||
}
|
||||
}
|
||||
|
||||
return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
|
||||
inputfd = ConnectionNumber(dpy);
|
||||
|
||||
if (inputfd < 0) {
|
||||
return False;
|
||||
}
|
||||
|
||||
pfd.fd = inputfd;
|
||||
pfd.events = POLLIN;
|
||||
|
||||
timeout = waitForInput ? W_DelayUntilNextTimerEvent_millis() : 0;
|
||||
|
||||
return poll(&pfd, 1, timeout) > 0;
|
||||
}
|
||||
|
||||
void WMNextEvent(Display * dpy, XEvent * event)
|
||||
|
||||
@@ -180,7 +180,7 @@ static WMFilePanel *makeFilePanel(WMScreen * scrPtr, const char *name, const cha
|
||||
WMResizeWidget(fPtr->iconLabel, 64, 64);
|
||||
WMMoveWidget(fPtr->iconLabel, 0, 0);
|
||||
WMSetLabelImagePosition(fPtr->iconLabel, WIPImageOnly);
|
||||
icon = WMCreateApplicationIconBlendedPixmap(scrPtr, (RColor *) NULL);
|
||||
icon = WMCreateApplicationIconGreyedPixmap(scrPtr);
|
||||
if (icon) {
|
||||
WMSetLabelImage(fPtr->iconLabel, icon);
|
||||
WMReleasePixmap(icon);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,825 +0,0 @@
|
||||
|
||||
#include "WINGsP.h"
|
||||
#include "WUtil.h"
|
||||
#include "wconfig.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <X11/Xft/Xft.h>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
/* XXX TODO */
|
||||
char *WMFontPanelFontChangedNotification = "WMFontPanelFontChangedNotification";
|
||||
|
||||
typedef struct W_FontPanel {
|
||||
WMWindow *win;
|
||||
|
||||
WMFrame *upperF;
|
||||
WMTextField *sampleT;
|
||||
|
||||
WMSplitView *split;
|
||||
|
||||
WMFrame *lowerF;
|
||||
WMLabel *famL;
|
||||
WMList *famLs;
|
||||
WMLabel *typL;
|
||||
WMList *typLs;
|
||||
WMLabel *sizL;
|
||||
WMTextField *sizT;
|
||||
WMList *sizLs;
|
||||
|
||||
WMAction2 *action;
|
||||
void *data;
|
||||
|
||||
WMButton *revertB;
|
||||
WMButton *setB;
|
||||
|
||||
WMPropList *fdb;
|
||||
} FontPanel;
|
||||
|
||||
#define MIN_UPPER_HEIGHT 20
|
||||
#define MIN_LOWER_HEIGHT 140
|
||||
|
||||
#define BUTTON_SPACE_HEIGHT 40
|
||||
|
||||
#define MIN_WIDTH 250
|
||||
#define MIN_HEIGHT (MIN_UPPER_HEIGHT+MIN_LOWER_HEIGHT+BUTTON_SPACE_HEIGHT)
|
||||
|
||||
#define DEF_UPPER_HEIGHT 60
|
||||
#define DEF_LOWER_HEIGHT 310
|
||||
|
||||
#define DEF_WIDTH 320
|
||||
#define DEF_HEIGHT (DEF_UPPER_HEIGHT+DEF_LOWER_HEIGHT)
|
||||
|
||||
static const int scalableFontSizes[] = {
|
||||
8,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
14,
|
||||
16,
|
||||
18,
|
||||
20,
|
||||
24,
|
||||
36,
|
||||
48,
|
||||
64
|
||||
};
|
||||
|
||||
static void setFontPanelFontName(FontPanel * panel, const char *family, const char *style, double size);
|
||||
|
||||
static int isXLFD(const char *font, int *length_ret);
|
||||
|
||||
static void arrangeLowerFrame(FontPanel * panel);
|
||||
|
||||
static void familyClick(WMWidget *, void *);
|
||||
static void typefaceClick(WMWidget *, void *);
|
||||
static void sizeClick(WMWidget *, void *);
|
||||
|
||||
static void listFamilies(WMScreen * scr, WMFontPanel * panel);
|
||||
|
||||
static void splitViewConstrainCallback(WMSplitView * sPtr, int indView, int *min, int *max)
|
||||
{
|
||||
/* Parameter not used, but tell the compiler that it is ok */
|
||||
(void) sPtr;
|
||||
(void) max;
|
||||
|
||||
if (indView == 0)
|
||||
*min = MIN_UPPER_HEIGHT;
|
||||
else
|
||||
*min = MIN_LOWER_HEIGHT;
|
||||
}
|
||||
|
||||
static void notificationObserver(void *self, WMNotification * notif)
|
||||
{
|
||||
WMFontPanel *panel = (WMFontPanel *) self;
|
||||
void *object = WMGetNotificationObject(notif);
|
||||
|
||||
if (WMGetNotificationName(notif) == WMViewSizeDidChangeNotification) {
|
||||
if (object == WMWidgetView(panel->win)) {
|
||||
int h = WMWidgetHeight(panel->win);
|
||||
int w = WMWidgetWidth(panel->win);
|
||||
|
||||
WMResizeWidget(panel->split, w, h - BUTTON_SPACE_HEIGHT);
|
||||
|
||||
WMMoveWidget(panel->setB, w - 80, h - (BUTTON_SPACE_HEIGHT - 5));
|
||||
|
||||
WMMoveWidget(panel->revertB, w - 240, h - (BUTTON_SPACE_HEIGHT - 5));
|
||||
|
||||
} else if (object == WMWidgetView(panel->upperF)) {
|
||||
|
||||
if (WMWidgetHeight(panel->upperF) < MIN_UPPER_HEIGHT) {
|
||||
WMResizeWidget(panel->upperF, WMWidgetWidth(panel->upperF), MIN_UPPER_HEIGHT);
|
||||
} else {
|
||||
WMResizeWidget(panel->sampleT, WMWidgetWidth(panel->upperF) - 20,
|
||||
WMWidgetHeight(panel->upperF) - 10);
|
||||
}
|
||||
|
||||
} else if (object == WMWidgetView(panel->lowerF)) {
|
||||
|
||||
if (WMWidgetHeight(panel->lowerF) < MIN_LOWER_HEIGHT) {
|
||||
WMResizeWidget(panel->upperF, WMWidgetWidth(panel->upperF), MIN_UPPER_HEIGHT);
|
||||
|
||||
WMMoveWidget(panel->lowerF, 0, WMWidgetHeight(panel->upperF)
|
||||
+ WMGetSplitViewDividerThickness(panel->split));
|
||||
|
||||
WMResizeWidget(panel->lowerF, WMWidgetWidth(panel->lowerF),
|
||||
WMWidgetWidth(panel->split) - MIN_UPPER_HEIGHT
|
||||
- WMGetSplitViewDividerThickness(panel->split));
|
||||
} else {
|
||||
arrangeLowerFrame(panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void closeWindow(WMWidget * w, void *data)
|
||||
{
|
||||
FontPanel *panel = (FontPanel *) data;
|
||||
|
||||
/* Parameter not used, but tell the compiler that it is ok */
|
||||
(void) w;
|
||||
|
||||
WMHideFontPanel(panel);
|
||||
}
|
||||
|
||||
static void setClickedAction(WMWidget * w, void *data)
|
||||
{
|
||||
FontPanel *panel = (FontPanel *) data;
|
||||
|
||||
/* Parameter not used, but tell the compiler that it is ok */
|
||||
(void) w;
|
||||
|
||||
if (panel->action)
|
||||
(*panel->action) (panel, panel->data);
|
||||
}
|
||||
|
||||
static void revertClickedAction(WMWidget * w, void *data)
|
||||
{
|
||||
/* Parameter not used, but tell the compiler that it is ok */
|
||||
(void) w;
|
||||
(void) data;
|
||||
|
||||
/*FontPanel *panel = (FontPanel*)data; */
|
||||
/* XXX TODO */
|
||||
}
|
||||
|
||||
WMFontPanel *WMGetFontPanel(WMScreen * scr)
|
||||
{
|
||||
FontPanel *panel;
|
||||
WMColor *dark, *white;
|
||||
WMFont *font;
|
||||
int divThickness;
|
||||
|
||||
if (scr->sharedFontPanel)
|
||||
return scr->sharedFontPanel;
|
||||
|
||||
panel = wmalloc(sizeof(FontPanel));
|
||||
|
||||
panel->win = WMCreateWindow(scr, "fontPanel");
|
||||
/* WMSetWidgetBackgroundColor(panel->win, WMWhiteColor(scr)); */
|
||||
WMSetWindowTitle(panel->win, _("Font Panel"));
|
||||
WMResizeWidget(panel->win, DEF_WIDTH, DEF_HEIGHT);
|
||||
WMSetWindowMinSize(panel->win, MIN_WIDTH, MIN_HEIGHT);
|
||||
WMSetViewNotifySizeChanges(WMWidgetView(panel->win), True);
|
||||
|
||||
WMSetWindowCloseAction(panel->win, closeWindow, panel);
|
||||
|
||||
panel->split = WMCreateSplitView(panel->win);
|
||||
WMResizeWidget(panel->split, DEF_WIDTH, DEF_HEIGHT - BUTTON_SPACE_HEIGHT);
|
||||
WMSetSplitViewConstrainProc(panel->split, splitViewConstrainCallback);
|
||||
|
||||
divThickness = WMGetSplitViewDividerThickness(panel->split);
|
||||
|
||||
panel->upperF = WMCreateFrame(panel->win);
|
||||
WMSetFrameRelief(panel->upperF, WRFlat);
|
||||
WMSetViewNotifySizeChanges(WMWidgetView(panel->upperF), True);
|
||||
panel->lowerF = WMCreateFrame(panel->win);
|
||||
/* WMSetWidgetBackgroundColor(panel->lowerF, WMBlackColor(scr)); */
|
||||
WMSetFrameRelief(panel->lowerF, WRFlat);
|
||||
WMSetViewNotifySizeChanges(WMWidgetView(panel->lowerF), True);
|
||||
|
||||
WMAddSplitViewSubview(panel->split, W_VIEW(panel->upperF));
|
||||
WMAddSplitViewSubview(panel->split, W_VIEW(panel->lowerF));
|
||||
|
||||
WMResizeWidget(panel->upperF, DEF_WIDTH, DEF_UPPER_HEIGHT);
|
||||
|
||||
WMResizeWidget(panel->lowerF, DEF_WIDTH, DEF_LOWER_HEIGHT);
|
||||
|
||||
WMMoveWidget(panel->lowerF, 0, 60 + divThickness);
|
||||
|
||||
white = WMWhiteColor(scr);
|
||||
dark = WMDarkGrayColor(scr);
|
||||
|
||||
panel->sampleT = WMCreateTextField(panel->upperF);
|
||||
WMResizeWidget(panel->sampleT, DEF_WIDTH - 20, 50);
|
||||
WMMoveWidget(panel->sampleT, 10, 10);
|
||||
WMSetTextFieldText(panel->sampleT, _("The quick brown fox jumps over the lazy dog"));
|
||||
|
||||
font = WMBoldSystemFontOfSize(scr, 12);
|
||||
|
||||
panel->famL = WMCreateLabel(panel->lowerF);
|
||||
WMSetWidgetBackgroundColor(panel->famL, dark);
|
||||
WMSetLabelText(panel->famL, _("Family"));
|
||||
WMSetLabelFont(panel->famL, font);
|
||||
WMSetLabelTextColor(panel->famL, white);
|
||||
WMSetLabelRelief(panel->famL, WRSunken);
|
||||
WMSetLabelTextAlignment(panel->famL, WACenter);
|
||||
|
||||
panel->famLs = WMCreateList(panel->lowerF);
|
||||
WMSetListAction(panel->famLs, familyClick, panel);
|
||||
|
||||
panel->typL = WMCreateLabel(panel->lowerF);
|
||||
WMSetWidgetBackgroundColor(panel->typL, dark);
|
||||
WMSetLabelText(panel->typL, _("Typeface"));
|
||||
WMSetLabelFont(panel->typL, font);
|
||||
WMSetLabelTextColor(panel->typL, white);
|
||||
WMSetLabelRelief(panel->typL, WRSunken);
|
||||
WMSetLabelTextAlignment(panel->typL, WACenter);
|
||||
|
||||
panel->typLs = WMCreateList(panel->lowerF);
|
||||
WMSetListAction(panel->typLs, typefaceClick, panel);
|
||||
|
||||
panel->sizL = WMCreateLabel(panel->lowerF);
|
||||
WMSetWidgetBackgroundColor(panel->sizL, dark);
|
||||
WMSetLabelText(panel->sizL, _("Size"));
|
||||
WMSetLabelFont(panel->sizL, font);
|
||||
WMSetLabelTextColor(panel->sizL, white);
|
||||
WMSetLabelRelief(panel->sizL, WRSunken);
|
||||
WMSetLabelTextAlignment(panel->sizL, WACenter);
|
||||
|
||||
panel->sizT = WMCreateTextField(panel->lowerF);
|
||||
/* WMSetTextFieldAlignment(panel->sizT, WARight); */
|
||||
|
||||
panel->sizLs = WMCreateList(panel->lowerF);
|
||||
WMSetListAction(panel->sizLs, sizeClick, panel);
|
||||
|
||||
WMReleaseFont(font);
|
||||
WMReleaseColor(white);
|
||||
WMReleaseColor(dark);
|
||||
|
||||
panel->setB = WMCreateCommandButton(panel->win);
|
||||
WMResizeWidget(panel->setB, 70, 24);
|
||||
WMMoveWidget(panel->setB, 240, DEF_HEIGHT - (BUTTON_SPACE_HEIGHT - 5));
|
||||
WMSetButtonText(panel->setB, _("Set"));
|
||||
WMSetButtonAction(panel->setB, setClickedAction, panel);
|
||||
|
||||
panel->revertB = WMCreateCommandButton(panel->win);
|
||||
WMResizeWidget(panel->revertB, 70, 24);
|
||||
WMMoveWidget(panel->revertB, 80, DEF_HEIGHT - (BUTTON_SPACE_HEIGHT - 5));
|
||||
WMSetButtonText(panel->revertB, _("Revert"));
|
||||
WMSetButtonAction(panel->revertB, revertClickedAction, panel);
|
||||
|
||||
WMRealizeWidget(panel->win);
|
||||
|
||||
WMMapSubwidgets(panel->upperF);
|
||||
WMMapSubwidgets(panel->lowerF);
|
||||
WMMapSubwidgets(panel->split);
|
||||
WMMapSubwidgets(panel->win);
|
||||
|
||||
WMUnmapWidget(panel->revertB);
|
||||
|
||||
arrangeLowerFrame(panel);
|
||||
|
||||
scr->sharedFontPanel = panel;
|
||||
|
||||
/* register notification observers */
|
||||
WMAddNotificationObserver(notificationObserver, panel,
|
||||
WMViewSizeDidChangeNotification, WMWidgetView(panel->win));
|
||||
WMAddNotificationObserver(notificationObserver, panel,
|
||||
WMViewSizeDidChangeNotification, WMWidgetView(panel->upperF));
|
||||
WMAddNotificationObserver(notificationObserver, panel,
|
||||
WMViewSizeDidChangeNotification, WMWidgetView(panel->lowerF));
|
||||
|
||||
listFamilies(scr, panel);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
void WMFreeFontPanel(WMFontPanel * panel)
|
||||
{
|
||||
if (panel == WMWidgetScreen(panel->win)->sharedFontPanel) {
|
||||
WMWidgetScreen(panel->win)->sharedFontPanel = NULL;
|
||||
}
|
||||
WMRemoveNotificationObserver(panel);
|
||||
WMUnmapWidget(panel->win);
|
||||
WMDestroyWidget(panel->win);
|
||||
wfree(panel);
|
||||
}
|
||||
|
||||
void WMShowFontPanel(WMFontPanel * panel)
|
||||
{
|
||||
WMMapWidget(panel->win);
|
||||
}
|
||||
|
||||
void WMHideFontPanel(WMFontPanel * panel)
|
||||
{
|
||||
WMUnmapWidget(panel->win);
|
||||
}
|
||||
|
||||
WMFont *WMGetFontPanelFont(WMFontPanel * panel)
|
||||
{
|
||||
return WMGetTextFieldFont(panel->sampleT);
|
||||
}
|
||||
|
||||
void WMSetFontPanelFont(WMFontPanel * panel, const char *fontName)
|
||||
{
|
||||
int fname_len;
|
||||
FcPattern *pattern;
|
||||
FcChar8 *family, *style;
|
||||
double size;
|
||||
|
||||
if (!isXLFD(fontName, &fname_len)) {
|
||||
/* maybe its proper fontconfig and we can parse it */
|
||||
pattern = FcNameParse((const FcChar8 *) fontName);
|
||||
} else {
|
||||
/* maybe its proper xlfd and we can convert it to an FcPattern */
|
||||
pattern = XftXlfdParse(fontName, False, False);
|
||||
/*//FcPatternPrint(pattern); */
|
||||
}
|
||||
|
||||
if (!pattern)
|
||||
return;
|
||||
|
||||
if (FcPatternGetString(pattern, FC_FAMILY, 0, &family) == FcResultMatch)
|
||||
if (FcPatternGetString(pattern, FC_STYLE, 0, &style) == FcResultMatch)
|
||||
if (FcPatternGetDouble(pattern, "pixelsize", 0, &size) == FcResultMatch)
|
||||
setFontPanelFontName(panel, (char *)family, (char *)style, size);
|
||||
|
||||
FcPatternDestroy(pattern);
|
||||
}
|
||||
|
||||
void WMSetFontPanelAction(WMFontPanel * panel, WMAction2 * action, void *data)
|
||||
{
|
||||
panel->action = action;
|
||||
panel->data = data;
|
||||
}
|
||||
|
||||
static void arrangeLowerFrame(FontPanel * panel)
|
||||
{
|
||||
int width = WMWidgetWidth(panel->lowerF) - 55 - 30;
|
||||
int height = WMWidgetHeight(panel->split) - WMWidgetHeight(panel->upperF);
|
||||
int fw, tw, sw;
|
||||
|
||||
#define LABEL_HEIGHT 20
|
||||
|
||||
height -= WMGetSplitViewDividerThickness(panel->split);
|
||||
|
||||
height -= LABEL_HEIGHT + 8;
|
||||
|
||||
fw = (125 * width) / 235;
|
||||
tw = (110 * width) / 235;
|
||||
sw = 55;
|
||||
|
||||
WMMoveWidget(panel->famL, 10, 0);
|
||||
WMResizeWidget(panel->famL, fw, LABEL_HEIGHT);
|
||||
|
||||
WMMoveWidget(panel->famLs, 10, 23);
|
||||
WMResizeWidget(panel->famLs, fw, height);
|
||||
|
||||
WMMoveWidget(panel->typL, 10 + fw + 3, 0);
|
||||
WMResizeWidget(panel->typL, tw, LABEL_HEIGHT);
|
||||
|
||||
WMMoveWidget(panel->typLs, 10 + fw + 3, 23);
|
||||
WMResizeWidget(panel->typLs, tw, height);
|
||||
|
||||
WMMoveWidget(panel->sizL, 10 + fw + 3 + tw + 3, 0);
|
||||
WMResizeWidget(panel->sizL, sw + 4, LABEL_HEIGHT);
|
||||
|
||||
WMMoveWidget(panel->sizT, 10 + fw + 3 + tw + 3, 23);
|
||||
WMResizeWidget(panel->sizT, sw + 4, 20);
|
||||
|
||||
WMMoveWidget(panel->sizLs, 10 + fw + 3 + tw + 3, 46);
|
||||
WMResizeWidget(panel->sizLs, sw + 4, height - 23);
|
||||
}
|
||||
|
||||
#define NUM_FIELDS 14
|
||||
|
||||
static int isXLFD(const char *font, int *length_ret)
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
*length_ret = 0;
|
||||
while (*font) {
|
||||
(*length_ret)++;
|
||||
if (*font++ == '-')
|
||||
c++;
|
||||
}
|
||||
|
||||
return c == NUM_FIELDS;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *typeface;
|
||||
WMArray *sizes;
|
||||
} Typeface;
|
||||
|
||||
typedef struct {
|
||||
char *name; /* gotta love simplicity */
|
||||
WMArray *typefaces;
|
||||
} Family;
|
||||
|
||||
static int compare_int(const void *a, const void *b)
|
||||
{
|
||||
int i1 = *(int *)a;
|
||||
int i2 = *(int *)b;
|
||||
|
||||
if (i1 < i2)
|
||||
return -1;
|
||||
else if (i1 > i2)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void addSizeToTypeface(Typeface * face, int size)
|
||||
{
|
||||
if (size == 0) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < wlengthof(scalableFontSizes); j++) {
|
||||
size = scalableFontSizes[j];
|
||||
|
||||
if (!WMCountInArray(face->sizes, (void *)(uintptr_t) size)) {
|
||||
WMAddToArray(face->sizes, (void *)(uintptr_t) size);
|
||||
}
|
||||
}
|
||||
WMSortArray(face->sizes, compare_int);
|
||||
} else {
|
||||
if (!WMCountInArray(face->sizes, (void *)(uintptr_t) size)) {
|
||||
WMAddToArray(face->sizes, (void *)(uintptr_t) size);
|
||||
WMSortArray(face->sizes, compare_int);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addTypefaceToXftFamily(Family * fam, const char *style)
|
||||
{
|
||||
Typeface *face;
|
||||
WMArrayIterator i;
|
||||
|
||||
if (fam->typefaces) {
|
||||
WM_ITERATE_ARRAY(fam->typefaces, face, i) {
|
||||
if (strcmp(face->typeface, style) != 0)
|
||||
continue; /* go to next interation */
|
||||
addSizeToTypeface(face, 0);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
fam->typefaces = WMCreateArray(4);
|
||||
}
|
||||
|
||||
face = wmalloc(sizeof(Typeface));
|
||||
|
||||
face->typeface = wstrdup(style);
|
||||
face->sizes = WMCreateArray(4);
|
||||
addSizeToTypeface(face, 0);
|
||||
|
||||
WMAddToArray(fam->typefaces, face);
|
||||
}
|
||||
|
||||
/*
|
||||
* families (same family name) (Hashtable of family -> array)
|
||||
* registries (same family but different registries)
|
||||
*
|
||||
*/
|
||||
static void addFontToXftFamily(WMHashTable * families, const char *name, const char *style)
|
||||
{
|
||||
WMArrayIterator i;
|
||||
WMArray *array;
|
||||
Family *fam;
|
||||
|
||||
array = WMHashGet(families, name);
|
||||
if (array) {
|
||||
WM_ITERATE_ARRAY(array, fam, i) {
|
||||
if (strcmp(fam->name, name) == 0)
|
||||
addTypefaceToXftFamily(fam, style);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
array = WMCreateArray(8);
|
||||
|
||||
fam = wmalloc(sizeof(Family));
|
||||
|
||||
fam->name = wstrdup(name);
|
||||
|
||||
addTypefaceToXftFamily(fam, style);
|
||||
|
||||
WMAddToArray(array, fam);
|
||||
|
||||
WMHashInsert(families, fam->name, array);
|
||||
}
|
||||
|
||||
static void listFamilies(WMScreen * scr, WMFontPanel * panel)
|
||||
{
|
||||
FcObjectSet *os = 0;
|
||||
FcFontSet *fs;
|
||||
FcPattern *pat;
|
||||
WMHashTable *families;
|
||||
WMHashEnumerator enumer;
|
||||
WMArray *array;
|
||||
int i;
|
||||
|
||||
pat = FcPatternCreate();
|
||||
os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, NULL);
|
||||
fs = FcFontList(0, pat, os);
|
||||
if (!fs) {
|
||||
WMRunAlertPanel(scr, panel->win, _("Error"),
|
||||
_("Could not init font config library\n"), _("OK"), NULL, NULL);
|
||||
return;
|
||||
}
|
||||
if (pat)
|
||||
FcPatternDestroy(pat);
|
||||
|
||||
families = WMCreateStringHashTable();
|
||||
|
||||
if (fs) {
|
||||
for (i = 0; i < fs->nfont; i++) {
|
||||
FcChar8 *family;
|
||||
FcChar8 *style;
|
||||
|
||||
if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch)
|
||||
if (FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch)
|
||||
addFontToXftFamily(families, (char *)family, (char *)style);
|
||||
}
|
||||
FcFontSetDestroy(fs);
|
||||
}
|
||||
|
||||
enumer = WMEnumerateHashTable(families);
|
||||
|
||||
while ((array = WMNextHashEnumeratorItem(&enumer))) {
|
||||
WMArrayIterator i;
|
||||
Family *fam;
|
||||
char buffer[256];
|
||||
WMListItem *item;
|
||||
|
||||
WM_ITERATE_ARRAY(array, fam, i) {
|
||||
strlcpy(buffer, fam->name, sizeof(buffer));
|
||||
item = WMAddListItem(panel->famLs, buffer);
|
||||
|
||||
item->clientData = fam;
|
||||
}
|
||||
|
||||
WMFreeArray(array);
|
||||
}
|
||||
|
||||
WMSortListItems(panel->famLs);
|
||||
|
||||
WMFreeHashTable(families);
|
||||
}
|
||||
|
||||
static void getSelectedFont(FontPanel * panel, char buffer[], int bufsize)
|
||||
{
|
||||
WMListItem *item;
|
||||
Family *family;
|
||||
Typeface *face;
|
||||
char *size;
|
||||
|
||||
item = WMGetListSelectedItem(panel->famLs);
|
||||
if (!item)
|
||||
return;
|
||||
family = (Family *) item->clientData;
|
||||
|
||||
item = WMGetListSelectedItem(panel->typLs);
|
||||
if (!item)
|
||||
return;
|
||||
face = (Typeface *) item->clientData;
|
||||
|
||||
size = WMGetTextFieldText(panel->sizT);
|
||||
|
||||
snprintf(buffer, bufsize, "%s:style=%s:pixelsize=%s", family->name, face->typeface, size);
|
||||
|
||||
wfree(size);
|
||||
}
|
||||
|
||||
static void preview(FontPanel * panel)
|
||||
{
|
||||
char buffer[512];
|
||||
WMFont *font;
|
||||
|
||||
getSelectedFont(panel, buffer, sizeof(buffer));
|
||||
font = WMCreateFont(WMWidgetScreen(panel->win), buffer);
|
||||
if (font) {
|
||||
WMSetTextFieldFont(panel->sampleT, font);
|
||||
WMReleaseFont(font);
|
||||
}
|
||||
}
|
||||
|
||||
static void familyClick(WMWidget * w, void *data)
|
||||
{
|
||||
WMList *lPtr = (WMList *) w;
|
||||
WMListItem *item;
|
||||
Family *family;
|
||||
Typeface *face;
|
||||
FontPanel *panel = (FontPanel *) data;
|
||||
WMArrayIterator i;
|
||||
/* current typeface and size */
|
||||
char *oface = NULL;
|
||||
char *osize = NULL;
|
||||
int facei = -1;
|
||||
int sizei = -1;
|
||||
|
||||
/* must try to keep the same typeface and size for the new family */
|
||||
item = WMGetListSelectedItem(panel->typLs);
|
||||
if (item)
|
||||
oface = wstrdup(item->text);
|
||||
|
||||
osize = WMGetTextFieldText(panel->sizT);
|
||||
|
||||
item = WMGetListSelectedItem(lPtr);
|
||||
family = (Family *) item->clientData;
|
||||
|
||||
WMClearList(panel->typLs);
|
||||
|
||||
WM_ITERATE_ARRAY(family->typefaces, face, i) {
|
||||
char buffer[256];
|
||||
int top = 0;
|
||||
WMListItem *fitem;
|
||||
|
||||
strlcpy(buffer, face->typeface, sizeof(buffer));
|
||||
if (strcasecmp(face->typeface, "Roman") == 0)
|
||||
top = 1;
|
||||
if (strcasecmp(face->typeface, "Regular") == 0)
|
||||
top = 1;
|
||||
if (top)
|
||||
fitem = WMInsertListItem(panel->typLs, 0, buffer);
|
||||
else
|
||||
fitem = WMAddListItem(panel->typLs, buffer);
|
||||
fitem->clientData = face;
|
||||
}
|
||||
|
||||
if (oface) {
|
||||
facei = WMFindRowOfListItemWithTitle(panel->typLs, oface);
|
||||
wfree(oface);
|
||||
}
|
||||
if (facei < 0) {
|
||||
facei = 0;
|
||||
}
|
||||
WMSelectListItem(panel->typLs, facei);
|
||||
typefaceClick(panel->typLs, panel);
|
||||
|
||||
if (osize) {
|
||||
sizei = WMFindRowOfListItemWithTitle(panel->sizLs, osize);
|
||||
}
|
||||
if (sizei >= 0) {
|
||||
WMSelectListItem(panel->sizLs, sizei);
|
||||
sizeClick(panel->sizLs, panel);
|
||||
}
|
||||
|
||||
if (osize)
|
||||
wfree(osize);
|
||||
|
||||
preview(panel);
|
||||
}
|
||||
|
||||
static void typefaceClick(WMWidget * w, void *data)
|
||||
{
|
||||
FontPanel *panel = (FontPanel *) data;
|
||||
WMListItem *item;
|
||||
Typeface *face;
|
||||
WMArrayIterator i;
|
||||
char buffer[32];
|
||||
|
||||
char *osize = NULL;
|
||||
int sizei = -1;
|
||||
void *size;
|
||||
|
||||
/* Parameter not used, but tell the compiler that it is ok */
|
||||
(void) w;
|
||||
|
||||
osize = WMGetTextFieldText(panel->sizT);
|
||||
|
||||
item = WMGetListSelectedItem(panel->typLs);
|
||||
face = (Typeface *) item->clientData;
|
||||
|
||||
WMClearList(panel->sizLs);
|
||||
|
||||
WM_ITERATE_ARRAY(face->sizes, size, i) {
|
||||
if (size != NULL) {
|
||||
int size_int = (intptr_t) size;
|
||||
|
||||
sprintf(buffer, "%i", size_int);
|
||||
|
||||
WMAddListItem(panel->sizLs, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (osize) {
|
||||
sizei = WMFindRowOfListItemWithTitle(panel->sizLs, osize);
|
||||
}
|
||||
if (sizei < 0) {
|
||||
sizei = WMFindRowOfListItemWithTitle(panel->sizLs, "12");
|
||||
}
|
||||
if (sizei < 0) {
|
||||
sizei = 0;
|
||||
}
|
||||
WMSelectListItem(panel->sizLs, sizei);
|
||||
WMSetListPosition(panel->sizLs, sizei);
|
||||
sizeClick(panel->sizLs, panel);
|
||||
|
||||
if (osize)
|
||||
wfree(osize);
|
||||
|
||||
preview(panel);
|
||||
}
|
||||
|
||||
static void sizeClick(WMWidget * w, void *data)
|
||||
{
|
||||
FontPanel *panel = (FontPanel *) data;
|
||||
WMListItem *item;
|
||||
|
||||
/* Parameter not used, but tell the compiler that it is ok */
|
||||
(void) w;
|
||||
|
||||
item = WMGetListSelectedItem(panel->sizLs);
|
||||
WMSetTextFieldText(panel->sizT, item->text);
|
||||
|
||||
WMSelectTextFieldRange(panel->sizT, wmkrange(0, strlen(item->text)));
|
||||
|
||||
preview(panel);
|
||||
}
|
||||
|
||||
static void setFontPanelFontName(FontPanel * panel, const char *family, const char *style, double size)
|
||||
{
|
||||
int famrow;
|
||||
int stlrow;
|
||||
int sz;
|
||||
char asize[64];
|
||||
void *vsize;
|
||||
WMListItem *item;
|
||||
Family *fam;
|
||||
Typeface *face;
|
||||
WMArrayIterator i;
|
||||
|
||||
famrow = WMFindRowOfListItemWithTitle(panel->famLs, family);
|
||||
if (famrow < 0) {
|
||||
famrow = 0;
|
||||
return;
|
||||
}
|
||||
WMSelectListItem(panel->famLs, famrow);
|
||||
WMSetListPosition(panel->famLs, famrow);
|
||||
|
||||
WMClearList(panel->typLs);
|
||||
|
||||
item = WMGetListSelectedItem(panel->famLs);
|
||||
|
||||
fam = (Family *) item->clientData;
|
||||
WM_ITERATE_ARRAY(fam->typefaces, face, i) {
|
||||
char buffer[256];
|
||||
int top = 0;
|
||||
WMListItem *fitem;
|
||||
|
||||
strlcpy(buffer, face->typeface, sizeof(buffer));
|
||||
if (strcasecmp(face->typeface, "Roman") == 0)
|
||||
top = 1;
|
||||
if (top)
|
||||
fitem = WMInsertListItem(panel->typLs, 0, buffer);
|
||||
else
|
||||
fitem = WMAddListItem(panel->typLs, buffer);
|
||||
fitem->clientData = face;
|
||||
}
|
||||
|
||||
stlrow = WMFindRowOfListItemWithTitle(panel->typLs, style);
|
||||
|
||||
if (stlrow < 0) {
|
||||
stlrow = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
WMSelectListItem(panel->typLs, stlrow);
|
||||
|
||||
item = WMGetListSelectedItem(panel->typLs);
|
||||
|
||||
face = (Typeface *) item->clientData;
|
||||
|
||||
WMClearList(panel->sizLs);
|
||||
|
||||
WM_ITERATE_ARRAY(face->sizes, vsize, i) {
|
||||
char buffer[32];
|
||||
|
||||
if (vsize != NULL) {
|
||||
int size_int = (intptr_t) vsize;
|
||||
|
||||
sprintf(buffer, "%i", size_int);
|
||||
|
||||
WMAddListItem(panel->sizLs, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(asize, sizeof(asize) - 1, "%d", (int)(size + 0.5));
|
||||
|
||||
sz = WMFindRowOfListItemWithTitle(panel->sizLs, asize);
|
||||
|
||||
if (sz < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
WMSelectListItem(panel->sizLs, sz);
|
||||
sizeClick(panel->sizLs, panel);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -724,14 +725,14 @@ WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * c
|
||||
gcv.graphics_exposures = False;
|
||||
|
||||
gcv.function = GXxor;
|
||||
gcv.foreground = W_PIXEL(scrPtr->white);
|
||||
gcv.foreground = WMColorPixel(scrPtr->white);
|
||||
if (gcv.foreground == 0)
|
||||
gcv.foreground = 1;
|
||||
scrPtr->xorGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCFunction
|
||||
| GCGraphicsExposures | GCForeground, &gcv);
|
||||
|
||||
gcv.function = GXxor;
|
||||
gcv.foreground = W_PIXEL(scrPtr->gray);
|
||||
gcv.foreground = WMColorPixel(scrPtr->gray);
|
||||
gcv.subwindow_mode = IncludeInferiors;
|
||||
scrPtr->ixorGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCFunction
|
||||
| GCGraphicsExposures | GCForeground | GCSubwindowMode, &gcv);
|
||||
@@ -742,8 +743,8 @@ WMScreen *WMCreateScreenWithRContext(Display * display, int screen, RContext * c
|
||||
scrPtr->clipGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCFunction | GCGraphicsExposures, &gcv);
|
||||
|
||||
stipple = XCreateBitmapFromData(display, W_DRAWABLE(scrPtr), STIPPLE_BITS, STIPPLE_WIDTH, STIPPLE_HEIGHT);
|
||||
gcv.foreground = W_PIXEL(scrPtr->darkGray);
|
||||
gcv.background = W_PIXEL(scrPtr->gray);
|
||||
gcv.foreground = WMColorPixel(scrPtr->darkGray);
|
||||
gcv.background = WMColorPixel(scrPtr->gray);
|
||||
gcv.fill_style = FillStippled;
|
||||
gcv.stipple = stipple;
|
||||
scrPtr->stippleGC = XCreateGC(display, W_DRAWABLE(scrPtr),
|
||||
@@ -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;
|
||||
@@ -1011,7 +1000,7 @@ void WMSetWidgetBackgroundColor(WMWidget * w, WMColor * color)
|
||||
WMRedisplayWidget(w);
|
||||
}
|
||||
|
||||
WMColor *WMGetWidgetBackgroundColor(WMWidget * w)
|
||||
WMColor * WMGetWidgetBackgroundColor(WMWidget * w)
|
||||
{
|
||||
return W_VIEW(w)->backColor;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "wings-rs-tests"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
insta = "1.47"
|
||||
insta-image = { version = "1.0", features = ["png"] }
|
||||
png = "0.18"
|
||||
tempdir = "0.3.7"
|
||||
wings-rs = { path = "../wings-rs" }
|
||||
wrlib-rs = { path = "../../wrlib-rs" }
|
||||
wutil-rs = { path = "../../wutil-rs" }
|
||||
x11 = "2.21.0"
|
||||
|
||||
[profile.dev.package]
|
||||
insta.opt-level = 3
|
||||
similar.opt-level = 3
|
||||
@@ -0,0 +1,100 @@
|
||||
AUTOMAKE_OPTIONS =
|
||||
|
||||
RUST_SOURCES = \
|
||||
examples/font_panel.rs \
|
||||
src/headless/mod.rs \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xvfb__tests__blank_screen.snap \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xvfb__tests__blank_screen.snap.png \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xvfb__tests__snowlamp_in_window.snap \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xvfb__tests__snowlamp_in_window.snap.png \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xvfb__tests__xeyes.snap \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xvfb__tests__xeyes.snap.png \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xwd__tests__snowlamp_encoded.snap \
|
||||
src/headless/snapshots/wings_rs_tests__headless__xwd__tests__snowlamp_encoded.snap.png \
|
||||
src/headless/snowlamp.xwd \
|
||||
src/headless/xvfb.rs \
|
||||
src/headless/xwd.rs \
|
||||
src/lib.rs \
|
||||
src/logo_data.rs \
|
||||
tests/font_panel_tests.rs \
|
||||
tests/system_pixmap_tests.rs \
|
||||
tests/wmpixmap_tests.rs \
|
||||
tests/snapshots/font_panel_tests__font_panel.snap \
|
||||
tests/snapshots/font_panel_tests__font_panel.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__arrow_down.snap \
|
||||
tests/snapshots/system_pixmap_tests__arrow_down.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__arrow_left.snap \
|
||||
tests/snapshots/system_pixmap_tests__arrow_left.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__arrow_right.snap \
|
||||
tests/snapshots/system_pixmap_tests__arrow_right.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__arrow_up.snap \
|
||||
tests/snapshots/system_pixmap_tests__arrow_up.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__check_mark.snap \
|
||||
tests/snapshots/system_pixmap_tests__check_mark.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_down.snap \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_down.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_left.snap \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_left.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_right.snap \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_right.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_up.snap \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_arrow_up.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_return_arrow.snap \
|
||||
tests/snapshots/system_pixmap_tests__highlighted_return_arrow.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__return_arrow.snap \
|
||||
tests/snapshots/system_pixmap_tests__return_arrow.snap.png \
|
||||
tests/snapshots/system_pixmap_tests__scroller_dimple.snap \
|
||||
tests/snapshots/system_pixmap_tests__scroller_dimple.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__empty_pixmap_on_window.snap \
|
||||
tests/snapshots/wmpixmap_tests__empty_pixmap_on_window.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__empty_window.snap \
|
||||
tests/snapshots/wmpixmap_tests__empty_window.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_blended_base_pixmap.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_blended_base_pixmap.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_blended_cleared_window.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_blended_cleared_window.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_blended_with_grey.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_blended_with_grey.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_pixmap.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_pixmap.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_base_pixmap.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_base_pixmap.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_cleared_window.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_cleared_window.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_non_integral_base_pixmap.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_non_integral_base_pixmap.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_non_integral_cleared_window.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_non_integral_cleared_window.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_non_integral_scaled.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_non_integral_scaled.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_with_grey.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_file_scaled_blended_with_grey.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_r_image_no_mask.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_r_image_no_mask.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__from_r_image_with_mask.snap \
|
||||
tests/snapshots/wmpixmap_tests__from_r_image_with_mask.snap.png \
|
||||
tests/snapshots/wmpixmap_tests__pixmap_from_xpm_data.snap \
|
||||
tests/snapshots/wmpixmap_tests__pixmap_from_xpm_data.snap.png
|
||||
|
||||
RUST_EXTRA = \
|
||||
Cargo.lock \
|
||||
Cargo.toml
|
||||
|
||||
Cargo.lock:
|
||||
cargo build
|
||||
|
||||
rustlib: $(RUST_SOURCES) $(RUST_EXTRA)
|
||||
cargo build
|
||||
|
||||
check-local: rustlib
|
||||
|
||||
clean-local:
|
||||
$(CARGO) clean
|
||||
|
||||
all: rustlib
|
||||
|
||||
# Tests run with cargo-nextest because it puts each application test in its own
|
||||
# process, and WINGs currently assumes that it is running in a single-threaded
|
||||
# environment.
|
||||
test: rustlib
|
||||
LD_LIBRARY_PATH=../.libs:../../wrlib/.libs $(CARGO) nextest run
|
||||
@@ -0,0 +1,13 @@
|
||||
fn main() {
|
||||
println!("cargo::rustc-link-search=../.libs");
|
||||
println!("cargo::rustc-link-search=../../wrlib/.libs");
|
||||
println!("cargo::rustc-link-arg=-lX11");
|
||||
println!("cargo::rustc-link-arg=-lWUtil");
|
||||
println!("cargo::rustc-link-arg=-lWINGs");
|
||||
println!("cargo::rustc-link-arg=-lwraster");
|
||||
println!("cargo::rustc-link-arg=-lX11");
|
||||
println!("cargo::rustc-link-arg=-lXft");
|
||||
println!("cargo::rustc-link-arg=-lpango-1.0");
|
||||
println!("cargo::rustc-link-arg=-lpangoxft-1.0");
|
||||
println!("cargo::rustc-link-arg=-lpangoft2-1.0");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use wings_rs::{font_panel::FontPanel, WINGsP::WMScreenMainLoop};
|
||||
use wings_rs_tests::LiveApplication;
|
||||
|
||||
fn main() {
|
||||
let app = LiveApplication::new("WMFontPanel");
|
||||
let mut font_panel = FontPanel::new(app.screen.as_ptr()).expect("could not create font panel");
|
||||
font_panel.show();
|
||||
unsafe {
|
||||
WMScreenMainLoop(app.screen.as_ptr());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
//! Provides utilites for integration tests that render to a headless X11 server.
|
||||
//!
|
||||
//! ## X11 `DISPLAY` value allocation
|
||||
//!
|
||||
//! When starting headless X servers, some care needs to be taken in selecting
|
||||
//! display numbers. An X server needs to have a unique display number and will
|
||||
//! not start if it is assigned a display number that is already in use.
|
||||
//!
|
||||
//! Local X servers usually leave a lockfile in `/tmp`, like `/tmp/.X0-lock` for
|
||||
//! display 0. Avoiding the use of display numbers that have a corresponding
|
||||
//! lockfile will get you part of the way towards avoiding collisions, but
|
||||
//! further coordination is provided by the [`Lock`] and
|
||||
//! [`DisplayNumberRegistry`] structs. These interfaces provide a mechanism for
|
||||
//! claiming a display number in a way that coordinates across processes and
|
||||
//! should be more efficient than simply scanning `/tmp` each time a new display
|
||||
//! number is needed.
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
io,
|
||||
path::PathBuf,
|
||||
sync::atomic::{self, AtomicU16},
|
||||
};
|
||||
|
||||
pub mod xvfb;
|
||||
pub mod xwd;
|
||||
|
||||
/// Represents a lock on a display number, which is released on drop.
|
||||
pub struct Lock {
|
||||
display: u16,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
/// Errors that may occur when trying to lock a display number.
|
||||
pub enum LockError {
|
||||
Io(io::Error),
|
||||
Locked,
|
||||
}
|
||||
|
||||
impl Lock {
|
||||
pub(crate) fn new(display: u16, path: PathBuf) -> Self {
|
||||
Lock {
|
||||
display,
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the locked `DISPLAY` value.
|
||||
pub fn display(&self) -> u16 {
|
||||
self.display
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Lock {
|
||||
fn drop(&mut self) {
|
||||
// `file` should be unlinked already, but we explicitly try to delete it
|
||||
// and unwrap the result so that errors aren't dropped silently.
|
||||
match fs::remove_file(&self.path) {
|
||||
Ok(_) => (),
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => (),
|
||||
Err(e) => panic!("could not unlink lock file: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shared across threads in this process to help keep us from repeatedly asking
|
||||
// for the same DISPLAY value.
|
||||
static NEXT_DISPLAY: AtomicU16 = AtomicU16::new(32);
|
||||
|
||||
/// Coordinates on the value of `DISPLAY` to use when creating new X11 servers.
|
||||
///
|
||||
/// Methods on `DisplayNumberRegistry` may be called across threads or in
|
||||
/// different processes to ensure that X servers are created with unique
|
||||
/// display numbers.
|
||||
pub struct DisplayNumberRegistry;
|
||||
|
||||
impl DisplayNumberRegistry {
|
||||
/// Returns a lock on the first local display number (the `N` in `:N` for
|
||||
/// the X client `DISPLAY` environment variable) that is not currently in
|
||||
/// use.
|
||||
///
|
||||
/// If no display numbers are available, returns `None`.
|
||||
///
|
||||
/// To avoid collisions with other processes, attempts are made to sniff out
|
||||
/// which display numbers are already in use by looking for lock files
|
||||
/// matching the pattern `/tmp/.X*-lock`. If such a file exists, its
|
||||
/// display number will not be used.
|
||||
///
|
||||
/// When an available display number is found, an empty lockfile is
|
||||
/// atomically created (and marking the display number as claimed to other
|
||||
/// well-behaved processes). When `Xvfb` is run using that display number,
|
||||
/// it silently overwrites the empty lockfile.
|
||||
///
|
||||
/// Any extant lockfile should be deleted by the `Drop` impl for [`Lock`],
|
||||
/// although tests that panic may leave stale lockfiles behind.
|
||||
pub fn next_unused_display() -> io::Result<Lock> {
|
||||
loop {
|
||||
let prev = NEXT_DISPLAY.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
if prev == u16::MAX {
|
||||
return Err(io::Error::other("display numbers exhausted; check /tmp/.X{n}-lock"));
|
||||
}
|
||||
let next = prev + 1;
|
||||
let path = PathBuf::from(format!("/tmp/.X{}-lock", next));
|
||||
match fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(&path) {
|
||||
Ok(_) => return Ok(Lock::new(next, path)),
|
||||
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn find_unused_display() {
|
||||
assert!(DisplayNumberRegistry::next_unused_display().is_ok());
|
||||
|
||||
assert_ne!(
|
||||
DisplayNumberRegistry::next_unused_display()
|
||||
.unwrap()
|
||||
.display,
|
||||
DisplayNumberRegistry::next_unused_display()
|
||||
.unwrap()
|
||||
.display
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/headless/xvfb.rs
|
||||
assertion_line: 387
|
||||
expression: xwd.into_png().unwrap()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/headless/xvfb.rs
|
||||
assertion_line: 416
|
||||
expression: compressed
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/headless/xvfb.rs
|
||||
assertion_line: 402
|
||||
expression: compressed
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/headless/xwd.rs
|
||||
assertion_line: 321
|
||||
expression: snowlamp_xwd.into_png().unwrap()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,471 @@
|
||||
//! Provides utilities for headlessly running and taking screenshots of X11
|
||||
//! programs in unit and integration tests.
|
||||
//!
|
||||
//! This uses [Xvfb](https://x.org/releases/X11R7.7/doc/man/man1/Xvfb.1.xhtml).
|
||||
//!
|
||||
//! Tests of this module invoke `xeyes` and `xwud` subprocesses. For all tests
|
||||
//! to pass, you may need to install these X11 utilities and make them available
|
||||
//! on your `PATH`.
|
||||
|
||||
use std::{
|
||||
ffi::CString,
|
||||
fs, io,
|
||||
process::{Child, Command, ExitStatus, Stdio},
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tempdir::TempDir;
|
||||
|
||||
use super::xwd;
|
||||
use super::Lock;
|
||||
|
||||
/// Arguments for `Xvfb`. Use `XvfbArgs::default()` for default values.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct XvfbArgs {
|
||||
/// Width of the X11 screen.
|
||||
pub width: u32,
|
||||
/// Height of the X11 screen.
|
||||
pub height: u32,
|
||||
/// Bit depth of the X11 screen.
|
||||
///
|
||||
/// This should probably be one of 8, 16, or 24. (And if you don't choose 24
|
||||
/// bits, you should have a good reason why.)
|
||||
pub depth: u8,
|
||||
}
|
||||
|
||||
impl Default for XvfbArgs {
|
||||
fn default() -> Self {
|
||||
XvfbArgs {
|
||||
width: 640,
|
||||
height: 480,
|
||||
depth: 24,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A captive `Xvfb` process, with affordances for running subprocesses that
|
||||
/// connect to it and taking screenshots.
|
||||
///
|
||||
/// When dropped, child processes will be killed automatically.
|
||||
pub struct XvfbProcess {
|
||||
lock: Lock,
|
||||
framebuffer_directory: TempDir,
|
||||
process: Child,
|
||||
subprocesses: Vec<(Child, SubprocessMonitor)>,
|
||||
is_shutdown: bool,
|
||||
}
|
||||
|
||||
impl XvfbProcess {
|
||||
/// Attempts to start `Xvfb` with default options.
|
||||
///
|
||||
/// Returns `None` if an error occurs while starting the `Xvfb` process.
|
||||
pub fn start_default(lock: Lock) -> SubprocessResult<Self> {
|
||||
XvfbProcess::start(lock, XvfbArgs::default())
|
||||
}
|
||||
|
||||
/// Starts an `Xvfb` process with options specified by `args`.
|
||||
///
|
||||
/// Returns `None` if an error occurs while starting the `Xvfb` process.
|
||||
///
|
||||
/// This function connects to the `Xvfb` server briefly to try to ensure
|
||||
/// that Xvfb is ready for clients to connect to it.
|
||||
pub fn start(lock: Lock, args: XvfbArgs) -> SubprocessResult<Self> {
|
||||
let framebuffer_directory = TempDir::new("wings_rs_xvfb").map_err(|e| {
|
||||
SubprocessError::new("Xvfb temp dir".into(), SubprocessErrorType::Spawn(e))
|
||||
})?;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
unsafe {
|
||||
// Kill children if this parent process dies catastrophically (e.g.,
|
||||
// a test raises SIGABRT).
|
||||
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL);
|
||||
}
|
||||
let process = Command::new("Xvfb")
|
||||
.arg(format!(":{}", lock.display))
|
||||
.arg("-screen")
|
||||
.arg("0")
|
||||
.arg(format!("{}x{}x{}", args.width, args.height, args.depth))
|
||||
.arg("-fbdir")
|
||||
.arg(format!("{}", framebuffer_directory.path().display()))
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|e| SubprocessError::new("Xvfb".into(), SubprocessErrorType::Spawn(e)))?;
|
||||
|
||||
let process = XvfbProcess {
|
||||
lock,
|
||||
framebuffer_directory,
|
||||
process,
|
||||
subprocesses: Vec::new(),
|
||||
is_shutdown: false,
|
||||
};
|
||||
|
||||
let now = Instant::now();
|
||||
let display_name = CString::new(
|
||||
format!(":{}", process.lock.display)
|
||||
).expect("could not construct display name");
|
||||
while now.elapsed() < Duration::from_secs(1) {
|
||||
unsafe {
|
||||
let display = x11::xlib::XOpenDisplay(display_name.as_ptr());
|
||||
if !display.is_null() {
|
||||
x11::xlib::XCloseDisplay(display);
|
||||
break;
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
return Ok(process);
|
||||
}
|
||||
|
||||
/// Returns the number in the `DISPLAY` environment variable to connect to
|
||||
/// this X11 server.
|
||||
pub fn display(&self) -> u16 {
|
||||
self.lock.display
|
||||
}
|
||||
|
||||
/// Provides access to the `Xvfb` child process wrapped by `self`.
|
||||
pub fn process(&mut self) -> &mut Child {
|
||||
&mut self.process
|
||||
}
|
||||
|
||||
/// Attempts to clean up all child processes. Returns any errors encountered, or `Ok` if none.
|
||||
///
|
||||
/// Child processes are cleaned up with the following escalating steps:
|
||||
///
|
||||
/// * Call [`SubprocessMonitor::on_xvfb_shutdown`] on each child's monitor.
|
||||
/// * Kill each of them directly (with [`Child::kill`]).
|
||||
/// * Kill the `Xvfb` process (which may cause any children still connected to that display server to exit).
|
||||
pub fn shutdown(&mut self) -> Result<(), Vec<SubprocessError>> {
|
||||
if self.is_shutdown {
|
||||
return Ok(());
|
||||
}
|
||||
self.is_shutdown = true;
|
||||
let mut errors = Vec::new();
|
||||
for (child, monitor) in self.subprocesses.iter_mut() {
|
||||
if let Err(e) = monitor.on_xvfb_shutdown(child) {
|
||||
errors.push(e);
|
||||
}
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
if let Err(e) = monitor.check_subprocess_exit(status) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
if let Err(e) = child.kill() {
|
||||
errors.push(SubprocessError::new(
|
||||
monitor.name.clone(),
|
||||
SubprocessErrorType::Kill(e),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => errors.push(SubprocessError::new(
|
||||
monitor.name.clone(),
|
||||
SubprocessErrorType::Status(e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
if let Err(e) = self.process.kill() {
|
||||
errors.push(SubprocessError::new(
|
||||
"Xvfb".into(),
|
||||
SubprocessErrorType::Kill(e),
|
||||
));
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs a simple command in this display server. No command line parsing is
|
||||
/// done, so `run("ls -la")` will probably not do what you expect (because
|
||||
/// there is no binary on your `PATH` called `ls -la`). Use [`XvfbProcess::run_args`]
|
||||
/// to provide arguments to `command`.
|
||||
pub fn run(&mut self, command: impl Into<String>) -> Result<(), SubprocessError> {
|
||||
let name = command.into();
|
||||
self.run_args(name.clone(), &name, |_c| {})
|
||||
}
|
||||
|
||||
/// Runs a command that you can provide arguments to in this display
|
||||
/// server.
|
||||
///
|
||||
/// The `f` parameter can be used to modify the `Command` before it is
|
||||
/// run (e.g., by adding arguments). For example:
|
||||
///
|
||||
/// ```
|
||||
/// # use wings_rs_tests::headless::DisplayNumberRegistry;
|
||||
/// # use wings_rs_tests::headless::xvfb::XvfbProcess;
|
||||
/// # fn main() {
|
||||
/// # let mut xvfb = XvfbProcess::start_default(DisplayNumberRegistry::next_unused_display().unwrap()).unwrap();
|
||||
/// xvfb.run_args(
|
||||
/// "demo of xeyes, angel-style",
|
||||
/// "/usr/bin/xeyes",
|
||||
/// |cmd| { cmd.arg("-biblicallyAccurate"); },
|
||||
/// ).unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn run_args(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
command: impl AsRef<std::ffi::OsStr>,
|
||||
f: impl FnOnce(&mut Command),
|
||||
) -> Result<(), SubprocessError> {
|
||||
let mut command = Command::new(command);
|
||||
command.env("DISPLAY", format!(":{}", self.lock.display));
|
||||
f(&mut command);
|
||||
let mut monitor = SubprocessMonitor::new(name.into(), command);
|
||||
monitor.with_subprocess_exit(SubprocessMonitor::require_clean_exit());
|
||||
let child = monitor.start()?;
|
||||
self.subprocesses.push((child, monitor));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks all subprocesses started with the [`XvfbProcess::run`] family of
|
||||
/// methods and returns errors for any that have an error status, or `Ok` if
|
||||
/// none do.
|
||||
pub fn check(&mut self) -> Result<(), Vec<SubprocessError>> {
|
||||
let errors: Vec<_> = self
|
||||
.subprocesses
|
||||
.iter_mut()
|
||||
.filter_map(|(child, monitor)| match child.try_wait() {
|
||||
Ok(Some(status)) => monitor.check_subprocess_exit(status).err(),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a screenshot of the current framebuffer contents in `xwd(1)`
|
||||
/// format.
|
||||
///
|
||||
/// For the same in a more generally useful format, try [`XvfbProcess::png_screenshot`].
|
||||
pub fn xwd_screenshot(&self) -> io::Result<xwd::XwdImage> {
|
||||
let path = self.framebuffer_directory.path().join("Xvfb_screen0");
|
||||
Ok(xwd::XwdImage::read(&mut fs::read(path)?.into_iter()))
|
||||
}
|
||||
|
||||
/// Returns a screenshot of the current framebuffer contents in PNG format.
|
||||
///
|
||||
/// Panics on errors.
|
||||
pub fn png_screenshot(&self) -> Vec<u8> {
|
||||
self.xwd_screenshot().unwrap().into_png().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for XvfbProcess {
|
||||
fn drop(&mut self) {
|
||||
self.shutdown().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub type SubprocessResult<T> = Result<T, SubprocessError>;
|
||||
|
||||
/// Errors that may occur when a subprocess fails to run or exit cleanly.
|
||||
#[derive(Debug)]
|
||||
pub struct SubprocessError {
|
||||
/// Meaningful name that can be used to determine what subprocess failed.
|
||||
pub name: String,
|
||||
pub err: SubprocessErrorType,
|
||||
}
|
||||
|
||||
impl SubprocessError {
|
||||
pub fn new(name: String, err: SubprocessErrorType) -> Self {
|
||||
SubprocessError { name, err }
|
||||
}
|
||||
}
|
||||
|
||||
/// Error details for `SubprocessError`.
|
||||
#[derive(Debug)]
|
||||
pub enum SubprocessErrorType {
|
||||
/// Subprocess exited with bad exit code.
|
||||
BadExit(ExitStatus),
|
||||
/// Could not spawn child process (with `io::Error` as the reason).
|
||||
Spawn(io::Error),
|
||||
/// Could not kill child process (with `io::Error` as the reason).
|
||||
Kill(io::Error),
|
||||
/// Could not query child process for exit status (with `io::Error` as the reason).
|
||||
Status(io::Error),
|
||||
}
|
||||
|
||||
/// Manages spawning a subprocess, monitoring it during execution, and killing it when `Xvfb` shuts down.
|
||||
pub struct SubprocessMonitor {
|
||||
name: String,
|
||||
command: Command,
|
||||
check_subprocess_exit: Option<Box<dyn FnMut(&str, ExitStatus) -> SubprocessResult<()>>>,
|
||||
on_xvfb_shutdown: Option<Box<dyn FnMut(&mut Child) -> SubprocessResult<()>>>,
|
||||
}
|
||||
|
||||
impl SubprocessMonitor {
|
||||
/// Creates a new monitor but does not start any subprocess yet.
|
||||
///
|
||||
/// This monitor may be configured further by calling mutators like
|
||||
/// [`SubprocessMonitor::with_subprocess_exit`]. Once configuration is
|
||||
/// completely, start the subprocess with [`SubprocessMonitor::start`].
|
||||
pub fn new(name: impl Into<String>, command: Command) -> Self {
|
||||
SubprocessMonitor {
|
||||
name: name.into(),
|
||||
command,
|
||||
check_subprocess_exit: None,
|
||||
on_xvfb_shutdown: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the callback to execute when [`SubprocessMonitor::check_subprocess_exit`] is called.
|
||||
pub fn with_subprocess_exit(
|
||||
&mut self,
|
||||
f: Box<dyn FnMut(&str, ExitStatus) -> SubprocessResult<()>>,
|
||||
) -> &mut Self {
|
||||
self.check_subprocess_exit = Some(f);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the callback to execute when
|
||||
/// [`SubprocessMonitor::on_xvfb_shutdown`] is called. It may check process
|
||||
/// status, clean up, etc.
|
||||
pub fn with_xvfb_shutdown(
|
||||
&mut self,
|
||||
f: Box<dyn FnMut(&mut Child) -> SubprocessResult<()>>,
|
||||
) -> &mut Self {
|
||||
self.on_xvfb_shutdown = Some(f);
|
||||
self
|
||||
}
|
||||
|
||||
/// Attempts to spawn a child process from the `Command` provided to
|
||||
/// [`SubprocessMonitor::new`].
|
||||
pub fn start(&mut self) -> SubprocessResult<Child> {
|
||||
self.command
|
||||
.spawn()
|
||||
.map_err(|e| SubprocessError::new(self.name.clone(), SubprocessErrorType::Spawn(e)))
|
||||
}
|
||||
|
||||
/// Called when subprocess exit status is polled. This may be used to signal
|
||||
/// an error because the exit status is bad.
|
||||
pub fn check_subprocess_exit(&mut self, status: ExitStatus) -> SubprocessResult<()> {
|
||||
if let Some(f) = &mut self.check_subprocess_exit {
|
||||
(f)(&self.name, status)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Called immediately before the parent display server is being shut
|
||||
/// down. This may be used to send `SIGINT` or otherwise clean up the child
|
||||
/// before it is killed more forcibly.
|
||||
pub fn on_xvfb_shutdown(&mut self, child: &mut Child) -> SubprocessResult<()> {
|
||||
if let Some(f) = &mut self.on_xvfb_shutdown {
|
||||
(f)(child)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a callback suitable for passing to [`SubprocessMonitor::check_subprocess_exit`]
|
||||
/// which requires that the process has exited cleanly.
|
||||
pub fn require_clean_exit() -> Box<dyn FnMut(&str, ExitStatus) -> SubprocessResult<()>> {
|
||||
Box::new(|name: &str, status: ExitStatus| {
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SubprocessError::new(
|
||||
String::from(name),
|
||||
SubprocessErrorType::BadExit(status),
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::headless::DisplayNumberRegistry;
|
||||
use super::XvfbProcess;
|
||||
use insta_image::assert_png_snapshot;
|
||||
use std::{
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// Starts Xvfb and returns a managing object, or panics.
|
||||
fn start_xvfb() -> XvfbProcess {
|
||||
XvfbProcess::start_default(
|
||||
DisplayNumberRegistry::next_unused_display().expect("cannot find a value for DISPLAY"),
|
||||
)
|
||||
.expect("cannot start Xvfb")
|
||||
}
|
||||
|
||||
/// Reads a single frame of PNG data from the PNG image in `data`. Panics on
|
||||
/// errors.
|
||||
fn read_png_metadata(data: &[u8]) -> png::OutputInfo {
|
||||
let mut reader = png::Decoder::new(std::io::Cursor::new(data))
|
||||
.read_info()
|
||||
.unwrap();
|
||||
let mut buf = vec![0; reader.output_buffer_size().unwrap()];
|
||||
reader.next_frame(&mut buf).unwrap()
|
||||
}
|
||||
|
||||
/// Launches `xeyes(1)` with colors to make sure we're doing the right thing
|
||||
/// with our color channels when encoding a screenshot tas PNG.
|
||||
fn xeyes_color(xvfb: &mut XvfbProcess) {
|
||||
xvfb.run_args("xeyes", "xeyes", |c| {
|
||||
c.arg("-outline")
|
||||
.arg("blue")
|
||||
.arg("-center")
|
||||
.arg("red")
|
||||
.arg("-fg")
|
||||
.arg("green");
|
||||
})
|
||||
.unwrap();
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn png_blank_screenshot() {
|
||||
let xvfb = start_xvfb();
|
||||
let xwd = xvfb.xwd_screenshot().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
xwd.visual_class(),
|
||||
crate::headless::xwd::VisualClass::TrueColor
|
||||
);
|
||||
|
||||
let args = crate::headless::xvfb::XvfbArgs::default();
|
||||
assert_eq!(xwd.header.depth, args.depth as u32);
|
||||
assert_eq!(xwd.header.width, args.width);
|
||||
assert_eq!(xwd.header.height, args.height);
|
||||
|
||||
assert_png_snapshot!("blank_screen", xwd.into_png().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn png_xeyes_screenshot() {
|
||||
let mut xvfb = start_xvfb();
|
||||
xeyes_color(&mut xvfb);
|
||||
|
||||
let compressed = xvfb.png_screenshot();
|
||||
let image_data = read_png_metadata(&compressed);
|
||||
|
||||
let args = crate::headless::xvfb::XvfbArgs::default();
|
||||
assert_eq!(image_data.width, args.width);
|
||||
assert_eq!(image_data.height, args.height);
|
||||
|
||||
assert_png_snapshot!("xeyes", compressed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn png_lamp_image() {
|
||||
let mut xvfb = start_xvfb();
|
||||
xvfb.run_args("xwud", "xwud", |c| {
|
||||
c.arg("-in").arg("src/headless/snowlamp.xwd");
|
||||
})
|
||||
.unwrap();
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
let compressed = xvfb.png_screenshot();
|
||||
|
||||
assert_png_snapshot!("snowlamp_in_window", compressed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
//! Provides basic support for rendering dumps of X11 windows made with [the `xwud` utility](https://gitlab.freedesktop.org/xorg/app/xwud/-/blob/master/xwud.c?ref_type=heads).
|
||||
|
||||
use std::{
|
||||
ffi::CString,
|
||||
io::Cursor,
|
||||
mem,
|
||||
};
|
||||
|
||||
const SUPPORTED_XWD_VERSION: u32 = 7;
|
||||
|
||||
/// Header for `xwd`-style dumps of X11 windows.
|
||||
///
|
||||
/// This is taken from `/usr/include/X11/XWDFile.h` on a Debian system.
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
pub struct XwdHeader {
|
||||
/// Total header size (including null-terminated window name).
|
||||
pub size: u32,
|
||||
/// Version of `xwd` that this dump came from.
|
||||
pub version: u32,
|
||||
pub _format: u32,
|
||||
/// Bit depth of the X11 server.
|
||||
pub depth: u32,
|
||||
/// Image width.
|
||||
pub width: u32,
|
||||
/// Image height.
|
||||
pub height: u32,
|
||||
pub _x_offset: u32,
|
||||
pub _byte_order: u32,
|
||||
pub _bitmap_unit: u32,
|
||||
pub _bitmap_bit_order: u32,
|
||||
pub _bitmap_pad: u32,
|
||||
pub bits_per_pixel: u32,
|
||||
pub _bytes_per_line: u32,
|
||||
/// X11 visual class for this window. See [`XwdImage::visual_class`] for a
|
||||
/// more human-interpretable value.
|
||||
pub visual_class: u32,
|
||||
/// Bitmask for the red channel in pixel data.
|
||||
pub red_mask: u32,
|
||||
/// Bitmask for the green channel in pixel data.
|
||||
pub green_mask: u32,
|
||||
/// Bitmask for the blue channel in pixel data.
|
||||
pub blue_mask: u32,
|
||||
/// Number of bits per RGB tuple in color data. (E.g., 24-bit color may
|
||||
/// actually be padded to 4 bytes.)
|
||||
pub bits_per_rgb: u32,
|
||||
pub _colormap_entries: u32,
|
||||
/// Number of colors in the colormap for the window that this dump came from.
|
||||
///
|
||||
/// Note that a colormap may exist but be unused (if the visual class is not
|
||||
/// one that uses a colormap).
|
||||
pub ncolors: u32,
|
||||
pub _window_width: u32,
|
||||
pub _window_height: u32,
|
||||
pub _window_x: u32,
|
||||
pub _window_y: u32,
|
||||
pub _window_border_width: u32,
|
||||
}
|
||||
|
||||
const HEADER_SIZE: u32 = mem::size_of::<XwdHeader>() as u32;
|
||||
|
||||
/// Color data for an entry in a colormap.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct XwdColor {
|
||||
pixel: u32,
|
||||
red: u16,
|
||||
green: u16,
|
||||
blue: u16,
|
||||
flags: u8,
|
||||
pad: u8,
|
||||
}
|
||||
|
||||
fn next_u32(bytes: &mut impl Iterator<Item = u8>) -> u32 {
|
||||
let bytes = [
|
||||
bytes.next().unwrap(),
|
||||
bytes.next().unwrap(),
|
||||
bytes.next().unwrap(),
|
||||
bytes.next().unwrap(),
|
||||
];
|
||||
u32::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
fn next_u16(bytes: &mut impl Iterator<Item = u8>) -> u16 {
|
||||
let bytes = [bytes.next().unwrap(), bytes.next().unwrap()];
|
||||
u16::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Describes how pixel data is represented in an `xwd` dump.
|
||||
///
|
||||
/// See descriptions of visual classes at
|
||||
/// <https://tronche.com/gui/x/xlib/window/visual-types.html> and [the official
|
||||
/// documentation](https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#visual_information)
|
||||
/// for more information.
|
||||
#[derive(Clone, Copy, Eq, Debug, PartialEq)]
|
||||
pub enum VisualClass {
|
||||
StaticGray,
|
||||
GrayScale,
|
||||
StaticColor,
|
||||
PseudoColor,
|
||||
TrueColor,
|
||||
DirectColor,
|
||||
}
|
||||
|
||||
impl XwdHeader {
|
||||
/// Decodes an `XwdHeader` from `bytes`, panicking on failure.
|
||||
///
|
||||
/// Leaves `bytes` pointing to the start of the window name.
|
||||
pub fn read(bytes: &mut impl Iterator<Item = u8>) -> XwdHeader {
|
||||
// This layout might change in the future, but let's just go with it for now.
|
||||
XwdHeader {
|
||||
size: next_u32(bytes),
|
||||
version: next_u32(bytes),
|
||||
_format: next_u32(bytes),
|
||||
depth: next_u32(bytes),
|
||||
width: next_u32(bytes),
|
||||
height: next_u32(bytes),
|
||||
_x_offset: next_u32(bytes),
|
||||
_byte_order: next_u32(bytes),
|
||||
_bitmap_unit: next_u32(bytes),
|
||||
_bitmap_bit_order: next_u32(bytes),
|
||||
_bitmap_pad: next_u32(bytes),
|
||||
bits_per_pixel: next_u32(bytes),
|
||||
_bytes_per_line: next_u32(bytes),
|
||||
visual_class: next_u32(bytes),
|
||||
red_mask: next_u32(bytes),
|
||||
green_mask: next_u32(bytes),
|
||||
blue_mask: next_u32(bytes),
|
||||
bits_per_rgb: next_u32(bytes),
|
||||
_colormap_entries: next_u32(bytes),
|
||||
ncolors: next_u32(bytes),
|
||||
_window_width: next_u32(bytes),
|
||||
_window_height: next_u32(bytes),
|
||||
_window_x: next_u32(bytes),
|
||||
_window_y: next_u32(bytes),
|
||||
_window_border_width: next_u32(bytes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XwdColor {
|
||||
/// Reads an `XwdColor` from `bytes`, panicking on failure.
|
||||
///
|
||||
/// Leaves `bytes` pointing to the next byte after the end of the color
|
||||
/// definition.
|
||||
pub fn read(bytes: &mut impl Iterator<Item = u8>) -> XwdColor {
|
||||
XwdColor {
|
||||
pixel: next_u32(bytes),
|
||||
red: next_u16(bytes),
|
||||
green: next_u16(bytes),
|
||||
blue: next_u16(bytes),
|
||||
flags: bytes.next().unwrap(),
|
||||
pad: bytes.next().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A decoded `xwd` dump.
|
||||
pub struct XwdImage {
|
||||
/// Header with image metadata.
|
||||
pub header: XwdHeader,
|
||||
/// Name of the X11 window that the image came from.
|
||||
pub name: CString,
|
||||
/// Colormap data for the X11 window that the image came from.
|
||||
///
|
||||
/// Note that this may be populated even if it is unused (because the
|
||||
/// window's visual class is one that does not use a colormap).
|
||||
pub colors: Vec<XwdColor>,
|
||||
/// Pixel data for the image.
|
||||
///
|
||||
/// This may be indices pointing into `colors`, or it may be actual pixel
|
||||
/// values.
|
||||
pub pixels: Vec<u8>,
|
||||
}
|
||||
|
||||
impl XwdImage {
|
||||
/// Reads an `XwdImage` from `bytes`, panicking on failure.
|
||||
pub fn read(bytes: &mut impl Iterator<Item = u8>) -> XwdImage {
|
||||
let header = XwdHeader::read(bytes);
|
||||
if header.version != SUPPORTED_XWD_VERSION {
|
||||
panic!(
|
||||
"header version {} is not supported. only version {} files are supported.",
|
||||
header.version, SUPPORTED_XWD_VERSION
|
||||
);
|
||||
}
|
||||
let name = Self::read_name(&header, bytes);
|
||||
let colors = Self::read_colors(&header, bytes);
|
||||
let pixels = bytes.collect();
|
||||
|
||||
XwdImage {
|
||||
header,
|
||||
name,
|
||||
colors,
|
||||
pixels,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a colormap for the image described by `header` from `bytes`.
|
||||
///
|
||||
/// `bytes` must point at the start of the colormap data. Leaves `bytes`
|
||||
/// pointing at the first byte of the next item in the data stream.
|
||||
pub fn read_colors(header: &XwdHeader, bytes: &mut impl Iterator<Item = u8>) -> Vec<XwdColor> {
|
||||
let mut colors = Vec::with_capacity(header.ncolors as usize);
|
||||
for _ in 0..header.ncolors {
|
||||
colors.push(XwdColor::read(bytes));
|
||||
}
|
||||
colors
|
||||
}
|
||||
|
||||
/// Reads the X11 window name from `bytes`.
|
||||
///
|
||||
/// `bytes` must point at the start of the window name. Leaves `bytes`
|
||||
/// pointing at the first byte of the next item in the data stream.
|
||||
///
|
||||
/// Note that the window name is a null-terminated string, but `header.size`
|
||||
/// (which is supposed to be equal to the size of the header structure plus
|
||||
/// the window name) may actually be longer than `strlen(name) +
|
||||
/// sizeof(HeaderType)`. When that is the case, the extra padding after the
|
||||
/// first null byte in the window name will be passed over silently.
|
||||
pub fn read_name(header: &XwdHeader, bytes: &mut impl Iterator<Item = u8>) -> CString {
|
||||
if header.size <= HEADER_SIZE {
|
||||
panic!(
|
||||
"Invalid header size {} (smaller than header struct size {})",
|
||||
header.size, HEADER_SIZE
|
||||
);
|
||||
}
|
||||
let mut buf = Vec::new();
|
||||
let mut found_nul = false;
|
||||
for _ in 0..header.size - HEADER_SIZE {
|
||||
let b = bytes.next().unwrap();
|
||||
if b == 0 {
|
||||
if found_nul {
|
||||
continue;
|
||||
} else {
|
||||
buf.push(b);
|
||||
found_nul = true;
|
||||
}
|
||||
} else {
|
||||
buf.push(b);
|
||||
}
|
||||
}
|
||||
CString::from_vec_with_nul(buf).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the visual class for this window dump.
|
||||
pub fn visual_class(&self) -> VisualClass {
|
||||
match self.header.visual_class {
|
||||
0 => VisualClass::StaticGray,
|
||||
1 => VisualClass::GrayScale,
|
||||
2 => VisualClass::StaticColor,
|
||||
3 => VisualClass::PseudoColor,
|
||||
4 => VisualClass::TrueColor,
|
||||
5 => VisualClass::DirectColor,
|
||||
_ => panic!(
|
||||
"unrecognized X11 visual class: {}",
|
||||
self.header.visual_class
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes this image as a PNG, consumes `self` in the process (because the
|
||||
/// image data is reused during the encoding process).
|
||||
///
|
||||
/// Note that only [`VisualClass::TrueColor`] images with a specific pixel
|
||||
/// format (24-bit color packed into 32-bit segments with BGR ordering) are
|
||||
/// supported.
|
||||
pub fn into_png(self) -> Result<Vec<u8>, png::EncodingError> {
|
||||
match self.visual_class() {
|
||||
VisualClass::TrueColor => into_png_true_color(self.header, self.pixels),
|
||||
_ => todo!("X11 visual classes other than TrueColor are not yet supported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn into_png_true_color(
|
||||
header: XwdHeader,
|
||||
mut pixels: Vec<u8>,
|
||||
) -> Result<Vec<u8>, png::EncodingError> {
|
||||
if header.depth != 24 {
|
||||
todo!("pixmap color depths other than 24-bit are not yet supported");
|
||||
}
|
||||
if !(header.red_mask == 0xff0000
|
||||
&& header.green_mask == 0x00ff00
|
||||
&& header.blue_mask == 0x0000ff)
|
||||
{
|
||||
todo!("color orderings other than RGB are not yet supported");
|
||||
}
|
||||
if header.bits_per_rgb == 32 {
|
||||
todo!("bits per rgb triplet other than 32 are not yet supported");
|
||||
}
|
||||
|
||||
let mut out = Cursor::new(Vec::<u8>::new());
|
||||
let mut encoder = png::Encoder::new(&mut out, header.width, header.height);
|
||||
encoder.set_color(png::ColorType::Rgba);
|
||||
encoder.set_depth(png::BitDepth::Eight);
|
||||
encoder.set_compression(png::Compression::High);
|
||||
|
||||
for pixel in pixels.chunks_mut(4) {
|
||||
let blue = pixel[0];
|
||||
let green = pixel[1];
|
||||
let red = pixel[2];
|
||||
pixel[0] = red;
|
||||
pixel[1] = green;
|
||||
pixel[2] = blue;
|
||||
pixel[3] = 0xFF;
|
||||
}
|
||||
|
||||
let mut writer = encoder.write_header()?;
|
||||
writer.write_image_data(&pixels)?;
|
||||
writer.finish()?;
|
||||
|
||||
Ok(out.into_inner())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::XwdImage;
|
||||
use insta_image::assert_png_snapshot;
|
||||
|
||||
#[test]
|
||||
fn png_encode() {
|
||||
let snowlamp_xwd = XwdImage::read(&mut include_bytes!("snowlamp.xwd").into_iter().copied());
|
||||
assert_png_snapshot!("snowlamp_encoded", snowlamp_xwd.into_png().unwrap());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
pub mod logo_data;
|
||||
pub mod headless;
|
||||
|
||||
use headless::{xvfb::XvfbProcess, DisplayNumberRegistry};
|
||||
use std::{
|
||||
env,
|
||||
ffi::{c_char, c_int, CStr, CString},
|
||||
ptr::{self, NonNull},
|
||||
sync::Mutex,
|
||||
};
|
||||
use wings_rs::WINGsP::{WMHandleEvent, WMInitializeApplication, WMReleaseApplication, WMScreen};
|
||||
|
||||
static APPLICATION_COUNT: Mutex<u32> = Mutex::new(0);
|
||||
|
||||
/// Stubbed wrapper for WMApplication.
|
||||
pub struct HeadlessApplication {
|
||||
/// The headless X server process.
|
||||
pub xvfb: XvfbProcess,
|
||||
/// An open client handle to the headless X server.
|
||||
pub display: NonNull<x11::xlib::Display>,
|
||||
/// A fully initialized `WMScreen` for an application running on the
|
||||
/// headless X server.
|
||||
pub screen: NonNull<WMScreen>,
|
||||
}
|
||||
|
||||
impl HeadlessApplication {
|
||||
/// Starts a headless X server and calls `WMInitializeApplication` if no
|
||||
/// WINGs application is active.
|
||||
pub fn new() -> Self {
|
||||
static PROGNAME: &'static CStr = c"Test@eqweq_ewq$eqw";
|
||||
|
||||
let xvfb = XvfbProcess::start_default(
|
||||
DisplayNumberRegistry::next_unused_display()
|
||||
.expect("cannot allocate a value for DISPLAY"),
|
||||
)
|
||||
.expect("cannot start Xvfb");
|
||||
|
||||
{
|
||||
let mut application_count = APPLICATION_COUNT.lock().unwrap();
|
||||
if *application_count == 0 {
|
||||
unsafe {
|
||||
let mut argv: Vec<*mut c_char> =
|
||||
vec![PROGNAME.as_ptr().cast::<c_char>() as *mut c_char];
|
||||
let mut argc: c_int = 1;
|
||||
WMInitializeApplication(
|
||||
PROGNAME.as_ptr().cast::<c_char>(),
|
||||
&mut argc as *mut _,
|
||||
argv.as_mut_ptr(),
|
||||
);
|
||||
}
|
||||
}
|
||||
*application_count += 1;
|
||||
}
|
||||
|
||||
let display_str = CString::new(format!(":{}", xvfb.display())).unwrap();
|
||||
let display =
|
||||
NonNull::new(unsafe { x11::xlib::XOpenDisplay(display_str.as_ptr()) }).unwrap();
|
||||
let screen = WMScreen::new(&display).unwrap();
|
||||
|
||||
HeadlessApplication {
|
||||
xvfb,
|
||||
display,
|
||||
screen,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pumps the WUtil and X11 event queues, hackily.
|
||||
///
|
||||
/// Runs WUtil event handlers that should be run by `now`, pumps the X11
|
||||
/// event queue, dispatches the next pending event (if any), and runs
|
||||
/// WUtil idle handlers (if no X11 events are available).
|
||||
///
|
||||
/// Returns `true` if more events are pending. (This does not account for
|
||||
/// WUtils timer events that might need to fire, since the future time when
|
||||
/// `pump_event_queue` will next be called cannot be known.)
|
||||
///
|
||||
/// This is somewhat hacky (because it does not match the WINGs main loop
|
||||
/// logic exactly), so it should only be used by tests.
|
||||
pub fn pump_event_queue(&mut self, now: std::time::Instant) -> bool {
|
||||
let display = self.display.as_ptr();
|
||||
wutil_rs::handlers::with_global_handlers(|handlers| handlers.check_timer_handlers(now));
|
||||
|
||||
unsafe {
|
||||
x11::xlib::XSync(display, 0);
|
||||
if x11::xlib::XPending(display) > 0 {
|
||||
let mut event = x11::xlib::XEvent { type_: 0 };
|
||||
x11::xlib::XNextEvent(display, &mut event as *mut _);
|
||||
WMHandleEvent(&mut event as *mut _);
|
||||
} else {
|
||||
wutil_rs::handlers::run_global_idle_handlers();
|
||||
}
|
||||
|
||||
x11::xlib::XSync(display, 0);
|
||||
if x11::xlib::XPending(display) > 0 {
|
||||
return true;
|
||||
}
|
||||
wutil_rs::handlers::with_global_handlers(|handlers| handlers.has_idle_handlers())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HeadlessApplication {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
x11::xlib::XCloseDisplay(self.display.as_ptr());
|
||||
// Leak self.screen, since WINGs doesn't provide a cleanup function.
|
||||
}
|
||||
let mut application_count = APPLICATION_COUNT.lock().unwrap();
|
||||
if *application_count <= 1 {
|
||||
*application_count = 0;
|
||||
unsafe {
|
||||
WMReleaseApplication();
|
||||
}
|
||||
} else {
|
||||
*application_count -= 1;
|
||||
}
|
||||
self.xvfb.shutdown().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple wrapper for demo WINGs applications.
|
||||
///
|
||||
/// This handles calling `WMInitializeApplication`, which operates on a
|
||||
/// singleton behind the scenes and wants to be passed command line arguments.
|
||||
///
|
||||
/// When the last instantiated `LiveApplication` is dropped,
|
||||
/// `WMReleaseApplication` is called automatically.
|
||||
pub struct LiveApplication {
|
||||
/// Live X11 `Display` that the application is running on.
|
||||
pub display: NonNull<x11::xlib::Display>,
|
||||
/// Fully intialized `WMScreen` for the application.
|
||||
///
|
||||
/// This is leaked when the `LiveApplication` is dropped because WINGs does
|
||||
/// not provide a deletion function.
|
||||
pub screen: NonNull<WMScreen>,
|
||||
}
|
||||
|
||||
impl LiveApplication {
|
||||
/// Creates a new application wrapper and initializes the global
|
||||
/// `WMApplication` if necessary.
|
||||
pub fn new(name: &str) -> Self {
|
||||
let mut application_count = APPLICATION_COUNT.lock().unwrap();
|
||||
if *application_count > 0 {
|
||||
panic!("application already started!");
|
||||
}
|
||||
let name = CString::new(name).expect("invalid program name");
|
||||
unsafe {
|
||||
let mut argv: Vec<CString> = vec![name];
|
||||
let name_ptr = argv[0].as_ptr().cast::<c_char>();
|
||||
for arg in env::args() {
|
||||
argv.push(CString::new(arg).expect("invalid argument string"));
|
||||
}
|
||||
let mut argv_ptrs: Vec<*mut c_char> = argv
|
||||
.iter_mut()
|
||||
.map(|a| a.as_ptr().cast::<c_char>() as *mut c_char)
|
||||
.collect();
|
||||
let mut argc: c_int = argv_ptrs.len().try_into().expect("invalid argument count");
|
||||
WMInitializeApplication(name_ptr, &mut argc as *mut _, argv_ptrs.as_mut_ptr());
|
||||
}
|
||||
*application_count = 1;
|
||||
|
||||
let display = NonNull::new(unsafe { x11::xlib::XOpenDisplay(ptr::null_mut()) })
|
||||
.expect("could not connect to X11 display");
|
||||
let screen = WMScreen::new(&display).unwrap();
|
||||
|
||||
LiveApplication { display, screen }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LiveApplication {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
x11::xlib::XCloseDisplay(self.display.as_ptr());
|
||||
// Leak self.screen, since WINGs doesn't provide a cleanup function.
|
||||
}
|
||||
let mut application_count = APPLICATION_COUNT.lock().unwrap();
|
||||
if *application_count <= 1 {
|
||||
*application_count = 0;
|
||||
unsafe {
|
||||
WMReleaseApplication();
|
||||
}
|
||||
} else {
|
||||
*application_count -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
use std::ffi::CStr;
|
||||
|
||||
/// Taken from logo.xpm originally in this repository.
|
||||
pub static GNUSTEP_XPM: &'static [&'static CStr] = &[
|
||||
c"45 45 8 1",
|
||||
c" c None",
|
||||
c". c #666666666666",
|
||||
c"X c #777777777777",
|
||||
c"o c #596559656185",
|
||||
c"O c #000000000000",
|
||||
c"+ c #3CF33CF33CF3",
|
||||
c"@ c #CF3CCF3CCF3C",
|
||||
c"# c #FFFFFFFFFFFF",
|
||||
c" ",
|
||||
c" .Xooooooooo. ",
|
||||
c" ..ooOOOOOOOOOOOOOo. ",
|
||||
c" .XoOOOOOOOOOOOOOOOOOO+ ",
|
||||
c" .oOOOOOOOOOOOOOOOOOOOOOO+ ",
|
||||
c" XOOOOOOOOOOOOOOOOOOOOOOOOOOo ",
|
||||
c" oOOOOOOOOOOOOOOOOOOOOOOOOOOOO+ ",
|
||||
c" oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+ooooooo",
|
||||
c" +OOOOOOOOOOOOOOOOOOOOOOOOoXXXXX.XOOOOOOO",
|
||||
c" XOOOOOOOOOOOOOOOOOOOOOOOOOX#######.OOOOOO",
|
||||
c" XOOOOOOOOOOOOOOOOOOOOOOOOOOX########oOOOOO",
|
||||
c" OOOOOOOOOOOOOOOOOOOOOOOOOOOX#########oOOOO",
|
||||
c" oOOOOOOOOOOOOOOOOOOOOOOOOOOOX#########@OOOO",
|
||||
c" OOOOOOOOOOOOOOOOOOOOOOOOOOOOX##########oOOO",
|
||||
c" oOOOOOOOOOOOOOOOOOOOOOOOOOOOOX##########@OOO",
|
||||
c" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOX###########+OO",
|
||||
c" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOX###########oOO",
|
||||
c"oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX###########@OO",
|
||||
c"+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOX###########@OO",
|
||||
c"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX############XO",
|
||||
c"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX############XO",
|
||||
c"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX############XO",
|
||||
c"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX############XO",
|
||||
c"OOOOOOOOOOOOOOOoX.X.X.X.XX.XXX@############XO",
|
||||
c"OOOOOOOOOOOOOOOX###########################XO",
|
||||
c"OOOOOOOOOOOOOOOX###########################XO",
|
||||
c"OOOOOOOOOOOOOOOX###########################XO",
|
||||
c"+OOOOOOOOOOOOOOX###########################OO",
|
||||
c"oOOOOOOOOOOOOOOX###########################OO",
|
||||
c" OOOOOOOOOOOOOOX##########################XOO",
|
||||
c" OOOOOOOOOOOOOOX##########################oOO",
|
||||
c" oOOOOOOOOOOOOOX#########################@OOO",
|
||||
c" OOOOOOOOOOOOOX#########################+OOO",
|
||||
c" oOOOOOOOOOOOOX########################@OOOO",
|
||||
c" OOOOOOOOOOOOX########################oOOOO",
|
||||
c" OOOOOOOOOOOX#######################oOOOOO",
|
||||
c" OOOOOOOOOOX######################XOOOOOO",
|
||||
c"OOOOOOOXXXXXXXX@#####################@OOOOOOO",
|
||||
c"OOOOOOOo############################@OOOOOOOO",
|
||||
c"OOOOOOOOO@#########################oOOOOOOOOO",
|
||||
c"OOOOOOOOOOX######################@oOOOOOOOOOO",
|
||||
c"OOOOOOOOOOOOX###################XOOOOOOOOOOOO",
|
||||
c"OOOOOOOOOOOOOOX@#############@XOOOOOOOOOOOOOO",
|
||||
c"OOOOOOOOOOOOOOOOOXXX#####XXXOOOOOOOOOOOOOOOOO",
|
||||
c"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
];
|
||||
@@ -0,0 +1,14 @@
|
||||
use insta_image::assert_png_snapshot;
|
||||
use std::time::Instant;
|
||||
use wings_rs::font_panel::FontPanel;
|
||||
use wings_rs_tests::HeadlessApplication;
|
||||
|
||||
#[test]
|
||||
fn show_font_panel() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
let mut font_panel =
|
||||
FontPanel::new(app.screen.as_ptr()).expect("could not construct font panel");
|
||||
font_panel.show();
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("font_panel", app.xvfb.png_screenshot());
|
||||
}
|
||||
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/font_panel_tests.rs
|
||||
assertion_line: 16
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 265
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 109
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 161
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 213
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 317
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 291
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 135
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 187
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 239
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 57
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 31
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/system_pixmap_tests.rs
|
||||
assertion_line: 83
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 56
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 41
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 142
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 160
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 162
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 58
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 204
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 222
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 268
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 286
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 304
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 220
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 130
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 229
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 68 KiB |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/wmpixmap_tests.rs
|
||||
assertion_line: 451
|
||||
expression: app.xvfb.png_screenshot()
|
||||
extension: png
|
||||
snapshot_kind: binary
|
||||
---
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,355 @@
|
||||
use insta_image::assert_png_snapshot;
|
||||
use std::time::Instant;
|
||||
use wings_rs::{
|
||||
pixmap::ffi::*,
|
||||
WINGsP,
|
||||
};
|
||||
use wings_rs_tests::HeadlessApplication;
|
||||
|
||||
#[test]
|
||||
fn draw_return_arrow() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_ReturnArrow);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("return_arrow", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_highlighted_return_arrow() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_HighlightedReturnArrow);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("highlighted_return_arrow", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_scroller_dimple() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_ScrollerDimple);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("scroller_dimple", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_arrow_left() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_ArrowLeft);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("arrow_left", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_highlighted_arrow_left() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_HighlightedArrowLeft);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("highlighted_arrow_left", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_arrow_right() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_ArrowRight);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("arrow_right", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_highlighted_arrow_right() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_HighlightedArrowRight);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("highlighted_arrow_right", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_arrow_up() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_ArrowUp);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("arrow_up", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_highlighted_arrow_up() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_HighlightedArrowUp);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("highlighted_arrow_up", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_arrow_down() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_ArrowDown);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("arrow_down", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_highlighted_arrow_down() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_HighlightedArrowDown);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("highlighted_arrow_down", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_check_mark() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let screen = &raw mut *app.screen.as_ptr();
|
||||
let display = (*screen).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = WMGetSystemPixmap(screen, WINGsP::constants::WSystemIcon_CheckMark);
|
||||
WMDrawPixmap(pixmap, win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("check_mark", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
use insta_image::assert_png_snapshot;
|
||||
use std::{
|
||||
ffi::c_char,
|
||||
ptr::{self, NonNull},
|
||||
time::Instant,
|
||||
};
|
||||
use wings_rs::pixmap::ffi::*;
|
||||
use wings_rs_tests::HeadlessApplication;
|
||||
use wrlib_rs::ffi::{RLoadImage, RReleaseImage};
|
||||
|
||||
#[test]
|
||||
fn draw_blank_pixmap() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = NonNull::new(WMCreatePixmap(app.screen.as_ptr(), 128, 196, 24, 1)).unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("empty_window", app.xvfb.png_screenshot());
|
||||
|
||||
let gc = x11::xlib::XCreateGC(display, (*pixmap.as_ptr()).pixmap(), 0, ptr::null_mut());
|
||||
x11::xlib::XSetForeground(display, gc, 0);
|
||||
x11::xlib::XFillRectangle(display, (*pixmap.as_ptr()).pixmap(), gc, 0, 0, 128, 196);
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("empty_pixmap_on_window", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XFreeGC(display, gc);
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
// WMCreatePixmapFromXPixmaps is not tested because it simply fills in struct
|
||||
// fields. Coverage from other tests in this file should be adequate.
|
||||
|
||||
#[test]
|
||||
fn draw_pixmap_from_file() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = NonNull::new(WMCreatePixmapFromFile(
|
||||
app.screen.as_ptr(),
|
||||
c"tests/image_128x120.png".as_ptr(),
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("from_file_pixmap", app.xvfb.png_screenshot());
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_pixmap_from_r_image_no_mask() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let rimage = RLoadImage(
|
||||
(*app.screen.as_ptr()).rcontext,
|
||||
c"tests/image_128x120.png".as_ptr(),
|
||||
0,
|
||||
);
|
||||
let pixmap =
|
||||
NonNull::new(WMCreatePixmapFromRImage(app.screen.as_ptr(), rimage, 255)).unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 70, 74);
|
||||
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("from_r_image_no_mask", app.xvfb.png_screenshot());
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_pixmap_from_r_image_with_mask() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XBlackPixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let rimage = NonNull::new(RLoadImage(
|
||||
(*app.screen.as_ptr()).rcontext,
|
||||
c"tests/image_128x120_varying_alpha.png".as_ptr(),
|
||||
255,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
// The threshold given to WMCreatePixmapFromRImage is compared against
|
||||
// the image's alpha channel. Pixels whose transparency are below the
|
||||
// threshold are masked out. This should draw a quartet of images with
|
||||
// varying masks.
|
||||
|
||||
// Totally masked.
|
||||
let pixmap_255 = NonNull::new(WMCreatePixmapFromRImage(
|
||||
app.screen.as_ptr(),
|
||||
rimage.as_ptr(),
|
||||
255,
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap_255.as_ptr(), win, 70, 74);
|
||||
// Three bubbles masked.
|
||||
let pixmap_200 = NonNull::new(WMCreatePixmapFromRImage(
|
||||
app.screen.as_ptr(),
|
||||
rimage.as_ptr(),
|
||||
200,
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap_200.as_ptr(), win, 200, 74);
|
||||
// Two bubbles masked.
|
||||
let pixmap_128 = NonNull::new(WMCreatePixmapFromRImage(
|
||||
app.screen.as_ptr(),
|
||||
rimage.as_ptr(),
|
||||
128,
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap_128.as_ptr(), win, 70, 200);
|
||||
// Nothing masked.
|
||||
let pixmap_0 = NonNull::new(WMCreatePixmapFromRImage(
|
||||
app.screen.as_ptr(),
|
||||
rimage.as_ptr(),
|
||||
0,
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap_0.as_ptr(), win, 200, 200);
|
||||
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("from_r_image_with_mask", app.xvfb.png_screenshot());
|
||||
WMReleasePixmap(pixmap_255.as_ptr());
|
||||
WMReleasePixmap(pixmap_200.as_ptr());
|
||||
WMReleasePixmap(pixmap_128.as_ptr());
|
||||
WMReleasePixmap(pixmap_0.as_ptr());
|
||||
RReleaseImage(rimage.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_blended_pixmap_from_r_image() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let rimage = RLoadImage(
|
||||
(*app.screen.as_ptr()).rcontext,
|
||||
c"tests/image_128x120.png".as_ptr(),
|
||||
0,
|
||||
);
|
||||
let pixmap =
|
||||
NonNull::new(WMCreateGreyedPixmapFromRImage(app.screen.as_ptr(), rimage)).unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 70, 74);
|
||||
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("from_r_image_no_mask", app.xvfb.png_screenshot());
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_blended_pixmap_from_file() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = NonNull::new(WMCreatePixmapFromFile(
|
||||
app.screen.as_ptr(),
|
||||
c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("from_file_blended_base_pixmap", app.xvfb.png_screenshot());
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"from_file_blended_cleared_window",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
|
||||
let pixmap = NonNull::new(WMCreateGreyedPixmapFromFile(
|
||||
app.screen.as_ptr(),
|
||||
c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!("from_file_blended_with_grey", app.xvfb.png_screenshot());
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_scaled_blended_pixmap_from_file() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = NonNull::new(WMCreatePixmapFromFile(
|
||||
app.screen.as_ptr(),
|
||||
c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"from_file_scaled_blended_base_pixmap",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"from_file_scaled_blended_cleared_window",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
|
||||
let pixmap = NonNull::new(WMCreateScaledGreyedPixmapFromFile(
|
||||
app.screen.as_ptr(),
|
||||
c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
|
||||
60,
|
||||
64,
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"from_file_scaled_blended_with_grey",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_scaled_blended_pixmap_from_file_non_integral_scaling() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = NonNull::new(WMCreatePixmapFromFile(
|
||||
app.screen.as_ptr(),
|
||||
c"tests/image_128x120.png".as_ptr(),
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"from_file_scaled_blended_non_integral_base_pixmap",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"from_file_scaled_blended_non_integral_cleared_window",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
|
||||
let pixmap = NonNull::new(WMCreateScaledGreyedPixmapFromFile(
|
||||
app.screen.as_ptr(),
|
||||
// No alpha channel on this image, so no color blending happens.
|
||||
c"tests/image_128x120.png".as_ptr(),
|
||||
// Shrink by only a little bit, such that simple integer division
|
||||
// might be thrown off. This test ensures that rescaling will
|
||||
// actually happen.
|
||||
110,
|
||||
114,
|
||||
))
|
||||
.unwrap();
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"from_file_scaled_blended_non_integral_scaled",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_pixmap_from_xpm_data() {
|
||||
let mut app = HeadlessApplication::new();
|
||||
let mut data: Vec<*mut c_char> = wings_rs_tests::logo_data::GNUSTEP_XPM
|
||||
.iter()
|
||||
.map(|line| line.as_ptr() as *mut c_char)
|
||||
.collect();
|
||||
unsafe {
|
||||
let display = (*app.screen.as_ptr()).display;
|
||||
let win = x11::xlib::XCreateSimpleWindow(
|
||||
display,
|
||||
(*app.screen.as_ptr()).rootWin,
|
||||
0,
|
||||
0,
|
||||
512,
|
||||
512,
|
||||
1,
|
||||
1,
|
||||
x11::xlib::XWhitePixel(display, 0),
|
||||
);
|
||||
x11::xlib::XMapWindow(display, win);
|
||||
|
||||
let pixmap = NonNull::new(WMCreatePixmapFromXPMData(
|
||||
app.screen.as_ptr(),
|
||||
data.as_mut_ptr(),
|
||||
)).unwrap();
|
||||
|
||||
WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
|
||||
while app.pump_event_queue(Instant::now()) {}
|
||||
assert_png_snapshot!(
|
||||
"pixmap_from_xpm_data",
|
||||
app.xvfb.png_screenshot()
|
||||
);
|
||||
|
||||
x11::xlib::XUnmapWindow(display, win);
|
||||
WMReleasePixmap(pixmap.as_ptr());
|
||||
}
|
||||
}
|
||||