Refactored code, changed resources and config.h

This commit is contained in:
phillbush 2020-05-26 18:43:45 -03:00
parent 2744390027
commit fd530f3f68
3 changed files with 159 additions and 137 deletions

View File

@ -1,11 +1,20 @@
static const char *font = "monospace:size=9"; /* font */
static const char *background = "#FFFFFF"; static const char *font = "monospace:size=9"; /* for regular items */
static const char *foreground = "#2E3436";
static const char *selbackground = "#3584E4"; /* colors */
static const char *selforeground = "#FFFFFF"; static const char *background_color = "#FFFFFF";
static const char *separator = "#CDC7C2"; static const char *foreground_color = "#2E3436";
static const char *border = "#E6E6E6"; static const char *selbackground_color = "#3584E4";
static int width = 130; static const char *selforeground_color = "#FFFFFF";
static int itemborder = 4; static const char *separator_color = "#CDC7C2";
static int menuborder = 1; static const char *border_color = "#E6E6E6";
static int separatorsize = 1;
/* sizes in pixels */
static int width_pixels = 130; /* minimum width of a menu */
static int padding_pixels = 4; /* padding around label in a item */
static int border_pixels = 1; /* menu border */
static int separator_pixels = 3; /* space around separator */
/* geometry of the right-pointing isoceles triangle for submenus */
static const int triangle_width = 3;
static const int triangle_height = 7;

View File

@ -94,13 +94,13 @@ The color of the separator between itens in the menu.
.B xmenu.width .B xmenu.width
The minimum width, in pixels, of the items in the menu. The minimum width, in pixels, of the items in the menu.
.TP .TP
.B xmenu.itemborder .B xmenu.padding
The size in pixels of the border around the label text in items in the menu. The size in pixels of the padding around the label text in items in the menu.
.TP .TP
.B xmenu.menuborder .B xmenu.borderWidth
The size in pixels of the border around the menu. The size in pixels of the border around the menu.
.TP .TP
.B xmenu.separatorsize .B xmenu.separatorWidth
The size in pixels of the item separator. The size in pixels of the item separator.
.SH EXAMPLES .SH EXAMPLES

193
xmenu.c
View File

