Add pango toggle, it is now optional for those of you who want a minimal

spmenu instance.
This commit is contained in:
speedie 2023-03-08 21:12:37 +01:00
parent 7ed80af25e
commit 21d75d806e
18 changed files with 919 additions and 391 deletions

View file

@ -27,8 +27,7 @@ spmenu: spmenu.o libs/sl/draw.o libs/sl/main.o
$(CC) -o $@ spmenu.o draw.o main.o $(LDFLAGS) $(CC) -o $@ spmenu.o draw.o main.o $(LDFLAGS)
clean: clean:
rm -f spmenu \ rm -f spmenu *.o \
$(OBJ) \
spmenu-$(VERSION).tar.gz \ spmenu-$(VERSION).tar.gz \
*zst* spmenu-$(VERSION).PKGBUILD *zst* spmenu-$(VERSION).PKGBUILD

View file

@ -49,12 +49,12 @@ Note: This is an incomplete list, it's just here to give you an idea of what thi
- libXrender - libXrender
- freetype - freetype
- imlib2 - imlib2
- Used for image support, can be disabled if you don't want this by editing host.mk and defining NOIMAGE - Used for image support, can be disabled if you don't want this by editing `toggle.mk`.
- libXinerama - libXinerama
- Can be disabled if you don't want/need multi-monitor support. - Can be disabled if you don't want/need multi-monitor support, edit `toggle.mk`
- tcc compiler (you can swap it out for GCC by passing CC="gcc" to the `make` command if you want) - tcc compiler (you can swap it out for GCC by passing CC="gcc" to the `make` command if you want)
- Pango (for drawing fonts) - Pango (for drawing fonts)
- If you do not want to use pango, consider my [older dmenu build](https://github.com/speedie-de/dmenu) - If you do not want to use pango, edit `toggle.mk`
### Installation (most GNU/Linux distributions) ### Installation (most GNU/Linux distributions)

View file

@ -11,6 +11,8 @@ a good more minimal alternative to spmenu.
Unlike dmenu and the many builds out there, spmenu has features like color Unlike dmenu and the many builds out there, spmenu has features like color
support, Vim-like modes, image support, proper keybind configuration, and more. support, Vim-like modes, image support, proper keybind configuration, and more.
Most of these features can be disabled during compile time by editing `toggle.mk`.
## 2. Usage ## 2. Usage
On runtime, spmenu reads from standard input (stdin). spmenu items are On runtime, spmenu reads from standard input (stdin). spmenu items are

14
host.mk
View file

@ -20,11 +20,7 @@ FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2 FREETYPEINC = /usr/include/freetype2
# xft # xft
XFT_CONF = xft XFTCONF = xft
# pango
PANGO_CONF = pango
PANGOXFT_CONF = pangoxft
# xrender # xrender
XRENDERLIBS = -lXrender XRENDERLIBS = -lXrender
@ -33,7 +29,7 @@ XRENDERLIBS = -lXrender
IMLIB2LIBS = -lImlib2 IMLIB2LIBS = -lImlib2
# openssl # openssl
OPENSSL_CONF = openssl OPENSSLCONF = openssl
# OpenBSD (uncomment) # OpenBSD (uncomment)
#FREETYPEINC = $(X11INC)/freetype2 #FREETYPEINC = $(X11INC)/freetype2
@ -43,8 +39,8 @@ OPENSSL_CONF = openssl
#X11INC = /opt/X11/include #X11INC = /opt/X11/include
#X11LIB = /opt/X11/lib #X11LIB = /opt/X11/lib
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMATOGGLE) $(BDTOGGLE) $(PANGOTOGGLE) $(IMLIB2TOGGLE)
CFLAGS = -std=c99 -pedantic -Wall -O2 $(INCS) $(CPPFLAGS) CFLAGS = -std=c99 -pedantic -Wall -O2 $(INCS) $(CPPFLAGS)
LDFLAGS = $(LIBS) LDFLAGS = $(LIBS)
INCS = -I$(X11INC) -I$(FREETYPEINC) -I$(BDINC) `pkg-config --cflags $(XFT_CONF) $(PANGO_CONF) $(PANGOXFT_CONF) $(OPENSSL_CONF)` INCS = -I$(X11INC) -I$(FREETYPEINC) -I$(BDINC) `pkg-config --cflags $(XFTCONF) $(PANGOCONF) $(PANGOXFTCONF) $(OPENSSLCONF)`
LIBS = -L$(X11LIB) $(X11LIBS) $(XINERAMALIBS) $(FREETYPELIBS) $(XRENDERLIBS) -lm `pkg-config --libs $(XFT_CONF) $(PANGO_CONF) $(PANGOXFT_CONF) $(OPENSSL_CONF)` $(BDLIBS) $(IMLIB2LIBS) LIBS = -L$(X11LIB) $(X11LIBS) $(XINERAMALIBS) $(FREETYPELIBS) $(XRENDERLIBS) -lm `pkg-config --libs $(XFTCONF) $(PANGOCONF) $(PANGOXFTCONF) $(OPENSSLCONF)` $(BDLIBS) $(IMLIB2LIBS)

