commit - 9c2414382fee8fe3db84cdb9a41a0af4ce509db4
commit + c9a3bfaa39dc4b1124d72146b051f226cceb44f0
blob - e4c4a82ea60dda0faae467a7bcdf28105194ff3c
blob + f72f121ae78676f2aaac111b14488a6d9594e7a3
--- Makefile
+++ Makefile
VERSION = 0.1
-CC ?= clang
-LIBS = `pkg-config --libs x11 xinerama`
-CFLAGS = -DUSE_XINERAMA -DVERSION=\"$(VERSION)\" `pkg-config --cflags x11 xinerama` -g -O0
+CC ?= clang
+OPTIONAL = xinerama xft
+LIBS = `pkg-config --libs x11 ${OPTIONAL}`
+CFLAGS = -DUSE_XINERAMA -DUSE_XFT -DVERSION=\"$(VERSION)\" `pkg-config --cflags x11 ${OPTIONAL}` -g -O0
.PHONY: all clean install
blob - e8ae32b032aa56518d138d0be0e27cdfa1da2081
blob + 0a3c9a6bdd4bcee4dba073d4aa34ca28153a6d0f
--- README.md
+++ README.md
- Xlib
- Xinerama (optional)
For multi-monitor support
+ - Xft (optional)
+ For TrueType (r) font support
- pkg-config (optional)
used in the makefile to generate `LIBS` and `CFLAGS` correctly
## Build
-As simple as `make`. If you want to disable Xinerama support just
-delete `-DUSE_XINERAMA` from the `CFLAGS` and `xinerama` from the
-`pkg-config` call from the Makefile.
+As simple as `make`. By default both Xft and Xinerama are enabled, if
+you want to disable them just delete the relative `-DUSE_` from the
+`CFLAGS` and update the `OPTIONAL` variable. Of course you can delete
+both, or just one of them.
## FAQ
At the moment the X Resource Database is the only way to interact
with the graphic appearance of MyMenu.
- - Optional TrueType support
-
- Opacity support
## Scripts
blob - 36cff68847fa11f57e6de4a6cb7762c3979a2ada
blob + 78f938aad87ffec4fe5222987fc9f1161cb6dca3
--- mymenu.1
+++ mymenu.1
.Bl -tag -width Ds
.It MyMenu.font
The font name, only bitmap font are supported. By default is set to
-"fixed"
+"fixed". If compiled with Xft(3) support this will be passed to xft.
.It MyMenu.layout
The layout of the menu. The possible values are "horizontal" and
"vertical", with the default being "horizontal". Every other value
blob - ace8989513debf93c111b8e42f36472347566019
blob + 87291ebf019e5939fe074eb39ee72e0eee65fbad
--- mymenu.c
+++ mymenu.c
# include <X11/extensions/Xinerama.h>
#endif
+#ifdef USE_XFT
+# include <X11/Xft/Xft.h>
+#endif
+
#define nil NULL
#define resname "MyMenu"
#define resclass "mymenu"
enum state {LOOPING, OK, ERR};
+enum text_type {PROMPT, COMPL, COMPL_HIGH};
+
struct rendering {
Display *d;
Window w;
GC completion_bg;
GC completion_highlighted;
GC completion_highlighted_bg;
+#ifdef USE_XFT
+ XftDraw *xftdraw;
+ XftColor xft_prompt;
+ XftColor xft_completion;
+ XftColor xft_completion_highlighted;
+ XftFont *font;
+#else
+ XFontSet *font;
+#endif
int width;
int height;
- XFontSet *font;
bool horizontal_layout;
char *ps1;
int ps1len;
n++;
lines[n] = nil;
return items;
+}
+
+int text_extents(char *str, int len, struct rendering *r, int *ret_width, int *ret_height) {
+ int height;
+ int width;
+#ifdef USE_XFT
+ XGlyphInfo gi;
+ XftTextExtentsUtf8(r->d, r->font, str, len, &gi);
+ height = (r->font->ascent - r->font->descent)/2;
+ width = gi.width - gi.x;
+#else
+ XRectangle rect;
+ XmbTextExtents(*r->font, str, len, nil, &rect);
+ height = rect.height;
+ width = rect.width;
+#endif
+ if (ret_width != nil) *ret_width = width;
+ if (ret_height != nil) *ret_height = height;
+ return width;
+}
+
+void draw_string(char *str, int len, int x, int y, struct rendering *r, enum text_type tt) {
+#ifdef USE_XFT
+ XftColor xftcolor;
+ if (tt == PROMPT) xftcolor = r->xft_prompt;
+ if (tt == COMPL) xftcolor = r->xft_completion;
+ if (tt == COMPL_HIGH) xftcolor = r->xft_completion_highlighted;
+
+ XftDrawStringUtf8(r->xftdraw, &xftcolor, r->font, x, y, str, len);
+#else
+ GC gc;
+ if (tt == PROMPT) gc = r->prompt;
+ if (tt == COMPL) gc = r->completion;
+ if (tt == COMPL_HIGH) gc = r->completion_highlighted;
+ Xutf8DrawString(r->d, r->w, *r->font, gc, x, y, str, len);
+#endif
}
+char *strdupn(char *str) {
+ int len = strlen(str);
+
+ if (str == nil || len == 0)
+ return nil;
+
+ char *dup = strdup(str);
+ if (dup == nil)
+ return nil;
+
+ for (int i = 0; i < len; ++i)
+ if (dup[i] == ' ')
+ dup[i] = 'n';
+
+ return dup;
+}
+
// |------------------|----------------------------------------------|
// | 20 char text | completion | completion | completion | compl |
// |------------------|----------------------------------------------|
// TODO: make these dynamic?
int prompt_width = 20; // char
int padding = 10;
- /* int start_at = XTextWidth(r->font, " ", 1) * prompt_width + padding; */
- XRectangle rect;
- int ps1xlen = XmbTextExtents(*r->font, r->ps1, r->ps1len, nil, &rect);
+ int width, height;
+ char *ps1_dup = strdupn(r->ps1);
+ ps1_dup = ps1_dup == nil ? r->ps1 : ps1_dup;
+ int ps1xlen = text_extents(ps1_dup, r->ps1len, r, &width, &height);
+ free(ps1_dup);
int start_at = ps1xlen;
- start_at += XmbTextExtents(*r->font, " ", 1, nil, &rect);
+ start_at = text_extents("n", 1, r, nil, nil);
start_at = start_at * prompt_width + padding;
- int texty = (rect.height + r->height) >>1;
+ int texty = (height + r->height) >>1;
XFillRectangle(r->d, r->w, r->prompt_bg, 0, 0, start_at, r->height);
int text_len = strlen(text);
if (text_len > prompt_width)
text = text + (text_len - prompt_width);
- /* XDrawString(r->d, r->w, r->prompt, padding, texty, text, MIN(text_len, prompt_width)); */
- Xutf8DrawString(r->d, r->w, *r->font, r->prompt, padding, texty, r->ps1, r->ps1len);
- Xutf8DrawString(r->d, r->w, *r->font, r->prompt, padding + ps1xlen, texty, text, MIN(text_len, prompt_width));
+ draw_string(r->ps1, r->ps1len, padding, texty, r, PROMPT);
+ draw_string(text, MIN(text_len, prompt_width), padding + ps1xlen, texty, r, PROMPT);
XFillRectangle(r->d, r->w, r->completion_bg, start_at, 0, r->width, r->height);
while (cs != nil) {
- GC g = cs->selected ? r->completion_highlighted : r->completion;
+ enum text_type tt = cs->selected ? COMPL_HIGH : COMPL;
GC h = cs->selected ? r->completion_highlighted_bg : r->completion_bg;
int len = strlen(cs->completion);
- /* int text_width = XTextWidth(r->font, cs->completion, len); */
- int text_width = XmbTextExtents(*r->font, cs->completion, len, nil, nil);
+ int text_width = text_extents(cs->completion, len, r, nil, nil);
XFillRectangle(r->d, r->w, h, start_at, 0, text_width + padding*2, r->height);
- /* XDrawString(r->d, r->w, g, start_at + padding, texty, cs->completion, len); */
- Xutf8DrawString(r->d, r->w, *r->font, g, start_at + padding, texty, cs->completion, len);
+ draw_string(cs->completion, len, start_at + padding, texty, r, tt);
start_at += text_width + padding * 2;
void draw_vertically(struct rendering *r, char *text, struct completions *cs) {
int padding = 10; // TODO make this dynamic
- XRectangle rect;
- XmbTextExtents(*r->font, "fjpgl", 5, &rect, nil);
- int start_at = rect.height + padding*2;
+ int height, width;
+ text_extents("fjpgl", 5, r, &width, &height);
+ int start_at = height + padding*2;
XFillRectangle(r->d, r->w, r->completion_bg, 0, 0, r->width, r->height);
XFillRectangle(r->d, r->w, r->prompt_bg, 0, 0, r->width, start_at);
- int ps1xlen = XmbTextExtents(*r->font, r->ps1, r->ps1len, nil, nil);
- Xutf8DrawString(r->d, r->w, *r->font, r->prompt, padding, padding*2, r->ps1, r->ps1len);
- Xutf8DrawString(r->d, r->w, *r->font, r->prompt, padding + ps1xlen, padding*2, text, strlen(text));
+ char *ps1_dup = strdupn(r->ps1);
+ ps1_dup = ps1_dup == nil ? r->ps1 : ps1_dup;
+ int ps1xlen = text_extents(ps1_dup, r->ps1len, r, nil, nil);
+ free(ps1_dup);
+
+ draw_string(r->ps1, r->ps1len, padding, padding*2, r, PROMPT);
+ draw_string(text, strlen(text), padding + ps1xlen, padding*2, r, PROMPT);
+
while (cs != nil) {
- GC g = cs->selected ? r->completion_highlighted : r->completion;
+ enum text_type tt = cs->selected ? COMPL_HIGH : COMPL;
GC h = cs->selected ? r->completion_highlighted_bg : r->completion_bg;
int len = strlen(cs->completion);
- XmbTextExtents(*r->font, cs->completion, len, &rect, nil);
- XFillRectangle(r->d, r->w, h, 0, start_at, r->width, rect.height + padding*2);
- Xutf8DrawString(r->d, r->w, *r->font, g, padding, start_at + padding*2, cs->completion, len);
+ text_extents(cs->completion, len, r, &width, &height);
+ XFillRectangle(r->d, r->w, h, 0, start_at, r->width, height + padding*2);
+ draw_string(cs->completion, len, padding, start_at + padding*2, r, tt);
- start_at += rect.height + padding *2;
+ start_at += height + padding *2;
if (start_at > r->height)
break; // don't draw completion if the space isn't enough
}
int main() {
- /* char *lines[INITIAL_ITEMS] = {0}; */
char **lines = calloc(INITIAL_ITEMS, sizeof(char*));
int nlines = readlines(&lines, INITIAL_ITEMS);
/* font = XLoadQueryFont(d, "fixed"); */
/* } */
// load the font
+#ifdef USE_XFT
+ XftFont *font = XftFontOpenName(d, DefaultScreen(d), fontname);
+#else
char **missing_charset_list;
int missing_charset_count;
XFontSet font = XCreateFontSet(d, fontname, &missing_charset_list, &missing_charset_count, nil);
fprintf(stderr, "Unable to load the font(s) %s\n", fontname);
return EX_UNAVAILABLE;
}
+#endif
// create the window
XSetWindowAttributes attr;
set_win_atoms_hints(d, w, width, height);
// we want some events
- XSelectInput(d, w, StructureNotifyMask | KeyPressMask | KeyReleaseMask | KeymapStateMask);
+ XSelectInput(d, w, StructureNotifyMask | KeyPressMask | KeymapStateMask);
// make the window appear on the screen
XMapWindow(d, w);
struct rendering r = {
.d = d,
.w = w,
+#ifdef USE_XFT
+ .font = font,
+#else
+ .font = &font,
+#endif
.prompt = XCreateGC(d, w, 0, &values),
.prompt_bg = XCreateGC(d, w, 0, &values),
.completion = XCreateGC(d, w, 0, &values),
.completion_bg = XCreateGC(d, w, 0, &values),
.completion_highlighted = XCreateGC(d, w, 0, &values),
.completion_highlighted_bg = XCreateGC(d, w, 0, &values),
- /* .prompt = XCreateGC(d, w, GCFont, &values), */
- /* .prompt_bg = XCreateGC(d, w, GCFont, &values), */
- /* .completion = XCreateGC(d, w, GCFont, &values), */
- /* .completion_bg = XCreateGC(d, w, GCFont, &values), */
- /* .completion_highlighted = XCreateGC(d, w, GCFont, &values), */
- /* .completion_highlighted_bg = XCreateGC(d, w, GCFont, &values), */
.width = width,
.height = height,
- .font = &font,
.horizontal_layout = horizontal_layout,
.ps1 = ps1,
.ps1len = strlen(ps1)
};
+
+#ifdef USE_XFT
+ r.xftdraw = XftDrawCreate(d, w, DefaultVisual(d, 0), DefaultColormap(d, 0));
+ // prompt
+ XRenderColor xrcolor;
+ xrcolor.red = p_fg.red;
+ xrcolor.green = p_fg.red;
+ xrcolor.blue = p_fg.red;
+ xrcolor.alpha = 65535;
+ XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_prompt);
+
+ // completion
+ xrcolor.red = compl_fg.red;
+ xrcolor.green = compl_fg.green;
+ xrcolor.blue = compl_fg.blue;
+ xrcolor.alpha = 65535;
+ XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_completion);
+
+ // completion highlighted
+ xrcolor.red = compl_highlighted_fg.red;
+ xrcolor.green = compl_highlighted_fg.green;
+ xrcolor.blue = compl_highlighted_fg.blue;
+ xrcolor.alpha = 65535;
+ XftColorAllocValue(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &xrcolor, &r.xft_completion_highlighted);
+#endif
+
// load the colors in our GCs
XSetForeground(d, r.prompt, p_fg.pixel);
XSetForeground(d, r.prompt_bg, p_bg.pixel);
XRefreshKeyboardMapping(&e.xmapping);
break;
- case KeyRelease: break; // ignore this
-
case KeyPress: {
XKeyPressedEvent *ev = (XKeyPressedEvent*)&e;
free(lines[i]);
}
+#ifdef USE_XFT
+ XftColorFree(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &r.xft_prompt);
+ XftColorFree(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &r.xft_completion);
+ XftColorFree(d, DefaultVisual(d, 0), DefaultColormap(d, 0), &r.xft_completion_highlighted);
+#endif
+
free(ps1);
free(fontname);
free(text);