@ -25,7 +25,8 @@ enum {ColorFG, ColorBG, ColorLast};
struct DC { struct DC {
XftColor normal[ColorLast]; XftColor normal[ColorLast];
XftColor selected[ColorLast]; XftColor selected[ColorLast];
XftColor decoration[ColorLast]; XftColor border;
XftColor separator;
Drawable d; Drawable d;
GC gc; GC gc;
@ -33,7 +34,7 @@ struct DC {
}; };
/* menu geometry structure */ /* menu geometry structure */
struct Geometry { struct MenuGeometry {
int itemb; /* item border */ int itemb; /* item border */
int itemw; /* item width */ int itemw; /* item width */
int itemh; /* item height */ int itemh; /* item height */
@ -80,6 +81,8 @@ static void setupgeom(void);
static struct Item *allocitem(const char *label, const char *output); static struct Item *allocitem(const char *label, const char *output);
static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level); static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
static void getmenuitem(Window win, int y, struct Menu **menu_ret, struct Item **item_ret); static void getmenuitem(Window win, int y, struct Menu **menu_ret, struct Item **item_ret);
static void drawseparator(struct Menu *menu, struct Item *item);
static void drawitem(struct Menu *menu, struct Item *item, XftColor *color);
static void drawmenu(void); static void drawmenu(void);
static void calcscreengeom(void); static void calcscreengeom(void);
static void calcmenu(struct Menu *menu); static void calcmenu(struct Menu *menu);
@ -99,16 +102,14 @@ static Visual *visual;
static Window rootwin; static Window rootwin;
static int screen; static int screen;
static struct DC dc; static struct DC dc;
static struct ScreenGeometry screengeom;
/* menu variables */ /* menu variables */
static struct Menu *rootmenu = NULL; static struct Menu *rootmenu = NULL;
static struct Menu *currmenu = NULL; static struct Menu *currmenu = NULL;
static char **menutitle; static char **menutitle;
static int menutitlecount; static int menutitlecount;
static struct MenuGeometry geom;
/* geometry variables */
static struct Geometry geom;
static struct ScreenGeometry screengeom;
#include "config.h" #include "config.h"
@ -154,10 +155,6 @@ main(int argc, char *argv[])
grabpointer(); grabpointer();
grabkeyboard(); grabkeyboard();
/* map root menu */
setcurrmenu(rootmenu);
XMapWindow(dpy, rootmenu->win);
/* run event loop */ /* run event loop */
run(); run();
@ -171,44 +168,44 @@ getresources(void)
{ {
char *xrm; char *xrm;
long n; long n;
XrmInitialize();
if ((xrm = XResourceManagerString(dpy))) {
char *type; char *type;
XrmDatabase xdb; XrmDatabase xdb;
XrmValue xval; XrmValue xval;
XrmInitialize();
if ((xrm = XResourceManagerString(dpy)) == NULL)
return;
xdb = XrmGetStringDatabase(xrm); xdb = XrmGetStringDatabase(xrm);
if (XrmGetResource(xdb, "xmenu.menuborder", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.borderWidth", "*", &type, &xval) == True)
if ((n = strtol(xval.addr, NULL, 10)) > 0) if ((n = strtol(xval.addr, NULL, 10)) > 0)
menuborder = n; border_pixels = n;
if (XrmGetResource(xdb, "xmenu.separatorsize", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.separatorWidth", "*", &type, &xval) == True)
if ((n = strtol(xval.addr, NULL, 10)) > 0) if ((n = strtol(xval.addr, NULL, 10)) > 0)
separatorsize = n; separator_pixels = n;
if (XrmGetResource(xdb, "xmenu.itemborder", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.padding", "*", &type, &xval) == True)
if ((n = strtol(xval.addr, NULL, 10)) > 0) if ((n = strtol(xval.addr, NULL, 10)) > 0)
itemborder = n; padding_pixels = n;
if (XrmGetResource(xdb, "xmenu.width", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.width", "*", &type, &xval) == True)
if ((n = strtol(xval.addr, NULL, 10)) > 0) if ((n = strtol(xval.addr, NULL, 10)) > 0)
width = n; width_pixels = n;
if (XrmGetResource(xdb, "xmenu.background", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.background", "*", &type, &xval) == True)
background = strdup(xval.addr); background_color = strdup(xval.addr);
if (XrmGetResource(xdb, "xmenu.foreground", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.foreground", "*", &type, &xval) == True)
foreground = strdup(xval.addr); foreground_color = strdup(xval.addr);
if (XrmGetResource(xdb, "xmenu.selbackground", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.selbackground", "*", &type, &xval) == True)
selbackground = strdup(xval.addr); selbackground_color = strdup(xval.addr);
if (XrmGetResource(xdb, "xmenu.selforeground", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.selforeground", "*", &type, &xval) == True)
selforeground = strdup(xval.addr); selforeground_color = strdup(xval.addr);
if (XrmGetResource(xdb, "xmenu.separator", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.separator", "*", &type, &xval) == True)
separator = strdup(xval.addr); separator_color = strdup(xval.addr);
if (XrmGetResource(xdb, "xmenu.border", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.border", "*", &type, &xval) == True)
border = strdup(xval.addr); border_color = strdup(xval.addr);
if (XrmGetResource(xdb, "xmenu.font", "*", &type, &xval) == True) if (XrmGetResource(xdb, "xmenu.font", "*", &type, &xval) == True)
font = strdup(xval.addr); font = strdup(xval.addr);
XrmDestroyDatabase(xdb); XrmDestroyDatabase(xdb);
}
} }
/* get color from color string */ /* get color from color string */
@ -224,18 +221,18 @@ static void
setupdc(void) setupdc(void)
{ {
/* get color pixels */ /* get color pixels */
getcolor(background, &dc.normal[ColorBG]); getcolor(background_color, &dc.normal[ColorBG]);
getcolor(foreground, &dc.normal[ColorFG]); getcolor(foreground_color, &dc.normal[ColorFG]);
getcolor(selbackground, &dc.selected[ColorBG]); getcolor(selbackground_color, &dc.selected[ColorBG]);
getcolor(selforeground, &dc.selected[ColorFG]); getcolor(selforeground_color, &dc.selected[ColorFG]);
getcolor(separator, &dc.decoration[ColorBG]); getcolor(separator_color, &dc.separator);
getcolor(border, &dc.decoration[ColorFG]); getcolor(border_color, &dc.border);
/* try to get font */ /* try to get font */
if ((dc.font = XftFontOpenName(dpy, screen, font)) == NULL) if ((dc.font = XftFontOpenName(dpy, screen, font)) == NULL)
errx(1, "cannot load font"); errx(1, "cannot load font");
/* create GC */ /* create common GC */
dc.gc = XCreateGC(dpy, rootwin, 0, NULL); dc.gc = XCreateGC(dpy, rootwin, 0, NULL);
} }
@ -243,11 +240,11 @@ setupdc(void)
static void static void
setupgeom(void) setupgeom(void)
{ {
geom.itemb = itemborder; geom.itemb = padding_pixels;
geom.itemh = dc.font->height + itemborder * 2; geom.itemh = dc.font->height + padding_pixels * 2;
geom.itemw = width; geom.itemw = width_pixels;
geom.border = menuborder; geom.border = border_pixels;
geom.separator = separatorsize; geom.separator = separator_pixels;
} }
/* allocate an item */ /* allocate an item */
@ -299,13 +296,15 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level)
menu->level = level; menu->level = level;
swa.override_redirect = True; swa.override_redirect = True;
swa.background_pixel = dc.decoration[ColorBG].pixel; swa.background_pixel = dc.normal[ColorBG].pixel;
swa.border_pixel = dc.decoration[ColorFG].pixel; swa.border_pixel = dc.border.pixel;
swa.save_under = True; /* pop-up windows should save_under*/
swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask
| PointerMotionMask | LeaveWindowMask; | PointerMotionMask | LeaveWindowMask;
menu->win = XCreateWindow(dpy, rootwin, 0, 0, geom.itemw, geom.itemh, geom.border, menu->win = XCreateWindow(dpy, rootwin, 0, 0, geom.itemw, geom.itemh, geom.border,
CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask, CWOverrideRedirect | CWBackPixel |
CWBorderPixel | CWEventMask | CWSaveUnder,
&swa); &swa);
return menu; return menu;
@ -573,6 +572,7 @@ setcurrmenu(struct Menu *currmenu_new)
/* if there was no currmenu, skip calculations */ /* if there was no currmenu, skip calculations */
if (currmenu == NULL) { if (currmenu == NULL) {
currmenu = currmenu_new; currmenu = currmenu_new;
XMapWindow(dpy, currmenu->win);
return; return;
} }
@ -607,9 +607,49 @@ setcurrmenu(struct Menu *currmenu_new)
item = NULL; item = NULL;
for (menu = currmenu; menu != lcamenu; menu = menu->parent) { for (menu = currmenu; menu != lcamenu; menu = menu->parent) {
XMapWindow(dpy, menu->win); XMapWindow(dpy, menu->win);
if (item != NULL) }
menu->selected = item; }
item = menu->caller;
/* draw separator item */
static void
drawseparator(struct Menu *menu, struct Item *item)
{
int linex, liney, linew;
linex = dc.font->height;
liney = item->y + item->h/2;
linew = menu->w - dc.font->height;
XSetForeground(dpy, dc.gc, dc.separator.pixel);
XDrawLine(dpy, menu->pixmap, dc.gc, linex, liney, linew, liney);
}
/* draw regular item */
static void
drawitem(struct Menu *menu, struct Item *item, XftColor *color)
{
int x, y;
x = 0 + dc.font->height;
y = item->y + dc.font->height + geom.itemb / 2;
XSetForeground(dpy, dc.gc, color[ColorFG].pixel);
XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font,
x, y, item->label, item->labellen);
/* draw triangle, if item contains a submenu */
if (item->submenu != NULL) {
x = menu->w - dc.font->height + geom.itemb - 1;
y = item->y + geom.itemh/2 - triangle_height/2 - 1;
XPoint triangle[] = {
{x, y},
{x + triangle_width, y + triangle_height/2},
{x, y + triangle_height},
{x, y}
};
XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
Convex, CoordModeOrigin);
} }
} }
@ -623,52 +663,26 @@ drawmenu(void)
for (menu = currmenu; menu != NULL; menu = menu->parent) { for (menu = currmenu; menu != NULL; menu = menu->parent) {
for (item = menu->list; item != NULL; item = item->next) { for (item = menu->list; item != NULL; item = item->next) {
XftColor *color; XftColor *color;
int labelx, labely;
/* determine item color */ /* determine item color */
if (item == menu->selected) if (item == menu->selected && item->label != NULL)
color = dc.selected; color = dc.selected;
else else
color = dc.normal; color = dc.normal;
/* continue if item is a separator */
if (item->label == NULL)
continue;
/* draw item box */ /* draw item box */
XSetForeground(dpy, dc.gc, color[ColorBG].pixel); XSetForeground(dpy, dc.gc, color[ColorBG].pixel);
XDrawRectangle(dpy, menu->pixmap, dc.gc, 0, item->y,
menu->w, item->h);
XFillRectangle(dpy, menu->pixmap, dc.gc, 0, item->y, XFillRectangle(dpy, menu->pixmap, dc.gc, 0, item->y,
menu->w, item->h); menu->w, item->h);
/* draw item label */ if (item->label == NULL) /* item is a separator */
labelx = 0 + dc.font->height; drawseparator(menu, item);
labely = item->y + dc.font->height + geom.itemb / 2; else /* item is a regular item */
XSetForeground(dpy, dc.gc, color[ColorFG].pixel); drawitem(menu, item, color);
XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font,
labelx, labely, item->label,
item->labellen);
/* draw triangle, if item contains a submenu */
if (item->submenu != NULL) {
int trianglex = menu->w - dc.font->height + geom.itemb - 1;
int triangley = item->y + (3 * item->h)/8 -1;
XPoint triangle[] = {
{trianglex, triangley},
{trianglex + item->h/8 + 1, item->y + item->h/2},
{trianglex, triangley + item->h/4 + 2},
{trianglex, triangley}
};
XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
Convex, CoordModeOrigin);
} }
XCopyArea(dpy, menu->pixmap, menu->win, dc.gc, 0, item->y, XCopyArea(dpy, menu->pixmap, menu->win, dc.gc, 0, 0,
menu->w, item->h, 0, item->y); menu->w, menu->h, 0, 0);
}
} }
} }
@ -723,6 +737,8 @@ run(void)
KeySym ksym; KeySym ksym;
XEvent ev; XEvent ev;
setcurrmenu(rootmenu);
while (!XNextEvent(dpy, &ev)) { while (!XNextEvent(dpy, &ev)) {
switch(ev.type) { switch(ev.type) {
case Expose: case Expose:
@ -731,23 +747,22 @@ run(void)
break; break;
case MotionNotify: case MotionNotify:
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item); getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
if (menu != NULL && item != NULL) { if (menu == NULL || item == NULL)
break;
if (previtem != item) { if (previtem != item) {
menu->selected = item;
if (item->submenu != NULL) if (item->submenu != NULL)
setcurrmenu(item->submenu); setcurrmenu(item->submenu);
else else
setcurrmenu(menu); setcurrmenu(menu);
previtem = item; previtem = item;
drawmenu(); drawmenu();
} else if (menu->selected != item) {
menu->selected = item;
drawmenu();
}
} }
break; break;
case ButtonRelease: case ButtonRelease:
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item); getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
if (menu != NULL && item != NULL) { if (menu == NULL || item == NULL)
break;
selectitem: selectitem:
if (item->label == NULL) if (item->label == NULL)
break; /* ignore separators */ break; /* ignore separators */
@ -760,9 +775,6 @@ selectitem:
currmenu->selected = currmenu->list; currmenu->selected = currmenu->list;
drawmenu(); drawmenu();
break; break;
} else {
return;
}
case ButtonPress: case ButtonPress:
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item); getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
if (menu == NULL || item == NULL) if (menu == NULL || item == NULL)
@ -798,6 +810,7 @@ selectitem:
drawmenu(); drawmenu();
break; break;
case LeaveNotify: case LeaveNotify:
previtem = NULL;
currmenu->selected = NULL; currmenu->selected = NULL;
drawmenu(); drawmenu();
break; break;
@ -805,7 +818,7 @@ selectitem:
} }
} }
/* recursivelly free a pixmap */ /* recursivelly free pixmaps and destroy windows */
static void static void
freewindow(struct Menu *menu) freewindow(struct Menu *menu)
{ {
@ -830,8 +843,8 @@ cleanup(void)
XftColorFree(dpy, visual, colormap, &dc.normal[ColorFG]); XftColorFree(dpy, visual, colormap, &dc.normal[ColorFG]);
XftColorFree(dpy, visual, colormap, &dc.selected[ColorBG]); XftColorFree(dpy, visual, colormap, &dc.selected[ColorBG]);
XftColorFree(dpy, visual, colormap, &dc.selected[ColorFG]); XftColorFree(dpy, visual, colormap, &dc.selected[ColorFG]);
XftColorFree(dpy, visual, colormap, &dc.decoration[ColorBG]); XftColorFree(dpy, visual, colormap, &dc.separator);
XftColorFree(dpy, visual, colormap, &dc.decoration[ColorFG]); XftColorFree(dpy, visual, colormap, &dc.border);
XFreeGC(dpy, dc.gc); XFreeGC(dpy, dc.gc);
XCloseDisplay(dpy); XCloseDisplay(dpy);