shod

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 6d90761e51bde3cdfd346900277c85081d389ad3
parent 918eaa2431a7477683080b0c104f285aecbd1107
Author: seninha <lucas@seninha.org>
Date:   Sun,  6 Mar 2022 16:51:42 -0300

Several changes

• Add TYPE_SPLASH window type for splash screen windows.
• Add shadowThickness X resource for thickness of the 3D shadow.
• Split *Colors X resources into three different resources.

Diffstat:
MREADME | 32++++++++++++++++++++++++++++++++
Mconfig.h | 24+++++++++++-------------
Mshod.1 | 79++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mshod.c | 700++++++++++++++++++++++++++++++++++++++++---------------------------------------
4 files changed, 438 insertions(+), 397 deletions(-)

diff --git a/README b/README @@ -39,3 +39,35 @@ Screenshots: TODO: • Add support for dropdown (quake-like) windows. • Add support for window rules. + + * * * + +WARNING! +Shod now names Xresources for colors just like how motif names them. +Your Xresources may affect Shod. To avoid this, copy the following +lines to your ~/.Xresources, or ~/.Xdefaults, or any other file read +by xrdb(1). + +shod*background: #3465A4 +shod*topShadowColor: #729FCF +shod*bottomShadowColor: #204A87 +shod.inactive.background: #555753 +shod.inactive.topShadowColor: #888A85 +shod.inactive.bottomShadowColor: #2E3436 +shod.urgent.background: #CC0000 +shod.urgent.topShadowColor: #EF2929 +shod.urgent.bottomShadowColor: #A40000 +shod.dock.background: #121212 +shod.dock.topShadowColor: #2E3436 +shod.dock.bottomShadowColor: #000000 +shod.borderWidth: 6 +shod.dockGravity: E +shod.dockWidth: 64 +shod.numOfDesktops: 10 +shod.notifGap: 3 +shod.notifGravity: NE +shod.shadowThickness: 2 +shod.snapProximity: 8 +shod.title.font: fixed +shod.title.foreground: FG +shod.titleWidth: 17 diff --git a/config.h b/config.h @@ -1,7 +1,7 @@ struct Config config = { /* * except for the foreground, colors fields are strings - * containing up to three elements delimited by colon: + * containing three elements delimited by colon: * the body color, the color of the light 3D shadow, * and the color of the dark 3D shadow. */ @@ -15,23 +15,18 @@ struct Config config = { .dockwidth = 64, /* width of the dock (or its height, if it is horizontal) */ .dockspace = 64, /* size of each dockapp (64 for windowmaker dockapps) */ .dockgravity = "E", /* placement of the dock */ - .dockcolors = "#121212:#2E3436:#000000", + .dockcolors = {"#121212", "#2E3436", "#000000"}, /* notification configuration */ .notifgap = 3, /* gap, in pixels, between notifications */ .notifgravity = "NE", /* placement of notifications */ - .notifcolors = "#3465A4:#729FCF:#204A87", + .notifcolors = {"#3465A4", "#729FCF", "#204A87"}, /* prompt configuration */ - .promptcolors = "#3465A4:#729FCF:#204A87", + .promptcolors = {"#3465A4", "#729FCF", "#204A87"}, - /* container colors */ + /* title bar */ .titlewidth = 17, - .titlecolors = { - [FOCUSED] = "#3465A4:#729FCF:#204A87", - [UNFOCUSED] = "#555753:#888A85:#2E3436", - [URGENT] = "#CC0000:#EF2929:#A40000", - }, .foreground = { [FOCUSED] = "#FFFFFF", [UNFOCUSED] = "#FFFFFF", @@ -41,8 +36,11 @@ struct Config config = { /* border */ .borderwidth = 6, .bordercolors = { - [FOCUSED] = "#3465A4:#729FCF:#204A87", - [UNFOCUSED] = "#555753:#888A85:#2E3436", - [URGENT] = "#CC0000:#EF2929:#A40000", + [FOCUSED] = {"#3465A4", "#729FCF", "#204A87"}, + [UNFOCUSED] = {"#555753", "#888A85", "#2E3436"}, + [URGENT] = {"#CC0000", "#EF2929", "#A40000"}, }, + + /* size of 3D shadow effect, must be less than borderwidth */ + .shadowthickness = 2, }; diff --git a/shod.1 b/shod.1 @@ -445,6 +445,18 @@ Dialogs are small windows that communicates informate to the user and can prompt .PP .B shod only changes the position and size of a dialog window when the size of its leader changes. +.SS Splash Screens +Windows of type +.B _NET_WM_WINDOW_TYPE_SPLASH +(called splash screens) +are mapped above all other windows and are stacked on the order they are spawned. +Splash screens cannot be manipulated. +Splash screens have no decoration around them. +.PP +Splash screens are transient windows that appear temporarily while an application is loading. +.PP +.B shod +centers the splash screens on the monitor. .SS Menus Windows of type .BR _NET_WM_WINDOW_TYPE_MENU , @@ -536,29 +548,19 @@ The placement of the dock can be changed with the .SH RESOURCES .B shod understands the following X resources. +Some of them (the resources that set colors) occur in triplets, +and all of them must be set for a consistent theme. .TP .B shod.borderWidth The width of the borders and divisions. .TP -.B shod.border.activeColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of the borders of active windows. -.TP -.B shod.border.inactiveColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of the borders of inactive windows. -.TP -.B shod.border.urgentColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of the borders of urgent windows. +.B shod.active.background, shod.active.topShadowColor, shod.active.bottomShadowColor +The body color, light shadow color, and dark shadow color for the 3D +effect of the borders and title bars of active windows. .TP -.B shod.dockColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of the dock. +.B shod.dock.background, shod.dock.topShadowColor, shod.dock.bottomShadowColor +The body color, light shadow color, and dark shadow color for the 3D +effect of the dock. .TP .B shod.dockGravity The placement of the dock described with up to two uppercase letters. @@ -578,14 +580,17 @@ alligned to its center. The width (for vertical docks) or height (for horizontal dock) of the dock in pixels. Defaults to 64 (the size of most dockapps). .TP +.B shod.inactive.background, shod.inactive.topShadowColor, shod.inactive.bottomShadowColor +The body color, light shadow color, and dark shadow color for the 3D +effect of the borders and title bars of inactive windows. +.TP .B shod.numOfDesktops The number of desktops for each monitor. The default is 10 desktops for each monitor. .TP -.B shod.notifColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of the nofications. +.B shod.notif.background, shod.notif.topShadowColor, shod.notif.bottomShadowColor +The body color, light shadow color, and dark shadow color for the 3D +effect of the notification borders. .TP .B shod.notifGap The gap in pixels between notifications. @@ -596,10 +601,13 @@ For example, a value of "N" will place notifications centered on the north edge A value of "E" will place notifications centered on the east edge of the screen. A value of "NE" (the default) will place notifications on the northeast corner of the screen. .TP -.B shod.promptColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of the prompt decoration. +.B shod.prompt.background, shod.prompt.topShadowColor, shod.prompt.bottomShadowColor +The body color, light shadow color, and dark shadow color for the 3D +effect of the prompt borders. +.TP +.B shod.shadowThickness +Thickness of the 3D shadow effect. +Must be less than the border width. .TP .B shod.snapProximity The proximity of edges of a container in pixels for the snap attraction to occur when moving the container. @@ -612,23 +620,12 @@ The font of the text in the title bar. .B shod.title.foreground The color of the text in the title bar. .TP -.B shod.title.activeColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of title bars of active windows. -.TP -.B shod.title.inactiveColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of title bars of inactive windows. -.TP -.B shod.title.urgentColors -Up to three colors, delimited by colons, -for the body color, light shadow color, and dark shadow color for the 3D effect -of title bars of urgent windows. -.TP .B shod.titleWidth The width of the title bar. +.TP +.B shod.urgent.background, shod.urgent.topShadowColor, shod.urgent.bottomShadowColor +The body color, light shadow color, and dark shadow color for the 3D +effect of the borders and title bars of urgent windows. .SH ENVIRONMENT The following environment variables affect the execution of .B shod diff --git a/shod.c b/shod.c @@ -36,6 +36,7 @@ enum { TYPE_DIALOG, TYPE_NOTIFICATION, TYPE_PROMPT, + TYPE_SPLASH, TYPE_DOCKAPP }; @@ -94,6 +95,7 @@ enum { LAYER_NORMAL, LAYER_ABOVE, LAYER_DOCK, + LAYER_SPLASH, LAYER_FULLSCREEN, LAYER_LAST }; @@ -289,6 +291,7 @@ struct Winres { struct Row *row; /* row (with buttons) of window */ struct Tab *t; /* tab of window */ struct Dialog *d; /* dialog of window */ + struct Splash *splash; /* splash window */ struct Menu *menu; /* menu of window */ }; @@ -423,6 +426,13 @@ struct Dockapp { int ignoreunmap; /* number of unmap requests to ignore */ }; +/* splash screen window */ +struct Splash { + struct Splash *prev, *next; /* pointers for list of splash windows */ + Window win; /* actual client window */ + int x, y, w, h; /* splash screen geometry */ +}; + /* data of a monitor */ struct Monitor { struct Monitor *prev, *next; /* pointers for list of monitors */ @@ -484,6 +494,7 @@ struct MwmHints { /* window manager stuff */ struct WM { struct Bar *bars; /* list of bars */ + struct Splash *splashlist; /* list of splash screen windows */ struct Monitor *monhead, *montail; /* list of monitors */ struct Notification *nhead, *ntail; /* list of notifications */ struct Container *c; /* list of containers */ @@ -511,15 +522,15 @@ struct Config { int snap; int borderwidth; int titlewidth; + int shadowthickness; const char *font; const char *notifgravity; const char *dockgravity; const char *foreground[STYLE_LAST]; - const char *titlecolors[STYLE_LAST]; - const char *bordercolors[STYLE_LAST]; - const char *dockcolors; - const char *notifcolors; - const char *promptcolors; + const char *bordercolors[STYLE_LAST][COLOR_LAST]; + const char *dockcolors[COLOR_LAST]; + const char *notifcolors[COLOR_LAST]; + const char *promptcolors[COLOR_LAST]; /* the values below are computed from the values above */ int corner; @@ -540,7 +551,7 @@ static Window root; static GC gc; static Atom atoms[ATOM_LAST]; static struct Theme theme; -static struct WM wm; +static struct WM wm = {}; static struct Dock dock; static unsigned long clientmask = CWEventMask | CWColormap | CWBackPixel | CWBorderPixel; static unsigned int depth; @@ -665,12 +676,7 @@ getresources(void) if (xrm == NULL || xdb == NULL) return; - if (XrmGetResource(xdb, "shod.dockColors", "*", &type, &xval) == True) - config.dockcolors = xval.addr; - if (XrmGetResource(xdb, "shod.notifColors", "*", &type, &xval) == True) - config.notifcolors = xval.addr; - if (XrmGetResource(xdb, "shod.promptColors", "*", &type, &xval) == True) - config.promptcolors = xval.addr; + if (XrmGetResource(xdb, "shod.title.font", "*", &type, &xval) == True) config.font = xval.addr; if (XrmGetResource(xdb, "shod.title.foreground", "*", &type, &xval) == True) { @@ -678,21 +684,55 @@ getresources(void) config.foreground[1] = xval.addr; config.foreground[2] = xval.addr; } - if (XrmGetResource(xdb, "shod.title.activeColors", "*", &type, &xval) == True) - config.titlecolors[FOCUSED] = xval.addr; - if (XrmGetResource(xdb, "shod.title.inactiveColors", "*", &type, &xval) == True) - config.titlecolors[UNFOCUSED] = xval.addr; - if (XrmGetResource(xdb, "shod.title.urgentColors", "*", &type, &xval) == True) - config.titlecolors[URGENT] = xval.addr; - if (XrmGetResource(xdb, "shod.border.activeColors", "*", &type, &xval) == True) - config.bordercolors[FOCUSED] = xval.addr; - if (XrmGetResource(xdb, "shod.border.inactiveColors", "*", &type, &xval) == True) - config.bordercolors[UNFOCUSED] = xval.addr; - if (XrmGetResource(xdb, "shod.border.urgentColors", "*", &type, &xval) == True) - config.bordercolors[URGENT] = xval.addr; + + if (XrmGetResource(xdb, "shod.dock.background", "*", &type, &xval) == True) + config.dockcolors[COLOR_MID] = xval.addr; + if (XrmGetResource(xdb, "shod.dock.topShadowColor", "*", &type, &xval) == True) + config.dockcolors[COLOR_LIGHT] = xval.addr; + if (XrmGetResource(xdb, "shod.dock.bottomShadowColor", "*", &type, &xval) == True) + config.dockcolors[COLOR_DARK] = xval.addr; + + if (XrmGetResource(xdb, "shod.notif.background", "*", &type, &xval) == True) + config.notifcolors[COLOR_MID] = xval.addr; + if (XrmGetResource(xdb, "shod.notif.topShadowColor", "*", &type, &xval) == True) + config.notifcolors[COLOR_LIGHT] = xval.addr; + if (XrmGetResource(xdb, "shod.notif.bottomShadowColor", "*", &type, &xval) == True) + config.notifcolors[COLOR_DARK] = xval.addr; + + if (XrmGetResource(xdb, "shod.prompt.background", "*", &type, &xval) == True) + config.promptcolors[COLOR_MID] = xval.addr; + if (XrmGetResource(xdb, "shod.prompt.topShadowColor", "*", &type, &xval) == True) + config.promptcolors[COLOR_LIGHT] = xval.addr; + if (XrmGetResource(xdb, "shod.prompt.bottomShadowColor", "*", &type, &xval) == True) + config.promptcolors[COLOR_DARK] = xval.addr; + + if (XrmGetResource(xdb, "shod.active.background", "*", &type, &xval) == True) + config.bordercolors[FOCUSED][COLOR_MID] = xval.addr; + if (XrmGetResource(xdb, "shod.active.topShadowColor", "*", &type, &xval) == True) + config.bordercolors[FOCUSED][COLOR_LIGHT] = xval.addr; + if (XrmGetResource(xdb, "shod.active.bottomShadowColor", "*", &type, &xval) == True) + config.bordercolors[FOCUSED][COLOR_DARK] = xval.addr; + + if (XrmGetResource(xdb, "shod.inactive.background", "*", &type, &xval) == True) + config.bordercolors[UNFOCUSED][COLOR_MID] = xval.addr; + if (XrmGetResource(xdb, "shod.inactive.topShadowColor", "*", &type, &xval) == True) + config.bordercolors[UNFOCUSED][COLOR_LIGHT] = xval.addr; + if (XrmGetResource(xdb, "shod.inactive.bottomShadowColor", "*", &type, &xval) == True) + config.bordercolors[UNFOCUSED][COLOR_DARK] = xval.addr; + + if (XrmGetResource(xdb, "shod.urgent.background", "*", &type, &xval) == True) + config.bordercolors[URGENT][COLOR_MID] = xval.addr; + if (XrmGetResource(xdb, "shod.urgent.topShadowColor", "*", &type, &xval) == True) + config.bordercolors[URGENT][COLOR_LIGHT] = xval.addr; + if (XrmGetResource(xdb, "shod.urgent.bottomShadowColor", "*", &type, &xval) == True) + config.bordercolors[URGENT][COLOR_DARK] = xval.addr; + if (XrmGetResource(xdb, "shod.borderWidth", "*", &type, &xval) == True) if ((n = strtol(xval.addr, NULL, 10)) > 0 && n < 100) config.borderwidth = n; + if (XrmGetResource(xdb, "shod.shadowThickness", "*", &type, &xval) == True) + if ((n = strtol(xval.addr, NULL, 10)) > 0 && n < 100) + config.shadowthickness = n; if (XrmGetResource(xdb, "shod.titleWidth", "*", &type, &xval) == True) if ((n = strtol(xval.addr, NULL, 10)) > 0 && n < 100) config.titlewidth = n; @@ -928,44 +968,26 @@ initroot(void) XSetInputFocus(dpy, root, RevertToParent, CurrentTime); } -/* allocate the three colors of an object */ -static void -createcolors(unsigned long *colors, const char *str) -{ - int i; - char *s, *t, *p; - - p = t = s = estrndup(str, strlen(str)); - for (i = 0; i < COLOR_LAST; i++) { - while (*p && *p != ':') - p++; - *(p++) = '\0'; - if (t == 0 || *t != '\0') - colors[i] = ealloccolor(t); - else - colors[i] = colors[i - 1]; - t = p; - } - free(s); -} - /* initialize decoration pixmap */ static void inittheme(void) { - int i; + int i, j; config.corner = config.borderwidth + config.titlewidth; config.divwidth = config.borderwidth; wm.minsize = config.corner * 2 + 10; for (i = 0; i < STYLE_LAST; i++) { - createcolors(theme.title[i], config.titlecolors[i]); - createcolors(theme.border[i], config.bordercolors[i]); + for (j = 0; j < COLOR_LAST; j++) { + theme.border[i][j] = ealloccolor(config.bordercolors[i][j]); + } theme.fg[i] = ealloccolor(config.foreground[i]); } - createcolors(theme.dock, config.dockcolors); - createcolors(theme.notif, config.notifcolors); - createcolors(theme.prompt, config.promptcolors); + for (j = 0; j < COLOR_LAST; j++) { + theme.dock[j] = ealloccolor(config.dockcolors[j]); + theme.notif[j] = ealloccolor(config.notifcolors[j]); + theme.prompt[j] = ealloccolor(config.promptcolors[j]); + } } /* create dock window */ @@ -996,18 +1018,11 @@ getwin(Window win) struct Tab *t; struct Dialog *d; struct Menu *menu; + struct Splash *splash; struct Notification *n; int i; - res.dock = NULL; - res.dapp = NULL; - res.bar = NULL; - res.row = NULL; - res.n = NULL; - res.c = NULL; - res.t = NULL; - res.d = NULL; - res.menu = NULL; + res = (struct Winres){0}; if (win == dock.win) { res.dock = &dock; return res; @@ -1030,6 +1045,12 @@ getwin(Window win) return res; } } + for (splash = wm.splashlist; splash != NULL; splash = splash->next) { + if (win == splash->win) { + res.splash = splash; + return res; + } + } for (c = wm.c; c != NULL; c = c->next) { if (win == c->frame) { res.c = c; @@ -1216,6 +1237,8 @@ getwintype(Window win, struct Tab **parent, Window *leader) return TYPE_NOTIFICATION; if (prop == atoms[_NET_WM_WINDOW_TYPE_PROMPT]) return TYPE_PROMPT; + if (prop == atoms[_NET_WM_WINDOW_TYPE_SPLASH]) + return TYPE_SPLASH; if (ismenu || prop == atoms[_NET_WM_WINDOW_TYPE_MENU] || prop == atoms[_NET_WM_WINDOW_TYPE_UTILITY] || @@ -1796,6 +1819,30 @@ winisurgent(Window win) return ret; } +/* if window is bigger than monitor, resize it while maintaining proportion */ +static void +fitmonitor(struct Monitor *mon, int *x, int *y, int *w, int *h, float factor) +{ + int origw, origh; + int minw, minh; + + origw = *w; + origh = *h; + minw = min(origw, mon->ww * factor); + minh = min(origh, mon->wh * factor); + if (origw * minh > origh * minw) { + minh = (origh * minw) / origw; + minw = (origw * minh) / origh; + } else { + minw = (origw * minh) / origh; + minh = (origh * minw) / origw; + } + *w = max(wm.minsize, minw); + *h = max(wm.minsize, minh); + *x = max(mon->wx, min(mon->wx + mon->ww - *w, *x)); + *y = max(mon->wy, min(mon->wy + mon->wh - *h, *y)); +} + /* get tab decoration style */ static int tabgetstyle(struct Tab *t) @@ -2011,29 +2058,12 @@ containerplace(struct Container *c, struct Desktop *desk, int userplaced) int ya, yb, xa, xb; int subx, suby; /* position of the larger subregion */ int subw, subh; /* larger subregion width and height */ - int origw, origh; if (desk == NULL || c == NULL || c->isminimized) return; mon = desk->mon; - - /* if window is bigger than monitor, resize it while maintaining proportion */ - origw = c->nw; - origh = c->nh; - w = min(origw, mon->ww); - h = min(origh, mon->wh); - if (origw * h > origh * w) { - h = (origh * w) / origw; - w = (origw * h) / origh; - } else { - w = (origw * h) / origh; - h = (origh * w) / origw; - } - c->nw = max(wm.minsize, w); - c->nh = max(wm.minsize, h); - c->nx = max(mon->wx, min(mon->wx + mon->ww - c->nw, c->nx)); - c->ny = max(mon->wy, min(mon->wy + mon->wh - c->nh, c->ny)); + fitmonitor(mon, &c->nx, &c->ny, &c->nw, &c->nh, 1.0); /* if the user placed the window, we should not re-place it */ if (userplaced) @@ -2122,40 +2152,46 @@ containerplace(struct Container *c, struct Desktop *desk, int userplaced) /* draw rectangle shadows */ static void -drawrectangle(Pixmap pix, int doubleshadow, int x, int y, int w, int h, unsigned long top, unsigned long bot) +drawrectangle(Pixmap pix, int x, int y, int w, int h, unsigned long top, unsigned long bot) { XGCValues val; - XRectangle recs[4]; + XRectangle *recs; + int i; if (w <= 0 || h <= 0) return; + recs = ecalloc(config.shadowthickness * 2, sizeof(*recs)); + /* draw light shadow */ - recs[0] = (XRectangle){.x = x + 0, .y = y + 0, .width = 1, .height = h - 1}; - recs[1] = (XRectangle){.x = x + 0, .y = y + 0, .width = w - 1, .height = 1}; - recs[2] = (XRectangle){.x = x + 1, .y = y + 1, .width = 1, .height = h - 3}; - recs[3] = (XRectangle){.x = x + 1, .y = y + 1, .width = w - 3, .height = 1}; + for(i = 0; i < config.shadowthickness; i++) { + recs[i * 2] = (XRectangle){.x = x + i, .y = y + i, .width = 1, .height = h - (i * 2 + 1)}; + recs[i * 2 + 1] = (XRectangle){.x = x + i, .y = y + i, .width = w - (i * 2 + 1), .height = 1}; + } val.foreground = top; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, pix, gc, recs, doubleshadow ? 4 : 2); + XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 2); /* draw dark shadow */ - recs[0] = (XRectangle){.x = x + w - 1, .y = y + 0, .width = 1, .height = h}; - recs[1] = (XRectangle){.x = x + 0, .y = y + h - 1, .width = w, .height = 1}; - recs[2] = (XRectangle){.x = x + w - 2, .y = y + 1, .width = 1, .height = h - 2}; - recs[3] = (XRectangle){.x = x + 1, .y = y + h - 2, .width = w - 2, .height = 1}; + for(i = 0; i < config.shadowthickness; i++) { + recs[i * 2] = (XRectangle){.x = x + w - 1 - i, .y = y + i, .width = 1, .height = h - i * 2}; + recs[i * 2 + 1] = (XRectangle){.x = x + i, .y = y + h - 1 - i, .width = w - i * 2, .height = 1}; + } val.foreground = bot; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, pix, gc, recs, doubleshadow ? 4 : 2); + XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 2); + + free(recs); } /* draw borders with shadows */ static void -drawborders(Pixmap pix, int doubleshadow, int w, int h, unsigned long *decor) +drawborders(Pixmap pix, int w, int h, unsigned long *decor) { XGCValues val; - XRectangle recs[8]; + XRectangle *recs; int partw, parth; + int i; if (w <= 0 || h <= 0) return; @@ -2163,76 +2199,36 @@ drawborders(Pixmap pix, int doubleshadow, int w, int h, unsigned long *decor) partw = w - 2 * config.borderwidth; parth = h - 2 * config.borderwidth; + recs = ecalloc(config.shadowthickness * 4, sizeof(*recs)); + /* draw background */ val.foreground = decor[COLOR_MID]; XChangeGC(dpy, gc, GCForeground, &val); XFillRectangle(dpy, pix, gc, 0, 0, w, h); /* draw light shadow */ - recs[0] = (XRectangle){.x = 0, .y = 0, .width = 1, .height = h - 1}; - recs[1] = (XRectangle){.x = 0, .y = 0, .width = w - 1, .height = 1}; - recs[2] = (XRectangle){ - .x = w - config.borderwidth, - .y = config.borderwidth - 1, - .width = 1, - .height = parth + 2 - }; - recs[3] = (XRectangle){ - .x = config.borderwidth - 1, - .y = h - config.borderwidth, - .width = partw + 2, - .height = 1 - }; - recs[4] = (XRectangle){.x = 1, .y = 1, .width = 1, .height = h - 2}; - recs[5] = (XRectangle){.x = 1, .y = 1, .width = w - 2, .height = 1}; - recs[6] = (XRectangle){ - .x = w - config.borderwidth + 1, - .y = config.borderwidth - 2, - .width = 1, - .height = parth + 4 - }; - recs[7] = (XRectangle){ - .x = config.borderwidth - 2, - .y = h - config.borderwidth + 1, - .width = partw + 4, - .height = 1 - }; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 4 + 0] = (XRectangle){.x = i, .y = i, .width = 1, .height = h - 1 - i}; + recs[i * 4 + 1] = (XRectangle){.x = i, .y = i, .width = w - 1 - i, .height = 1}; + recs[i * 4 + 2] = (XRectangle){.x = w - config.borderwidth + i, .y = config.borderwidth - 1 - i, .width = 1, .height = parth + 2 * (i + 1)}; + recs[i * 4 + 3] = (XRectangle){.x = config.borderwidth - 1 - i, .y = h - config.borderwidth + i, .width = partw + 2 * (i + 1), .height = 1}; + } val.foreground = decor[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, pix, gc, recs, doubleshadow ? 8 : 4); + XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 4); /* draw dark shadow */ - recs[0] = (XRectangle){.x = w - 1, .y = 0, .width = 1, .height = h}; - recs[1] = (XRectangle){.x = 0, .y = h - 1, .width = w, .height = 1}; - recs[2] = (XRectangle){ - .x = config.borderwidth - 1, - .y = config.borderwidth - 1, - .width = 1, - .height = parth + 1 - }; - recs[3] = (XRectangle){ - .x = config.borderwidth - 1, - .y = config.borderwidth - 1, - .width = partw + 1, - .height = 1 - }; - recs[4] = (XRectangle){.x = w - 2, .y = 1, .width = 1, .height = h - 2}; - recs[5] = (XRectangle){.x = 1, .y = h - 2, .width = w - 2, .height = 1}; - recs[6] = (XRectangle){ - .x = config.borderwidth - 2, - .y = config.borderwidth - 2, - .width = 1, - .height = parth + 3 - }; - recs[7] = (XRectangle){ - .x = config.borderwidth - 2, - .y = config.borderwidth - 2, - .width = partw + 3, - .height = 1 - }; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 4 + 0] = (XRectangle){.x = w - 1 - i, .y = i, .width = 1, .height = h - i * 2}; + recs[i * 4 + 1] = (XRectangle){.x = i, .y = h - 1 - i, .width = w - i * 2, .height = 1}; + recs[i * 4 + 2] = (XRectangle){.x = config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i, .width = 1, .height = parth + 1 + i * 2}; + recs[i * 4 + 3] = (XRectangle){.x = config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i, .width = partw + 1 + i * 2, .height = 1}; + } val.foreground = decor[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, pix, gc, recs, doubleshadow ? 8 : 4); + XFillRectangles(dpy, pix, gc, recs, config.shadowthickness * 4); + + free(recs); } /* decorate dialog window */ @@ -2255,7 +2251,7 @@ dialogdecorate(struct Dialog *d) d->pw = fullw; d->ph = fullh; - drawborders(d->pix, (config.borderwidth - 4 > 0), fullw, fullh, decor); + drawborders(d->pix, fullw, fullh, decor); XCopyArea(dpy, d->pix, d->frame, gc, 0, 0, fullw, fullh, 0, 0); } @@ -2273,18 +2269,18 @@ tabdecorate(struct Tab *t, int pressed) int x, y, i; style = tabgetstyle(t); - mid = theme.title[style][COLOR_MID]; + mid = theme.border[style][COLOR_MID]; if (t->row != NULL && t != t->row->col->c->selcol->selrow->seltab) { - top = theme.title[style][COLOR_LIGHT]; - bot = theme.title[style][COLOR_DARK]; + top = theme.border[style][COLOR_LIGHT]; + bot = theme.border[style][COLOR_DARK]; drawlines = 0; } else if (t->row != NULL && pressed) { - top = theme.title[style][COLOR_DARK]; - bot = theme.title[style][COLOR_LIGHT]; + top = theme.border[style][COLOR_DARK]; + bot = theme.border[style][COLOR_LIGHT]; drawlines = 1; } else { - top = theme.title[style][COLOR_LIGHT]; - bot = theme.title[style][COLOR_DARK]; + top = theme.border[style][COLOR_LIGHT]; + bot = theme.border[style][COLOR_DARK]; drawlines = 1; } @@ -2309,11 +2305,14 @@ tabdecorate(struct Tab *t, int pressed) XChangeGC(dpy, gc, GCForeground, &val); XFillRectangle(dpy, t->pixtitle, gc, 0, 0, t->w, config.titlewidth); + /* draw shadows */ + drawrectangle(t->pixtitle, 0, 0, t->w, config.titlewidth, top, bot); + /* write tab title */ if (t->name != NULL) { len = strlen(t->name); XmbTextExtents(theme.fontset, t->name, len, &dr, &box); - x = (t->w - box.width) / 2 - box.x; + x = max(0, (t->w - box.width) / 2 - box.x); y = (config.titlewidth - box.height) / 2 - box.y; for (i = 3; drawlines && i < config.titlewidth - 3; i += 3) { @@ -2335,14 +2334,10 @@ tabdecorate(struct Tab *t, int pressed) XmbDrawString(dpy, t->pixtitle, theme.fontset, gc, x, y, t->name, len); } - drawrectangle(t->pixtitle, (config.borderwidth - 4 > 0), 0, 0, t->w, config.titlewidth, top, bot); - /* draw frame background */ - if (!pressed) { - val.foreground = mid; - XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangle(dpy, t->pix, gc, 0, 0, t->winw, t->winh); - } + val.foreground = mid; + XChangeGC(dpy, gc, GCForeground, &val); + XFillRectangle(dpy, t->pix, gc, 0, 0, t->winw, t->winh); XCopyArea(dpy, t->pixtitle, t->title, gc, 0, 0, t->w, config.titlewidth, 0, 0); XCopyArea(dpy, t->pix, t->frame, gc, 0, 0, t->winw, t->winh, 0, 0); @@ -2360,21 +2355,20 @@ buttonleftdecorate(struct Row *row, int pressed) w = config.titlewidth - 9; style = (row->seltab) ? tabgetstyle(row->seltab) : UNFOCUSED; - mid = theme.title[style][COLOR_MID]; + mid = theme.border[style][COLOR_MID]; if (pressed) { - top = theme.title[style][COLOR_DARK]; - bot = theme.title[style][COLOR_LIGHT]; + top = theme.border[style][COLOR_DARK]; + bot = theme.border[style][COLOR_LIGHT]; } else { - top = theme.title[style][COLOR_LIGHT]; - bot = theme.title[style][COLOR_DARK]; + top = theme.border[style][COLOR_LIGHT]; + bot = theme.border[style][COLOR_DARK]; } /* draw background */ val.foreground = mid; XChangeGC(dpy, gc, GCForeground, &val); XFillRectangle(dpy, row->pixbl, gc, 0, 0, config.titlewidth, config.titlewidth); - - drawrectangle(row->pixbl, (config.borderwidth - 4 > 0), 0, 0, config.titlewidth, config.titlewidth, top, bot); + drawrectangle(row->pixbl, 0, 0, config.titlewidth, config.titlewidth, top, bot); if (w > 0) { x = 4; @@ -2404,13 +2398,13 @@ buttonrightdecorate(Window button, Pixmap pix, int style, int pressed) int w; w = (config.titlewidth - 11) / 2; - mid = theme.title[style][COLOR_MID]; + mid = theme.border[style][COLOR_MID]; if (pressed) { - top = theme.title[style][COLOR_DARK]; - bot = theme.title[style][COLOR_LIGHT]; + top = theme.border[style][COLOR_DARK]; + bot = theme.border[style][COLOR_LIGHT]; } else { - top = theme.title[style][COLOR_LIGHT]; - bot = theme.title[style][COLOR_DARK]; + top = theme.border[style][COLOR_LIGHT]; + bot = theme.border[style][COLOR_DARK]; } /* draw background */ @@ -2418,7 +2412,7 @@ buttonrightdecorate(Window button, Pixmap pix, int style, int pressed) XChangeGC(dpy, gc, GCForeground, &val); XFillRectangle(dpy, pix, gc, 0, 0, config.titlewidth, config.titlewidth); - drawrectangle(pix, (config.borderwidth - 4 > 0), 0, 0, config.titlewidth, config.titlewidth, top, bot); + drawrectangle(pix, 0, 0, config.titlewidth, config.titlewidth, top, bot); if (w > 0) { pts[0] = (XPoint){.x = 3, .y = config.titlewidth - 5}; @@ -2459,21 +2453,22 @@ containerdecorate(struct Container *c, struct Column *cdiv, struct Row *rdiv, in struct Row *row; struct Tab *t; struct Dialog *d; - XRectangle recs[10]; + XRectangle *recs; XGCValues val; unsigned long *decor; - int doubleshadow; int x, y, w, h; int isshaded; + int i; if (c == NULL) return; - doubleshadow = config.borderwidth - 4 > 0; decor = theme.border[containergetstyle(c)]; w = c->w - config.corner * 2; h = c->h - config.corner * 2; isshaded = containerisshaded(c); + recs = ecalloc(config.shadowthickness * 5, sizeof(*recs)); + /* (re)create pixmap */ if (c->pw != c->w || c->ph != c->h || c->pix == None) { if (c->pix != None) @@ -2490,169 +2485,153 @@ containerdecorate(struct Container *c, struct Column *cdiv, struct Row *rdiv, in if (c->b > 0) { /* top edge */ - drawrectangle(c->pix, doubleshadow, config.corner, 0, w, config.borderwidth, + drawrectangle(c->pix, config.corner, 0, w, config.borderwidth, (o == N ? decor[COLOR_DARK] : decor[COLOR_LIGHT]), (o == N ? decor[COLOR_LIGHT] : decor[COLOR_DARK])); /* bottom edge */ - drawrectangle(c->pix, doubleshadow, config.corner, c->h - config.borderwidth, w, config.borderwidth, + drawrectangle(c->pix, config.corner, c->h - config.borderwidth, w, config.borderwidth, (o == S ? decor[COLOR_DARK] : decor[COLOR_LIGHT]), (o == S ? decor[COLOR_LIGHT] : decor[COLOR_DARK])); /* left edge */ - drawrectangle(c->pix, doubleshadow, 0, config.corner, config.borderwidth, h, + drawrectangle(c->pix, 0, config.corner, config.borderwidth, h, (o == W ? decor[COLOR_DARK] : decor[COLOR_LIGHT]), (o == W ? decor[COLOR_LIGHT] : decor[COLOR_DARK])); /* left edge */ - drawrectangle(c->pix, doubleshadow, c->w - config.borderwidth, config.corner, config.borderwidth, h, + drawrectangle(c->pix, c->w - config.borderwidth, config.corner, config.borderwidth, h, (o == E ? decor[COLOR_DARK] : decor[COLOR_LIGHT]), (o == E ? decor[COLOR_LIGHT] : decor[COLOR_DARK])); if (isshaded) { /* left corner */ x = 0; - recs[0] = (XRectangle){.x = x + 0, .y = 0, .width = 1, .height = c->h - 1}; - recs[1] = (XRectangle){.x = x + 0, .y = 0, .width = config.corner - 1, .height = 1}; - recs[2] = (XRectangle){.x = x + config.borderwidth - 1, .y = c->h - config.borderwidth, .width = config.titlewidth, .height = 1}; - recs[3] = (XRectangle){.x = x + 1, .y = 0, .width = 1, .height = c->h - 2}; - recs[4] = (XRectangle){.x = x + 0, .y = 1, .width = config.corner - 2, .height = 1}; - recs[5] = (XRectangle){.x = x + config.borderwidth - 2, .y = c->h - config.borderwidth + 1, .width = config.titlewidth, .height = 1}; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = x + i, .y = 0, .width = 1, .height = c->h - 1 - i}; + recs[i * 3 + 1] = (XRectangle){.x = x + 0, .y = i, .width = config.corner - 1 - i, .height = 1}; + recs[i * 3 + 2] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = c->h - config.borderwidth + i, .width = config.titlewidth, .height = 1}; + } val.foreground = (o & W) ? decor[COLOR_DARK] : decor[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 6 : 3); - recs[0] = (XRectangle){.x = x + config.borderwidth - 1, .y = config.borderwidth - 1, .width = 1, .height = c->h - config.borderwidth * 2 + 1}; - recs[1] = (XRectangle){.x = x + config.borderwidth - 1, .y = config.borderwidth - 1, .width = config.titlewidth + 1, .height = 1}; - recs[2] = (XRectangle){.x = x + config.corner - 1, .y = 0, .width = 1, .height = config.borderwidth}; - recs[3] = (XRectangle){.x = x + config.corner - 1, .y = c->h - config.borderwidth, .width = 1, .height = config.borderwidth}; - recs[4] = (XRectangle){.x = x + 0, .y = c->h - 1, .width = config.corner, .height = 1}; - recs[5] = (XRectangle){.x = x + config.borderwidth - 2, .y = config.borderwidth - 2, .width = 1, .height = c->h - config.borderwidth * 2 + 3}; - recs[6] = (XRectangle){.x = x + config.borderwidth - 2, .y = config.borderwidth - 2, .width = config.titlewidth + 2, .height = 1}; - recs[7] = (XRectangle){.x = x + config.corner - 2, .y = 1, .width = 1, .height = config.borderwidth - 1}; - recs[8] = (XRectangle){.x = x + config.corner - 2, .y = c->h - config.borderwidth + 1, .width = 1, .height = config.borderwidth - 1}; - recs[9] = (XRectangle){.x = x + 1, .y = c->h - 2, .width = config.corner - 1, .height = 1}; + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 3); + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 5 + 0] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i, .width = 1, .height = c->h - config.borderwidth * 2 + 1 + i * 2}; + recs[i * 5 + 1] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = config.borderwidth - 1 - i, .width = config.titlewidth + 1 + i, .height = 1}; + recs[i * 5 + 2] = (XRectangle){.x = x + config.corner - 1 - i, .y = i, .width = 1, .height = config.borderwidth - i}; + recs[i * 5 + 3] = (XRectangle){.x = x + config.corner - 1 - i, .y = c->h - config.borderwidth + i, .width = 1, .height = config.borderwidth - i}; + recs[i * 5 + 4] = (XRectangle){.x = x + i, .y = c->h - 1 - i, .width = config.corner - i, .height = 1}; + } val.foreground = (o & W) ? decor[COLOR_LIGHT] : decor[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 10 : 5); + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 5); /* right corner */ x = c->w - config.corner; - recs[0] = (XRectangle){.x = x + 0, .y = 0, .width = 1, .height = config.borderwidth - 1}; - recs[1] = (XRectangle){.x = x + 0, .y = 0, .width = config.corner - 1, .height = 1}; - recs[2] = (XRectangle){.x = x + config.titlewidth, .y = config.borderwidth - 1, .width = 1, .height = c->h - config.borderwidth * 2 + 3}; - recs[3] = (XRectangle){.x = x + 0, .y = c->h - config.borderwidth, .width = config.titlewidth + 1, .height = 1}; - recs[4] = (XRectangle){.x = x + 0, .y = c->h - config.borderwidth, .width = 1, .height = config.borderwidth - 1}; - recs[5] = (XRectangle){.x = x + 1, .y = 0, .width = 1, .height = config.borderwidth - 2}; - recs[6] = (XRectangle){.x = x + 0, .y = 1, .width = config.corner - 2, .height = 1}; - recs[7] = (XRectangle){.x = x + config.titlewidth + 1, .y = config.borderwidth - 2, .width = 1, .height = c->h - config.borderwidth * 2 + 4}; - recs[8] = (XRectangle){.x = x + 1, .y = c->h - config.borderwidth + 1, .width = config.titlewidth + 1, .height = 1}; - recs[9] = (XRectangle){.x = x + 1, .y = c->h - config.borderwidth + 1, .width = 1, .height = config.borderwidth - 3}; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 5 + 0] = (XRectangle){.x = x + i, .y = 0, .width = 1, .height = config.borderwidth - 1 - i}; + recs[i * 5 + 1] = (XRectangle){.x = x + 0, .y = i, .width = config.corner - 1 - i, .height = 1}; + recs[i * 5 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = config.borderwidth - 1 - i, .width = 1, .height = c->h - config.borderwidth * 2 + 1 + i * 2}; + recs[i * 5 + 3] = (XRectangle){.x = x + i, .y = c->h - config.borderwidth + i, .width = config.titlewidth + 1, .height = 1}; + recs[i * 5 + 4] = (XRectangle){.x = x + i, .y = c->h - config.borderwidth + i, .width = 1, .height = config.borderwidth - 1 - i * 2}; + } val.foreground = (o == E) ? decor[COLOR_DARK] : decor[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 10 : 5); - recs[0] = (XRectangle){.x = x + config.corner - 1, .y = 0, .width = 1, .height = c->h}; - recs[1] = (XRectangle){.x = x + 0, .y = config.borderwidth - 1, .width = config.titlewidth, .height = 1}; - recs[2] = (XRectangle){.x = x + 0, .y = c->h - 1, .width = config.corner, .height = 1}; - recs[3] = (XRectangle){.x = x + config.corner - 2, .y = 1, .width = 1, .height = c->h - 1}; - recs[4] = (XRectangle){.x = x + 1, .y = config.borderwidth - 2, .width = config.titlewidth, .height = 1}; - recs[5] = (XRectangle){.x = x + 1, .y = c->h - 2, .width = config.corner - 1, .height = 1}; + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 5); + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = x + config.corner - 1 - i, .y = i, .width = 1, .height = c->h - i}; + recs[i * 3 + 1] = (XRectangle){.x = x + i, .y = config.borderwidth - 1 - i, .width = config.titlewidth, .height = 1}; + recs[i * 3 + 2] = (XRectangle){.x = x + i, .y = c->h - 1 - i, .width = config.corner - i, .height = 1}; + } val.foreground = (o == E) ? decor[COLOR_LIGHT] : decor[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 6 : 3); + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 3); } else { /* top left corner */ x = y = 0; - recs[0] = (XRectangle){.x = x + 0, .y = y + 0, .width = 1, .height = config.corner - 1}; - recs[1] = (XRectangle){.x = x + 0, .y = y + 0, .width = config.corner - 1, .height = 1}; - recs[2] = (XRectangle){.x = x + 1, .y = y + 0, .width = 1, .height = config.corner - 2}; - recs[3] = (XRectangle){.x = x + 0, .y = y + 1, .width = config.corner - 2, .height = 1}; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 2 + 0] = (XRectangle){.x = x + i, .y = y + 0, .width = 1, .height = config.corner - 1 - i}; + recs[i * 2 + 1] = (XRectangle){.x = x + 0, .y = y + i, .width = config.corner - 1 - i, .height = 1}; + } val.foreground = (o == NW) ? decor[COLOR_DARK] : decor[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 4: 2); - recs[0] = (XRectangle){.x = x + config.borderwidth - 1, .y = y + config.borderwidth - 1, .width = 1, .height = config.titlewidth + 1}; - recs[1] = (XRectangle){.x = x + config.borderwidth - 1, .y = y + config.borderwidth - 1, .width = config.titlewidth + 1, .height = 1}; - recs[2] = (XRectangle){.x = x + config.corner - 1, .y = y, .width = 1, .height = config.borderwidth}; - recs[3] = (XRectangle){.x = x, .y = y + config.corner - 1, .width = config.borderwidth, .height = 1}; - recs[4] = (XRectangle){.x = x + config.borderwidth - 2, .y = y + config.borderwidth - 2, .width = 1, .height = config.titlewidth + 2}; - recs[5] = (XRectangle){.x = x + config.borderwidth - 2, .y = y + config.borderwidth - 2, .width = config.titlewidth + 2, .height = 1}; - recs[6] = (XRectangle){.x = x + config.corner - 2, .y = y + 1, .width = 1, .height = config.borderwidth - 1}; - recs[7] = (XRectangle){.x = x + 1, .y = y + config.corner - 2, .width = config.borderwidth - 1, .height = 1}; + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 2); + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 4 + 0] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + config.borderwidth - 1 - i, .width = 1, .height = config.titlewidth + 1 + i}; + recs[i * 4 + 1] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + config.borderwidth - 1 - i, .width = config.titlewidth + 1 + i, .height = 1}; + recs[i * 4 + 2] = (XRectangle){.x = x + config.corner - 1 - i, .y = y + i, .width = 1, .height = config.borderwidth - i}; + recs[i * 4 + 3] = (XRectangle){.x = x + i, .y = y + config.corner - 1 - i, .width = config.borderwidth - i, .height = 1}; + } val.foreground = (o == NW) ? decor[COLOR_LIGHT] : decor[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 8 : 4); + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 4); /* bottom left corner */ x = 0; y = c->h - config.corner; - recs[0] = (XRectangle){.x = x + 0, .y = y + 0, .width = 1, .height = config.corner - 1}; - recs[1] = (XRectangle){.x = x + 0, .y = y + 0, .width = config.borderwidth - 1, .height = 1}; - recs[2] = (XRectangle){.x = x + config.borderwidth - 1, .y = y + config.titlewidth, .width = config.titlewidth, .height = 1}; - recs[3] = (XRectangle){.x = x + 1, .y = y + 0, .width = 1, .height = config.corner - 2}; - recs[4] = (XRectangle){.x = x + 0, .y = y + 1, .width = config.borderwidth - 2, .height = 1}; - recs[5] = (XRectangle){.x = x + config.borderwidth - 2, .y = y + config.titlewidth + 1, .width = config.titlewidth, .height = 1}; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = x + i, .y = y + 0, .width = 1, .height = config.corner - 1 - i}; + recs[i * 3 + 1] = (XRectangle){.x = x + 0, .y = y + i, .width = config.borderwidth - 1 - i, .height = 1}; + recs[i * 3 + 2] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + config.titlewidth + i, .width = config.titlewidth, .height = 1}; + } val.foreground = (o == SW) ? decor[COLOR_DARK] : decor[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 6 : 3); - recs[0] = (XRectangle){.x = x + config.borderwidth - 1, .y = y + 0, .width = 1, .height = config.titlewidth}; - recs[1] = (XRectangle){.x = x + 0, .y = y + config.corner - 1, .width = config.corner, .height = 1}; - recs[2] = (XRectangle){.x = x + config.corner - 1, .y = y + config.titlewidth, .width = 1, .height = config.borderwidth}; - recs[3] = (XRectangle){.x = x + config.borderwidth - 2, .y = y + 1, .width = 1, .height = config.titlewidth}; - recs[4] = (XRectangle){.x = x + 1, .y = y + config.corner - 2, .width = config.corner - 1, .height = 1}; - recs[5] = (XRectangle){.x = x + config.corner - 2, .y = y + config.titlewidth + 1, .width = 1, .height = config.borderwidth - 1}; + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 3); + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = x + config.borderwidth - 1 - i, .y = y + i, .width = 1, .height = config.titlewidth}; + recs[i * 3 + 1] = (XRectangle){.x = x + i, .y = y + config.corner - 1 - i, .width = config.corner - i, .height = 1}; + recs[i * 3 + 2] = (XRectangle){.x = x + config.corner - 1 - i, .y = y + config.titlewidth + i, .width = 1, .height = config.borderwidth - i}; + } val.foreground = (o == SW) ? decor[COLOR_LIGHT] : decor[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 6 : 3); + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 3); /* top right corner */ x = c->w - config.corner; y = 0; - recs[0] = (XRectangle){.x = x + 0, .y = y + 0, .width = 1, .height = config.borderwidth - 1}; - recs[1] = (XRectangle){.x = x + 0, .y = y + 0, .width = config.corner - 1, .height = 1}; - recs[2] = (XRectangle){.x = x + config.titlewidth, .y = y + config.borderwidth - 1, .width = 1, .height = config.titlewidth}; - recs[3] = (XRectangle){.x = x + 1, .y = y + 0, .width = 1, .height = config.borderwidth - 2}; - recs[4] = (XRectangle){.x = x + 0, .y = y + 1, .width = config.corner - 2, .height = 1}; - recs[5] = (XRectangle){.x = x + config.titlewidth + 1, .y = y + config.borderwidth - 2, .width = 1, .height = config.titlewidth}; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = x + i, .y = y + 0, .width = 1, .height = config.borderwidth - 1 - i}; + recs[i * 3 + 1] = (XRectangle){.x = x + 0, .y = y + i, .width = config.corner - 1 - i, .height = 1}; + recs[i * 3 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = y + config.borderwidth - 1 - i, .width = 1, .height = config.titlewidth}; + } val.foreground = (o == NE) ? decor[COLOR_DARK] : decor[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 6 : 3); - recs[0] = (XRectangle){.x = x + config.corner - 1, .y = y + 0, .width = 1, .height = config.corner}; - recs[1] = (XRectangle){.x = x + 0, .y = y + config.borderwidth - 1, .width = config.titlewidth, .height = 1}; - recs[2] = (XRectangle){.x = x + config.titlewidth, .y = y + config.corner - 1, .width = config.borderwidth, .height = 1}; - recs[3] = (XRectangle){.x = x + config.corner - 2, .y = y + 1, .width = 1, .height = config.corner}; - recs[4] = (XRectangle){.x = x + 1, .y = y + config.borderwidth - 2, .width = config.titlewidth, .height = 1}; - recs[5] = (XRectangle){.x = x + config.titlewidth + 1, .y = y + config.corner - 2, .width = config.borderwidth - 1, .height = 1}; + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 3); + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = x + config.corner - 1 - i, .y = y + i, .width = 1, .height = config.corner}; + recs[i * 3 + 1] = (XRectangle){.x = x + i, .y = y + config.borderwidth - 1 - i, .width = config.titlewidth, .height = 1}; + recs[i * 3 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = y + config.corner - 1 - i, .width = config.borderwidth - i, .height = 1}; + } val.foreground = (o == NE) ? decor[COLOR_LIGHT] : decor[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 6 : 3); + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 3); /* bottom right corner */ x = c->w - config.corner; y = c->h - config.corner; - recs[0] = (XRectangle){.x = x + 0, .y = y + config.titlewidth, .width = 1, .height = config.borderwidth - 1}; - recs[1] = (XRectangle){.x = x + config.titlewidth, .y = y + 0, .width = config.borderwidth - 1, .height = 1}; - recs[2] = (XRectangle){.x = x + config.titlewidth, .y = y + 0, .width = 1, .height = config.titlewidth + 1}; - recs[3] = (XRectangle){.x = x + 0, .y = y + config.titlewidth, .width = config.titlewidth + 1, .height = 1}; - recs[4] = (XRectangle){.x = x + 1, .y = y + config.titlewidth + 1, .width = 1, .height = config.borderwidth - 3}; - recs[5] = (XRectangle){.x = x + config.titlewidth + 1, .y = y + 1, .width = config.borderwidth - 3, .height = 1}; - recs[6] = (XRectangle){.x = x + config.titlewidth + 1, .y = y + 1, .width = 1, .height = config.titlewidth + 1}; - recs[7] = (XRectangle){.x = x + 1, .y = y + config.titlewidth + 1, .width = config.titlewidth + 1, .height = 1}; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 4 + 0] = (XRectangle){.x = x + i, .y = y + config.titlewidth + i, .width = 1, .height = config.borderwidth - 1 - i * 2}; + recs[i * 4 + 1] = (XRectangle){.x = x + config.titlewidth + i, .y = y + i, .width = config.borderwidth - 1 - i * 2, .height = 1}; + recs[i * 4 + 2] = (XRectangle){.x = x + config.titlewidth + i, .y = y + i, .width = 1, .height = config.titlewidth + 1}; + recs[i * 4 + 3] = (XRectangle){.x = x + i, .y = y + config.titlewidth + i, .width = config.titlewidth + 1, .height = 1}; + } val.foreground = (o == SE) ? decor[COLOR_DARK] : decor[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 8 : 4); - recs[0] = (XRectangle){.x = x + config.corner - 1, .y = y + 0, .width = 1, .height = config.corner}; - recs[1] = (XRectangle){.x = x + 0, .y = y + config.corner - 1, .width = config.corner, .height = 1}; - recs[2] = (XRectangle){.x = x + config.corner - 2, .y = y + 1, .width = 1, .height = config.corner - 1}; - recs[3] = (XRectangle){.x = x + 1, .y = y + config.corner - 2, .width = config.corner - 1, .height = 1}; + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 4); + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 2 + 0] = (XRectangle){.x = x + config.corner - 1 - i, .y = y + i, .width = 1, .height = config.corner - i}; + recs[i * 2 + 1] = (XRectangle){.x = x + i, .y = y + config.corner - 1 - i, .width = config.corner - i, .height = 1}; + } val.foreground = (o == SE) ? decor[COLOR_LIGHT] : decor[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, c->pix, gc, recs, doubleshadow ? 4 : 2); + XFillRectangles(dpy, c->pix, gc, recs, config.shadowthickness * 2); } } for (col = c->cols; col != NULL; col = col->next) { /* draw column division */ if (col->next != NULL) { - drawrectangle(c->pix, doubleshadow, col->x + col->w, c->b, config.divwidth, c->h - 2 * c->b, + drawrectangle(c->pix, col->x + col->w, c->b, config.divwidth, c->h - 2 * c->b, (col == cdiv ? decor[COLOR_DARK] : decor[COLOR_LIGHT]), (col == cdiv ? decor[COLOR_LIGHT] : decor[COLOR_DARK])); } @@ -2660,7 +2639,7 @@ containerdecorate(struct Container *c, struct Column *cdiv, struct Row *rdiv, in for (row = col->rows; row != NULL; row = row->next) { /* draw row division */ if (!isshaded && col->maxrow == NULL && row->next != NULL) { - drawrectangle(c->pix, doubleshadow, col->x, row->y + row->h, col->w, config.divwidth, + drawrectangle(c->pix, col->x, row->y + row->h, col->w, config.divwidth, (row == rdiv ? decor[COLOR_DARK] : decor[COLOR_LIGHT]), (row == rdiv ? decor[COLOR_LIGHT] : decor[COLOR_DARK])); } @@ -2697,6 +2676,7 @@ containerdecorate(struct Container *c, struct Column *cdiv, struct Row *rdiv, in } XCopyArea(dpy, c->pix, c->frame, gc, 0, 0, c->w, c->h, 0, 0); + free(recs); } /* decorate prompt frame */ @@ -2704,9 +2684,9 @@ static void promptdecorate(struct Prompt *prompt, int w, int h) { XGCValues val; - XRectangle recs[10]; + XRectangle *recs; int partw, parth; - int doubleshadow; + int i; if (prompt->pw == w && prompt->ph == h && prompt->pix != None) goto done; @@ -2717,10 +2697,9 @@ promptdecorate(struct Prompt *prompt, int w, int h) prompt->pix = XCreatePixmap(dpy, prompt->frame, w, h, depth); prompt->pw = w; prompt->ph = h; - + recs = ecalloc(config.shadowthickness * 3, sizeof(*recs)); partw = w - 2 * config.borderwidth; parth = h - 2 * config.borderwidth; - doubleshadow = (config.borderwidth - 4 > 0); /* draw background */ val.foreground = theme.prompt[COLOR_MID]; @@ -2728,61 +2707,26 @@ promptdecorate(struct Prompt *prompt, int w, int h) XFillRectangle(dpy, prompt->pix, gc, 0, 0, w, h); /* draw light shadow */ - recs[0] = (XRectangle){.x = 0, .y = 0, .width = 1, .height = h - 1}; - recs[1] = (XRectangle){.x = 0, .y = 0, .width = config.borderwidth - 1, .height = 1}; - recs[2] = (XRectangle){ - .x = w - config.borderwidth, - .y = 0, - .width = 1, - .height = parth + config.borderwidth, - }; - recs[3] = (XRectangle){ - .x = config.borderwidth - 1, - .y = h - config.borderwidth, - .width = partw + 2, - .height = 1 - }; - recs[4] = (XRectangle){.x = 1, .y = 1, .width = 1, .height = h - 2}; - recs[5] = (XRectangle){.x = 0, .y = 1, .width = config.borderwidth - 2, .height = 1}; - recs[6] = (XRectangle){ - .x = w - config.borderwidth + 1, - .y = 0, - .width = 1, - .height = parth + config.borderwidth + 1, - }; - recs[7] = (XRectangle){ - .x = config.borderwidth - 2, - .y = h - config.borderwidth + 1, - .width = partw + 4, - .height = 1 - }; - recs[8] = (XRectangle){.x = w - config.borderwidth, .y = 0, .width = config.borderwidth - 1, .height = 1}; - recs[9] = (XRectangle){.x = w - config.borderwidth, .y = 1, .width = config.borderwidth - 2, .height = 1}; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = i, .y = i, .width = 1, .height = h - 1 - i}; + recs[i * 3 + 1] = (XRectangle){.x = w - config.borderwidth + i, .y = 0, .width = 1, .height = parth + config.borderwidth + i}; + recs[i * 3 + 2] = (XRectangle){.x = config.borderwidth - 1 - i, .y = h - config.borderwidth + i, .width = partw + 2 + i * 2, .height = 1}; + } val.foreground = theme.prompt[COLOR_LIGHT]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, prompt->pix, gc, recs, doubleshadow ? 10 : 5); + XFillRectangles(dpy, prompt->pix, gc, recs, config.shadowthickness * 3); /* draw dark shadow */ - recs[0] = (XRectangle){.x = w - 1, .y = 0, .width = 1, .height = h}; - recs[1] = (XRectangle){.x = 0, .y = h - 1, .width = w, .height = 1}; - recs[2] = (XRectangle){ - .x = config.borderwidth - 1, - .y = 0, - .width = 1, - .height = parth + config.borderwidth, - }; - recs[3] = (XRectangle){.x = w - 2, .y = 1, .width = 1, .height = h - 2}; - recs[4] = (XRectangle){.x = 1, .y = h - 2, .width = w - 2, .height = 1}; - recs[5] = (XRectangle){ - .x = config.borderwidth - 2, - .y = 1, - .width = 1, - .height = parth + config.borderwidth, - }; + for (i = 0; i < config.shadowthickness; i++) { + recs[i * 3 + 0] = (XRectangle){.x = w - 1 - i, .y = i, .width = 1, .height = h - i * 2}; + recs[i * 3 + 1] = (XRectangle){.x = i, .y = h - 1 - i, .width = w - i * 2, .height = 1}; + recs[i * 3 + 2] = (XRectangle){.x = config.borderwidth - 1 - i, .y = i, .width = 1, .height = parth + config.borderwidth}; + } val.foreground = theme.prompt[COLOR_DARK]; XChangeGC(dpy, gc, GCForeground, &val); - XFillRectangles(dpy, prompt->pix, gc, recs, doubleshadow ? 6 : 3); + XFillRectangles(dpy, prompt->pix, gc, recs, config.shadowthickness * 3); + free(recs); done: XCopyArea(dpy, prompt->pix, prompt->frame, gc, 0, 0, w, h, 0, 0); } @@ -2800,7 +2744,7 @@ notifdecorate(struct Notification *n) n->pw = n->w; n->ph = n->h; - drawborders(n->pix, (config.borderwidth - 4 > 0), n->w, n->h, theme.notif); + drawborders(n->pix, n->w, n->h, theme.notif); XCopyArea(dpy, n->pix, n->frame, gc, 0, 0, n->w, n->h, 0, 0); } @@ -2813,11 +2757,9 @@ menudecorate(struct Menu *menu, int titlepressed) XGCValues val; size_t len; unsigned long top, bot; - int doubleshadow; int tw, th; int x, y; - doubleshadow = config.borderwidth - 4 > 0; if (menu->pw != menu->w || menu->ph != menu->h || menu->pix == None) { if (menu->pix != None) XFreePixmap(dpy, menu->pix); @@ -2836,18 +2778,18 @@ menudecorate(struct Menu *menu, int titlepressed) menu->th = th; if (titlepressed) { - top = theme.title[FOCUSED][COLOR_DARK]; - bot = theme.title[FOCUSED][COLOR_LIGHT]; + top = theme.border[FOCUSED][COLOR_DARK]; + bot = theme.border[FOCUSED][COLOR_LIGHT]; } else { - top = theme.title[FOCUSED][COLOR_LIGHT]; - bot = theme.title[FOCUSED][COLOR_DARK]; + top = theme.border[FOCUSED][COLOR_LIGHT]; + bot = theme.border[FOCUSED][COLOR_DARK]; } val.fill_style = FillSolid; - val.foreground = theme.title[FOCUSED][COLOR_MID]; + val.foreground = theme.border[FOCUSED][COLOR_MID]; XChangeGC(dpy, gc, GCFillStyle | GCForeground, &val); XFillRectangle(dpy, menu->pixtitlebar, gc, 0, 0, menu->tw, menu->th); - drawborders(menu->pix, doubleshadow, menu->w, menu->h, theme.border[FOCUSED]); - drawrectangle(menu->pixtitlebar, doubleshadow, 0, 0, menu->tw, config.titlewidth, top, bot); + drawborders(menu->pix, menu->w, menu->h, theme.border[FOCUSED]); + drawrectangle(menu->pixtitlebar, 0, 0, menu->tw, config.titlewidth, top, bot); /* write menu title */ if (menu->name != NULL) { len = strlen(menu->name); @@ -4180,6 +4122,50 @@ dialognew(Window win, int maxw, int maxh, int ignoreunmap) return d; } +/* create new splash screen */ +static struct Splash * +splashnew(Window win, int w, int h) +{ + struct Splash *splash; + + splash = emalloc(sizeof(*splash)); + *splash = (struct Splash){ + .win = win, + .w = w, + .h = h, + }; + XReparentWindow(dpy, win, root, 0, 0); + return splash; +} + +/* center splash screen on monitor and raise it above other windows */ +static void +splashplace(struct Splash *splash) +{ + Window wins[2]; + fitmonitor(wm.selmon, &splash->x, &splash->y, &splash->w, &splash->h, 0.5); + splash->x = wm.selmon->wx + (wm.selmon->ww - splash->w) / 2; + splash->y = wm.selmon->wy + (wm.selmon->wh - splash->h) / 2; + wins[1] = splash->win; + wins[0] = wm.layerwins[LAYER_SPLASH]; + XMoveWindow(dpy, splash->win, splash->x, splash->y); + XRestackWindows(dpy, wins, 2); +} + +/* delete splash screen window */ +static void +splashdel(struct Splash *splash) +{ + if (splash->next != NULL) + splash->next->prev = splash->prev; + if (splash->prev != NULL) + splash->prev->next = splash->next; + else + wm.splashlist = splash->next; + icccmdeletestate(splash->win); + free(splash); +} + /* check if monitor geometry is unique */ static int monisuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) @@ -4255,6 +4241,7 @@ monupdate(void) struct Monitor *tmp; struct Container *c, *focus; struct Column *col; + struct Splash *splash; struct Row *row; struct Tab *t; struct Menu *menu; @@ -4334,6 +4321,8 @@ monupdate(void) } } } + for (splash = wm.splashlist; splash != NULL; splash = splash->next) + splashplace(splash); if (focus != NULL) /* if a contained changed desktop, focus it */ tabfocus(focus->selcol->selrow->seltab, 1); @@ -4646,7 +4635,7 @@ dockdecorate(void) XChangeGC(dpy, gc, GCFillStyle | GCForeground, &val); XFillRectangle(dpy, dock.pix, gc, 0, 0, dock.w, dock.h); - drawrectangle(dock.pix, 1, 0, 0, dock.w, dock.h, theme.dock[COLOR_LIGHT], theme.dock[COLOR_DARK]); + drawrectangle(dock.pix, 0, 0, dock.w, dock.h, theme.dock[COLOR_LIGHT], theme.dock[COLOR_DARK]); XCopyArea(dpy, dock.pix, dock.win, gc, 0, 0, dock.w, dock.h, 0, 0); } @@ -4873,6 +4862,19 @@ decorate(struct Winres *res) } } +/* add splash screen and center it on the screen */ +static void +managesplash(struct Splash *splash) +{ + if (wm.splashlist != NULL) + wm.splashlist->prev = splash; + splash->next = wm.splashlist; + wm.splashlist = splash; + icccmwmstate(splash->win, NormalState); + splashplace(splash); + XMapWindow(dpy, splash->win); +} + /* add dialog window into tab */ static void managedialog(struct Tab *t, struct Dialog *d) @@ -5051,6 +5053,7 @@ manage(Window win, XWindowAttributes *wa, int ignoreunmap) struct Tab *t; struct Container *c; struct Dialog *d; + struct Splash *splash; struct Menu *menu; Window leader; int userplaced; @@ -5076,6 +5079,11 @@ manage(Window win, XWindowAttributes *wa, int ignoreunmap) preparewin(win); manageprompt(win, wa->width, wa->height); break; + case TYPE_SPLASH: + preparewin(win); + splash = splashnew(win, wa->width, wa->height); + managesplash(splash); + break; case TYPE_DIALOG: preparewin(win); d = dialognew(win, wa->width, wa->height, ignoreunmap); @@ -6124,6 +6132,9 @@ xeventdestroynotify(XEvent *e) res = getwin(ev->window); if (res.dapp && ev->window == res.dapp->win) { dockappdel(res.dapp); + } else if (res.splash != NULL) { + splashdel(res.splash); + return; } else if (res.n && ev->window == res.n->win) { notifdel(res.n); return; @@ -6227,6 +6238,9 @@ xeventunmapnotify(XEvent *e) if (res.bar != NULL && ev->window == res.bar->win) { bardel(res.bar); return; + } else if (res.splash != NULL) { + splashdel(res.splash); + return; } else if (res.n && ev->window == res.n->win) { notifdel(res.n); return;