View file

@ -301,30 +301,29 @@ out:
void void
setimgsize(const Arg *arg) setimgsize(const Arg *arg)
{ {
#if !USEIMAGE #if USEIMAGE
return;
#endif
setimagesize(imagewidth + arg->i, imageheight + arg->i); setimagesize(imagewidth + arg->i, imageheight + arg->i);
#endif
} }
void void
flipimg(const Arg *arg) flipimg(const Arg *arg)
{ {
#if !USEIMAGE #if USEIMAGE
return;
#endif
if (!image) return; if (!image) return;
flip = flip ? 0 : arg->i ? 1 : 2; flip = flip ? 0 : arg->i ? 1 : 2;
drawmenu(); drawmenu();
#endif
} }
void void
setimgpos(const Arg *arg) setimgpos(const Arg *arg)
{ {
#if USEIMAGE
if (!image || hideimage) return; if (!image || hideimage) return;
if (imageposition < 3) { if (imageposition < 3) {
@ -334,11 +333,13 @@ setimgpos(const Arg *arg)
} }
drawmenu(); drawmenu();
#endif
} }
void void
setimggaps(const Arg *arg) setimggaps(const Arg *arg)
{ {
#if USEIMAGE
imagegaps += arg->i; imagegaps += arg->i;
if (!image || hideimage) return; if (!image || hideimage) return;
@ -351,40 +352,38 @@ setimggaps(const Arg *arg)
imagegaps -= arg->i; imagegaps -= arg->i;
drawmenu(); drawmenu();
#endif
} }
void void
rotateimg(const Arg *arg) rotateimg(const Arg *arg)
{ {
#if !USEIMAGE #if USEIMAGE
return;
#endif
if (!image || hideimage) return; if (!image || hideimage) return;
rotation += arg->i ? arg->i : 1; rotation += arg->i ? arg->i : 1;
drawmenu(); drawmenu();
#endif
} }
void void
toggleimg(const Arg *arg) toggleimg(const Arg *arg)
{ {
#if !USEIMAGE #if USEIMAGE
return;
#endif
hideimage = !hideimage; hideimage = !hideimage;
drawmenu(); drawmenu();
#endif
} }
void void
defaultimg(const Arg *arg) defaultimg(const Arg *arg)
{ {
#if !USEIMAGE #if USEIMAGE
return;
#endif
if (hideimage || !image) return; if (hideimage || !image) return;
@ -395,4 +394,5 @@ defaultimg(const Arg *arg)
} }
drawmenu(); drawmenu();
#endif
} }

5
libs/rtl-none.c Normal file
View file

@ -0,0 +1,5 @@
void
apply_fribidi(char *str)
{
return;
}

1
libs/rtl-none.h Normal file
View file

@ -0,0 +1 @@
static void apply_fribidi(char *str);

View file

@ -1,9 +1,6 @@
void void
apply_fribidi(char *str) apply_fribidi(char *str)
{ {
#if !USERTL
return;
#endif
FriBidiStrIndex len = strlen(str); FriBidiStrIndex len = strlen(str);
FriBidiChar logical[BUFSIZ]; FriBidiChar logical[BUFSIZ];
FriBidiChar visual[BUFSIZ]; FriBidiChar visual[BUFSIZ];

287
libs/sl/draw-pango.c Normal file
View file

@ -0,0 +1,287 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#if USEPANGO
#include <pango/pango.h>
#include <pango/pangoxft.h>
#endif
#include "draw.h"
#include "main.h"
Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
{
Drw *drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy;
drw->screen = screen;
drw->root = root;
drw->w = w;
drw->h = h;
drw->visual = visual;
drw->depth = depth;
drw->cmap = cmap;
drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
}
void
drw_resize(Drw *drw, unsigned int w, unsigned int h)
{
if (!drw)
return;
drw->w = w;
drw->h = h;
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
}
void
drw_free(Drw *drw)
{
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
drw_font_free(drw->font);
free(drw);
}
static Fnt *
xfont_create(Drw *drw, const char *fontname)
{
Fnt *font;
PangoFontMap *fontmap;
PangoContext *context;
PangoFontDescription *desc;
PangoFontMetrics *metrics;
if (!fontname) {
die("no font specified.");
}
font = ecalloc(1, sizeof(Fnt));
font->dpy = drw->dpy;
fontmap = pango_xft_get_font_map(drw->dpy, drw->screen);
context = pango_font_map_create_context(fontmap);
desc = pango_font_description_from_string(fontname);
font->layout = pango_layout_new(context);
pango_layout_set_font_description(font->layout, desc);
metrics = pango_context_get_metrics(context, desc, pango_language_from_string ("en-us"));
font->h = pango_font_metrics_get_height(metrics) / PANGO_SCALE;
pango_font_metrics_unref(metrics);
g_object_unref(context);
return font;
}
static void
xfont_free(Fnt *font)
{
if (!font)
return;
if (font->layout)
g_object_unref(font->layout);
free(font);
}
Fnt*
drw_font_create(Drw* drw, const char font[], size_t fontcount)
{
Fnt *fnt = NULL;
if (!drw || !font)
return NULL;
fnt = xfont_create(drw, font);
return (drw->font = fnt);
}
void
drw_font_free(Fnt *font)
{
if (font) {
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
{
if (!drw || !dest || !clrname)
return;
if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
{
size_t i;
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
return NULL;
for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
return ret;
}
void
drw_setscheme(Drw *drw, Clr *scm)
{
if (drw)
drw->scheme = scm;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
if (!drw || !drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
}
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup)
{
char buf[1024];
int ty;
unsigned int ew = 0;
XftDraw *d = NULL;
size_t i, len;
int render = x || y || w || h;
if (!drw || (render && !drw->scheme) || !text || !drw->font)
return 0;
if (!render) {
w = ~w;
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
x += lpad;
w -= lpad;
}
len = strlen(text);
if (len) {
drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
/* shorten text if necessary */
for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
if (len) {
memcpy(buf, text, len);
buf[len] = '\0';
if (len < strlen(text))
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) {
ty = y + (h - drw->font->h) / 2;
if(markup)
pango_layout_set_markup(drw->font->layout, buf, len);
else
pango_layout_set_text(drw->font->layout, buf, len);
pango_xft_render_layout(d, &drw->scheme[invert ? ColBg : ColFg],
drw->font->layout, x * PANGO_SCALE, ty * PANGO_SCALE);
if(markup) /* clear markup attributes */
pango_layout_set_attributes(drw->font->layout, NULL);
}
x += ew;
w -= ew;
}
}
if (d)
XftDrawDestroy(d);
return x + (render ? w : 0);
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
if (!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
unsigned int
drw_font_getwidth(Drw *drw, const char *text, Bool markup)
{
if (!drw || !drw->font || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup)
{
if (!font || !text)
return;
PangoRectangle r;
if(markup)
pango_layout_set_markup(font->layout, text, len);
else
pango_layout_set_text(font->layout, text, len);
pango_layout_get_extents(font->layout, 0, &r);
if(markup) /* clear markup attributes */
pango_layout_set_attributes(font->layout, NULL);
if (w)
*w = r.width / PANGO_SCALE;
if (h)
*h = font->h;
}
Cur *
drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
}
void
drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

57
libs/sl/draw-pango.h Normal file
View file

@ -0,0 +1,57 @@
/* See LICENSE file for copyright and license details. */
typedef struct {
Cursor cursor;
} Cur;
typedef struct Fnt {
Display *dpy;
unsigned int h;
PangoLayout *layout;
} Fnt;
enum { ColFg, ColBg }; /* Clr scheme index */
typedef XftColor Clr;
typedef struct {
unsigned int w, h;
Display *dpy;
int screen;
Window root;
Visual *visual;
unsigned int depth;
Colormap cmap;
Drawable drawable;
GC gc;
Clr *scheme;
Fnt *font;
} Drw;
/* Drawable abstraction */
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap);
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);
/* Fnt abstraction */
Fnt *drw_font_create(Drw* drw, const char font[], size_t fontcount);
void drw_font_free(Fnt* set);
unsigned int drw_font_getwidth(Drw *drw, const char *text, Bool markup);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup);
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, const unsigned int alpha);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */
void drw_setscheme(Drw *drw, Clr *scm);
/* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

424
libs/sl/draw-xft.c Normal file
View file

@ -0,0 +1,424 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include "draw.h"
#include "main.h"
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static long
utf8decodebyte(const char c, size_t *i)
{
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i];
return 0;
}
static size_t
utf8validate(long *u, size_t i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
}
static size_t
utf8decode(const char *c, long *u, size_t clen)
{
size_t i, j, len, type;
long udecoded;
*u = UTF_INVALID;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
return len;
}
Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
{
Drw *drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy;
drw->screen = screen;
drw->root = root;
drw->w = w;
drw->h = h;
drw->visual = visual;
drw->depth = depth;
drw->cmap = cmap;
drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
}
void
drw_resize(Drw *drw, unsigned int w, unsigned int h)
{
if (!drw)
return;
drw->w = w;
drw->h = h;
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
}
void
drw_free(Drw *drw)
{
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
drw_font_free(drw->font);
free(drw);
}
/* This function is an implementation detail. Library users should use
* drw_font_create instead.
*/
static Fnt *
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{
Fnt *font;
XftFont *xfont = NULL;
FcPattern *pattern = NULL;
if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield the
* same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in missing-character
* rectangles being drawn, at least with some fonts. */
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
return NULL;
}
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont);
return NULL;
}
} else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font from pattern.\n");
return NULL;
}
} else {
die("no font specified.");
}
font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont;
font->pattern = pattern;
font->h = xfont->ascent + xfont->descent;
font->dpy = drw->dpy;
return font;
}
static void
xfont_free(Fnt *font)
{
if (!font)
return;
if (font->pattern)
FcPatternDestroy(font->pattern);
XftFontClose(font->dpy, font->xfont);
free(font);
}
Fnt*
drw_font_create(Drw* drw, char *font[], size_t fontcount)
{
Fnt *cur, *ret = NULL;
size_t i;
if (!drw || !font)
return NULL;
for (i = 1; i <= fontcount; i++) {
if ((cur = xfont_create(drw, font[fontcount - i], NULL))) {
cur->next = ret;
ret = cur;
}
}
return (drw->font = ret);
}
void
drw_font_free(Fnt *font)
{
if (font) {
drw_font_free(font->next);
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
{
if (!drw || !dest || !clrname)
return;
if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
{
size_t i;
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
return NULL;
for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
return ret;
}
void
drw_setfont(Drw *drw, Fnt *set)
{
if (drw)
drw->font = set;
}
void
drw_setscheme(Drw *drw, Clr *scm)
{
if (drw)
drw->scheme = scm;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
if (!drw || !drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
}
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup)
{
char buf[1024];
int ty;
unsigned int ew;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
size_t i, len;
int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
FcPattern *fcpattern;
FcPattern *match;
XftResult result;
int charexists = 0;
if (!drw || (render && !drw->scheme) || !text || !drw->font)
return 0;
if (!render) {
w = ~w;
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
x += lpad;
w -= lpad;
}
usedfont = drw->font;
while (1) {
utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (curfont = drw->font; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
} else {
nextfont = curfont;
}
break;
}
}
if (!charexists || nextfont)
break;
else
charexists = 0;
}
if (utf8strlen) {
drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL, True);
/* shorten text if necessary */
for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(usedfont, utf8str, len, &ew, NULL, True);
if (len) {
memcpy(buf, utf8str, len);
buf[len] = '\0';
if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
}
x += ew;
w -= ew;
}
}
if (!*text) {
break;
} else if (nextfont) {
charexists = 0;
usedfont = nextfont;
} else {
/* Regardless of whether or not a fallback font is found, the
* character must be drawn. */
charexists = 1;
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->font->pattern) {
/* Refer to the comment in xfont_create for more information. */
die("the first font in the cache must be loaded from a font string.");
}
fcpattern = FcPatternDuplicate(drw->font->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
FcDefaultSubstitute(fcpattern);
match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
FcCharSetDestroy(fccharset);
FcPatternDestroy(fcpattern);
if (match) {
usedfont = xfont_create(drw, NULL, match);
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
for (curfont = drw->font; curfont->next; curfont = curfont->next)
; /* NOP */
curfont->next = usedfont;
} else {
xfont_free(usedfont);
usedfont = drw->font;
}
}
}
}
if (d)
XftDrawDestroy(d);
return x + (render ? w : 0);
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
if (!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
unsigned int
drw_font_getwidth(Drw *drw, const char *text, Bool markup)
{
if (!drw || !drw->font || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0, True);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup)
{
XGlyphInfo ext;
if (!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
if (w)
*w = ext.xOff;
if (h)
*h = font->h;
}
Cur *
drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
}
void
drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

60
libs/sl/draw-xft.h Normal file
View file

@ -0,0 +1,60 @@
/* See LICENSE file for copyright and license details. */
typedef struct {
Cursor cursor;
} Cur;
typedef struct Fnt {
Display *dpy;
unsigned int h;
XftFont *xfont;
FcPattern *pattern;
struct Fnt *next;
} Fnt;
enum { ColFg, ColBg }; /* Clr scheme index */
typedef XftColor Clr;
typedef struct {
unsigned int w, h;
Display *dpy;
int screen;
Window root;
Visual *visual;
unsigned int depth;
Colormap cmap;
Drawable drawable;
GC gc;
Clr *scheme;
Fnt *font;
} Drw;
/* Drawable abstraction */
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap);
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);
/* Fnt abstraction */
Fnt *drw_font_create(Drw* drw, char *font[], size_t fontcount);
void drw_font_free(Fnt* set);
unsigned int drw_font_getwidth(Drw *drw, const char *text, Bool markup);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup);
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */
void drw_setfont(Drw *drw, Fnt *set);
void drw_setscheme(Drw *drw, Clr *scm);
/* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

View file

@ -1,285 +1,11 @@
/* See LICENSE file for copyright and license details. */ #if PANGO
#include <stdio.h> #define USEPANGO 1
#include <stdlib.h> #else
#include <string.h> #define USEPANGO 0
#include <X11/Xlib.h> #endif
#include <X11/Xft/Xft.h>
#include <pango/pango.h>
#include <pango/pangoxft.h>
#include "draw.h" #if USEPANGO
#include "main.h" #include "draw-pango.c"
#else
Drw * #include "draw-xft.c"
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) #endif
{
Drw *drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy;
drw->screen = screen;
drw->root = root;
drw->w = w;
drw->h = h;
drw->visual = visual;
drw->depth = depth;
drw->cmap = cmap;
drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
}
void
drw_resize(Drw *drw, unsigned int w, unsigned int h)
{
if (!drw)
return;
drw->w = w;
drw->h = h;
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
}
void
drw_free(Drw *drw)
{
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
drw_font_free(drw->font);
free(drw);
}
static Fnt *
xfont_create(Drw *drw, const char *fontname)
{
Fnt *font;
PangoFontMap *fontmap;
PangoContext *context;
PangoFontDescription *desc;
PangoFontMetrics *metrics;
if (!fontname) {
die("no font specified.");
}
font = ecalloc(1, sizeof(Fnt));
font->dpy = drw->dpy;
fontmap = pango_xft_get_font_map(drw->dpy, drw->screen);
context = pango_font_map_create_context(fontmap);
desc = pango_font_description_from_string(fontname);
font->layout = pango_layout_new(context);
pango_layout_set_font_description(font->layout, desc);
metrics = pango_context_get_metrics(context, desc, pango_language_from_string ("en-us"));
font->h = pango_font_metrics_get_height(metrics) / PANGO_SCALE;
pango_font_metrics_unref(metrics);
g_object_unref(context);
return font;
}
static void
xfont_free(Fnt *font)
{
if (!font)
return;
if (font->layout)
g_object_unref(font->layout);
free(font);
}
Fnt*
drw_font_create(Drw* drw, const char font[])
{
Fnt *fnt = NULL;
if (!drw || !font)
return NULL;
fnt = xfont_create(drw, font);
return (drw->font = fnt);
}
void
drw_font_free(Fnt *font)
{
if (font) {
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
{
if (!drw || !dest || !clrname)
return;
if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
{
size_t i;
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
return NULL;
for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
return ret;
}
void
drw_setscheme(Drw *drw, Clr *scm)
{
if (drw)
drw->scheme = scm;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
if (!drw || !drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
}
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup)
{
char buf[1024];
int ty;
unsigned int ew = 0;
XftDraw *d = NULL;
size_t i, len;
int render = x || y || w || h;
if (!drw || (render && !drw->scheme) || !text || !drw->font)
return 0;
if (!render) {
w = ~w;
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
x += lpad;
w -= lpad;
}
len = strlen(text);
if (len) {
drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
/* shorten text if necessary */
for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
if (len) {
memcpy(buf, text, len);
buf[len] = '\0';
if (len < strlen(text))
for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) {
ty = y + (h - drw->font->h) / 2;
if(markup)
pango_layout_set_markup(drw->font->layout, buf, len);
else
pango_layout_set_text(drw->font->layout, buf, len);
pango_xft_render_layout(d, &drw->scheme[invert ? ColBg : ColFg],
drw->font->layout, x * PANGO_SCALE, ty * PANGO_SCALE);
if(markup) /* clear markup attributes */
pango_layout_set_attributes(drw->font->layout, NULL);
}
x += ew;
w -= ew;
}
}
if (d)
XftDrawDestroy(d);
return x + (render ? w : 0);
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
if (!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
unsigned int
drw_font_getwidth(Drw *drw, const char *text, Bool markup)
{
if (!drw || !drw->font || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup)
{
if (!font || !text)
return;
PangoRectangle r;
if(markup)
pango_layout_set_markup(font->layout, text, len);
else
pango_layout_set_text(font->layout, text, len);
pango_layout_get_extents(font->layout, 0, &r);
if(markup) /* clear markup attributes */
pango_layout_set_attributes(font->layout, NULL);
if (w)
*w = r.width / PANGO_SCALE;
if (h)
*h = font->h;
}
Cur *
drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
}
void
drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

View file

@ -1,57 +1,11 @@
/* See LICENSE file for copyright and license details. */ #if PANGO
#define USEPANGO 1
#else
#define USEPANGO 0
#endif
typedef struct { #if USEPANGO
Cursor cursor; #include "draw-pango.h"
} Cur; #else
#include "draw-xft.h"
typedef struct Fnt { #endif
Display *dpy;
unsigned int h;
PangoLayout *layout;
} Fnt;
enum { ColFg, ColBg }; /* Clr scheme index */
typedef XftColor Clr;
typedef struct {
unsigned int w, h;
Display *dpy;
int screen;
Window root;
Visual *visual;
unsigned int depth;
Colormap cmap;
Drawable drawable;
GC gc;
Clr *scheme;
Fnt *font;
} Drw;
/* Drawable abstraction */
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap);
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);
/* Fnt abstraction */
Fnt *drw_font_create(Drw* drw, const char font[]);
void drw_font_free(Fnt* set);
unsigned int drw_font_getwidth(Drw *drw, const char *text, Bool markup);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup);
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, const unsigned int alpha);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */
void drw_setscheme(Drw *drw, Clr *scm);
/* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

View file

@ -5,5 +5,5 @@
VERSION = 0.2.1 VERSION = 0.2.1
# includes and libs # includes and libs
INCS = -I$(X11INC) -I$(FREETYPEINC) $(BDINC) `pkg-config --cflags xft pango pangoxft` INCS = -I$(X11INC) -I$(FREETYPEINC) $(BDINC) `pkg-config --cflags xft $(PANGO_CONF) $(PANGO_XFT_CONF)`
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) $(XRENDERLIBS) $(IMLIB2LIBS) $(BDLIBS) -lm `pkg-config --libs xft pango pangoxft` LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) $(XRENDERLIBS) $(IMLIB2LIBS) $(BDLIBS) -lm `pkg-config --libs xft $(PANGO_CONF) $(PANGO_XFT_CONF)`

View file

@ -27,6 +27,9 @@ alternative to spmenu.
Unlike dmenu and the many builds out there, spmenu has features like Unlike dmenu and the many builds out there, spmenu has features like
color support, Vim-like modes, image support, proper keybind color support, Vim-like modes, image support, proper keybind
configuration, and more. configuration, and more.
.PP
Most of these features can be disabled during compile time by editing
\f[V]toggle.mk\f[R].
.SS 2. Usage .SS 2. Usage
.PP .PP
On runtime, spmenu reads from standard input (stdin). On runtime, spmenu reads from standard input (stdin).

View file

@ -26,14 +26,21 @@
#include <unistd.h> #include <unistd.h>
/* check if we should enable right to left language support */ /* check if we should enable right to left language support */
#ifdef NORTL #ifndef RTL
#define USERTL 0 #define USERTL 0
#else #else
#define USERTL 1 #define USERTL 1
#endif #endif
/* check if we should enable pango support */
#ifndef PANGO
#define USEPANGO 0
#else
#define USEPANGO 1
#endif
/* check if we should enable image support */ /* check if we should enable image support */
#ifdef NOIMAGE #ifndef IMAGE
#define USEIMAGE 0 #define USEIMAGE 0
#else #else
#define USEIMAGE 1 #define USEIMAGE 1
@ -46,13 +53,6 @@
#define USEXINERAMA 0 #define USEXINERAMA 0
#endif #endif
/* NOXINERAMA overrides XINERAMA */
#ifdef NOXINERAMA
#define USEXINERAMA 0
#else
#define USEXINERAMA 1
#endif
/* include right to left language library */ /* include right to left language library */
#if USERTL #if USERTL
#include <fribidi.h> #include <fribidi.h>
@ -77,7 +77,10 @@
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/Xresource.h> #include <X11/Xresource.h>
#include <X11/Xft/Xft.h> #include <X11/Xft/Xft.h>
#if USEPANGO
#include <pango/pango.h> #include <pango/pango.h>
#endif
#include "libs/sl/draw.h" #include "libs/sl/draw.h"
#include "libs/sl/main.h" #include "libs/sl/main.h"
@ -159,6 +162,7 @@ static int rotation = 0;
static int imagew = 0; static int imagew = 0;
static int imageh = 0; static int imageh = 0;
static int imageg = 0; static int imageg = 0;
static int longestedge = 0;
static int needredraw = 1; static int needredraw = 1;
/* set an integer if to 1 if we have right to left language support enabled /* set an integer if to 1 if we have right to left language support enabled
@ -216,14 +220,15 @@ static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
static char *(*fstrstr)(const char *, const char *) = cistrstr; static char *(*fstrstr)(const char *, const char *) = cistrstr;
#if USEIMAGE #if USEIMAGE
static int longestedge = 0; /* longest edge */
#include "libs/img.h" #include "libs/img.h"
#include "libs/img.c" #include "libs/img.c"
#endif #endif
#if USERTL #if USERTL
#include "libs/rtl.h" #include "libs/rtl.h"
#include "libs/rtl.c" #include "libs/rtl.c"
#else
#include "libs/rtl-none.h"
#include "libs/rtl-none.c"
#endif #endif
#include "libs/event.h" #include "libs/event.h"
#include "libs/event.c" #include "libs/event.c"
@ -998,7 +1003,7 @@ main(int argc, char *argv[])
xinitvisual(); xinitvisual();
drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
if (!drw_font_create(drw, font)) if (!drw_font_create(drw, font, LENGTH(font)))
die("no fonts could be loaded."); die("no fonts could be loaded.");
lrpad = drw->font->h; lrpad = drw->font->h;

View file

@ -4,10 +4,22 @@
# Multi-monitor support. # Multi-monitor support.
# Requires libXinerama # Requires libXinerama
# Comment these lines if you don't need it. # Comment these lines if you don't need it.
XINERAMALIBS = -lXinerama XINERAMALIBS = -lXinerama
XINERAMAFLAGS = -DXINERAMA XINERAMATOGGLE = -DXINERAMA
# Right to left language support # Right to left language support
# Comment these lines if you don't need it. # Comment these lines if you don't need it.
BDLIBS = -lfribidi BDLIBS = -lfribidi
BDINC = /usr/include/fribidi BDINC = /usr/include/fribidi
BDTOGGLE = -DFRIBIDI
# Pango
# Comment these lines if you don't need it.
PANGOCONF = pango
PANGOXFTCONF = pangoxft
PANGOTOGGLE = -DPANGO
# Image support
# Comment these lines if you don't need it.
IMLIB2LIBS = -lImlib2
IMLIB2TOGGLE = -DIMAGE