From f48d1b5e869bc876c5192fd6b557ad5ddc064327 Mon Sep 17 00:00:00 2001 From: augety Date: Sun, 25 Aug 2024 15:05:11 +0800 Subject: [PATCH] feat(patches): add tag-preview patche --- config.def.h | 4 +- config.mk | 5 +- dwm.c | 141 +++++++++++++- patches/dwm-tag-preview-6.3.diff | 315 +++++++++++++++++++++++++++++++ 4 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 patches/dwm-tag-preview-6.3.diff diff --git a/config.def.h b/config.def.h index a3a9e02..d442782 100644 --- a/config.def.h +++ b/config.def.h @@ -3,6 +3,8 @@ /* appearance */ static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int snap = 32; /* snap pixel */ +static const int scalepreview = 4; /* preview scaling (display w and h / scalepreview) */ +static const int previewbar = 1; /* show the bar in the preview window */ static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ static const unsigned int systrayspacing = 2; /* systray spacing */ @@ -61,7 +63,7 @@ static const Layout layouts[] = { { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ - { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + { MODKEY|ControlMask|ShiftMask, KEY, previewtag, {.ui = TAG } }, \ /* helper for spawning shell commands in the pre dwm-5.0 fashion */ #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } diff --git a/config.mk b/config.mk index 9847d55..efb7af2 100644 --- a/config.mk +++ b/config.mk @@ -21,9 +21,12 @@ FREETYPEINC = /usr/include/freetype2 #FREETYPEINC = ${X11INC}/freetype2 #MANPREFIX = ${PREFIX}/man +# Imlib2 (tag previews) +IMLIB2LIBS = -lImlib2 + # includes and libs INCS = -I${X11INC} -I${FREETYPEINC} -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${IMLIB2LIBS} # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} diff --git a/dwm.c b/dwm.c index 2e3421b..30cdf0d 100644 --- a/dwm.c +++ b/dwm.c @@ -41,6 +41,7 @@ #include #endif /* XINERAMA */ #include +#include #include "drw.h" #include "util.h" @@ -130,6 +131,9 @@ typedef struct { typedef struct Pertag Pertag; struct Monitor { + int previewshow; + Window tagwin; + Pixmap *tagmap; char ltsymbol[16]; float mfact; int nmaster; @@ -287,6 +291,10 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); static void zoom(const Arg *arg); +static void showtagpreview(unsigned int i); +static void takepreview(void); +static void previewtag(const Arg *arg); + /* variables */ static Systray *systray = NULL; static const char autostartblocksh[] = "autostart_blocking.sh"; @@ -515,6 +523,11 @@ buttonpress(XEvent *e) if (i < LENGTH(tags)) { click = ClkTagBar; arg.ui = 1 << i; + /* hide preview if we click the bar */ + if (selmon->previewshow) { + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + } } else if (ev->x < x + TEXTW(selmon->ltsymbol)) click = ClkLtSymbol; /* 2px right padding */ @@ -598,6 +611,7 @@ void cleanupmon(Monitor *mon) { Monitor *m; + size_t i; if (mon == mons) mons = mons->next; @@ -605,8 +619,14 @@ cleanupmon(Monitor *mon) for (m = mons; m && m->next != mon; m = m->next); m->next = mon->next; } + for (i = 0; i < LENGTH(tags); i++) + if (mon->tagmap[i]) + XFreePixmap(dpy, mon->tagmap[i]); + free(mon->tagmap); XUnmapWindow(dpy, mon->barwin); XDestroyWindow(dpy, mon->barwin); + XUnmapWindow(dpy, mon->tagwin); + XDestroyWindow(dpy, mon->tagwin); free(mon); } @@ -791,6 +811,7 @@ createmon(void) m->topbar = topbar; m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; + m->tagmap = ecalloc(LENGTH(tags), sizeof(Pixmap)); strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); m->pertag = ecalloc(1, sizeof(Pertag)); m->pertag->curtag = m->pertag->prevtag = 1; @@ -1449,11 +1470,41 @@ monocle(Monitor *m) void motionnotify(XEvent *e) { - int x, i; + // int x, i; static Monitor *mon = NULL; Client *c; Monitor *m; XMotionEvent *ev = &e->xmotion; + unsigned int i, x; + + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + /* FIXME when hovering the mouse over the tags and we view the tag, + * the preview window get's in the preview shot */ + + if (i < LENGTH(tags)) { + if (selmon->previewshow != (i + 1) + && !(selmon->tagset[selmon->seltags] & 1 << i)) { + selmon->previewshow = i + 1; + showtagpreview(i); + } else if (selmon->tagset[selmon->seltags] & 1 << i) { + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + } + } else if (selmon->previewshow) { + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + } + } else if (ev->window == selmon->tagwin) { + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + } else if (selmon->previewshow) { + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + } if (ev->window != selmon->barwin) { if (selmon->hov) { @@ -2108,6 +2159,82 @@ setmfact(const Arg *arg) arrange(selmon); } +void +showtagpreview(unsigned int i) +{ + if (!selmon->previewshow || !selmon->tagmap[i]) { + XUnmapWindow(dpy, selmon->tagwin); + return; + } + + XSetWindowBackgroundPixmap(dpy, selmon->tagwin, selmon->tagmap[i]); + XCopyArea(dpy, selmon->tagmap[i], selmon->tagwin, drw->gc, 0, 0, + selmon->mw / scalepreview, selmon->mh / scalepreview, + 0, 0); + XSync(dpy, False); + XMapRaised(dpy, selmon->tagwin); +} + +void +takepreview(void) +{ + Client *c; + Imlib_Image image; + unsigned int occ = 0, i; + + for (c = selmon->clients; c; c = c->next) + occ |= c->tags; + //occ |= c->tags == 255 ? 0 : c->tags; /* hide vacants */ + + for (i = 0; i < LENGTH(tags); i++) { + /* searching for tags that are occupied && selected */ + if (!(occ & 1 << i) || !(selmon->tagset[selmon->seltags] & 1 << i)) + continue; + + if (selmon->tagmap[i]) { /* tagmap exist, clean it */ + XFreePixmap(dpy, selmon->tagmap[i]); + selmon->tagmap[i] = 0; + } + + /* try to unmap the window so it doesn't show the preview on the preview */ + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + XSync(dpy, False); + + if (!(image = imlib_create_image(sw, sh))) { + fprintf(stderr, "dwm: imlib: failed to create image, skipping."); + continue; + } + imlib_context_set_image(image); + imlib_context_set_display(dpy); + /* uncomment if using alpha patch */ + //imlib_image_set_has_alpha(1); + //imlib_context_set_blend(0); + //imlib_context_set_visual(visual); + imlib_context_set_visual(DefaultVisual(dpy, screen)); + imlib_context_set_drawable(root); + + if (previewbar) + imlib_copy_drawable_to_image(0, selmon->wx, selmon->wy, selmon->ww, selmon->wh, 0, 0, 1); + else + imlib_copy_drawable_to_image(0, selmon->mx, selmon->my, selmon->mw ,selmon->mh, 0, 0, 1); + selmon->tagmap[i] = XCreatePixmap(dpy, selmon->tagwin, selmon->mw / scalepreview, selmon->mh / scalepreview, DefaultDepth(dpy, screen)); + imlib_context_set_drawable(selmon->tagmap[i]); + imlib_render_image_part_on_drawable_at_size(0, 0, selmon->mw, selmon->mh, 0, 0, selmon->mw / scalepreview, selmon->mh / scalepreview); + imlib_free_image(); + } +} + +void +previewtag(const Arg *arg) +{ + if (selmon->previewshow != (arg->ui + 1)) + selmon->previewshow = arg->ui + 1; + else + selmon->previewshow = 0; + showtagpreview(arg->ui); +} + void setup(void) { @@ -2419,6 +2546,7 @@ toggleview(const Arg *arg) int i; if (newtagset) { + takepreview(); selmon->tagset[selmon->seltags] = newtagset; if (newtagset == ~0) { @@ -2532,10 +2660,18 @@ updatebars(void) XSetWindowAttributes wa = { .override_redirect = True, .background_pixmap = ParentRelative, - .event_mask = ButtonPressMask|ExposureMask + .event_mask = ButtonPressMask|ExposureMask|PointerMotionMask }; + XClassHint ch = {"dwm", "dwm"}; for (m = mons; m; m = m->next) { + if (!m->tagwin) { + m->tagwin = XCreateWindow(dpy, root, m->wx, m->by + bh, m->mw / scalepreview, + m->mh / scalepreview, 0, DefaultDepth(dpy, screen), CopyFromParent, + DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->tagwin, cursor[CurNormal]->cursor); + XUnmapWindow(dpy, m->tagwin); + } if (m->barwin) continue; w = m->ww; @@ -2893,6 +3029,7 @@ view(const Arg *arg) if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; + takepreview(); selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) { selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; diff --git a/patches/dwm-tag-preview-6.3.diff b/patches/dwm-tag-preview-6.3.diff new file mode 100644 index 0000000..dbee35d --- /dev/null +++ b/patches/dwm-tag-preview-6.3.diff @@ -0,0 +1,315 @@ +From 841ad7d5767f945ee9da6c5afc8cff98ca2f8231 Mon Sep 17 00:00:00 2001 +From: explosion-mental +Date: Thu, 1 Sep 2022 16:21:58 -0500 +Subject: [PATCH] [PATCH] tag previews: free() tagmap and add previewtag func + +Allows you to see the contents of an already viewed tag. So a more +accurate description would be to re-view a tag. + +Allows you to see the contents of an already viewed tag. So a more +accurate description would be to re-view a tag. + +Compatibility with the alpha patch (replacing DefaultDepth() and +DefaultVisual() with depth and visual + window masks) and hide vacants can be +achieved, I left some lines to uncomment. + +added: +* more compact structure, more probable to patch on top of other patches + or easier to patch manually (like not moving the Monitor struct..) +* create the window preview in updatebars() +* renamed switchtag() -> takepreview(), makes more sense since it's + "taking" the preview (basically a screenshot). +* option previewbar, whether to show the bar in the preview or not. +* previewtag which takes a tag (unsigned int from 0 to the last tag) and + previews it. This allows to preview tags without using the + cursor/mouse (which avoids a recursive previews preview). + adding it to the TAGKEYS macro makes more sense so I've added it + replacing (keybinding wise, not functionality) toggletag. +``` +\#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ +-> { MODKEY|ControlMask|ShiftMask, KEY, previewtag, {.ui = TAG } }, +``` +--- + config.def.h | 4 +- + config.mk | 5 +- + dwm.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 145 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..eb70348 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -3,6 +3,8 @@ + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ ++static const int scalepreview = 4; /* preview scaling (display w and h / scalepreview) */ ++static const int previewbar = 1; /* show the bar in the preview window */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; +@@ -50,7 +52,7 @@ static const Layout layouts[] = { + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ +- { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, ++ { MODKEY|ControlMask|ShiftMask, KEY, previewtag, {.ui = TAG } }, \ + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +diff --git a/config.mk b/config.mk +index b6eb7e0..6f5129e 100644 +--- a/config.mk ++++ b/config.mk +@@ -20,9 +20,12 @@ FREETYPEINC = /usr/include/freetype2 + # OpenBSD (uncomment) + #FREETYPEINC = ${X11INC}/freetype2 + ++# Imlib2 (tag previews) ++IMLIB2LIBS = -lImlib2 ++ + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${IMLIB2LIBS} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/dwm.c b/dwm.c +index a96f33c..0c0ba12 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -40,6 +40,7 @@ + #include + #endif /* XINERAMA */ + #include ++#include + + #include "drw.h" + #include "util.h" +@@ -112,6 +113,9 @@ typedef struct { + } Layout; + + struct Monitor { ++ int previewshow; ++ Window tagwin; ++ Pixmap *tagmap; + char ltsymbol[16]; + float mfact; + int nmaster; +@@ -235,6 +239,10 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); + static int xerrorstart(Display *dpy, XErrorEvent *ee); + static void zoom(const Arg *arg); + ++static void showtagpreview(unsigned int i); ++static void takepreview(void); ++static void previewtag(const Arg *arg); ++ + /* variables */ + static const char broken[] = "broken"; + static char stext[256]; +@@ -438,6 +446,11 @@ buttonpress(XEvent *e) + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; ++ /* hide preview if we click the bar */ ++ if (selmon->previewshow) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } + } else if (ev->x < x + blw) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) +@@ -498,6 +511,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ size_t i; + + if (mon == mons) + mons = mons->next; +@@ -505,8 +519,14 @@ cleanupmon(Monitor *mon) + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } ++ for (i = 0; i < LENGTH(tags); i++) ++ if (mon->tagmap[i]) ++ XFreePixmap(dpy, mon->tagmap[i]); ++ free(mon->tagmap); + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ XUnmapWindow(dpy, mon->tagwin); ++ XDestroyWindow(dpy, mon->tagwin); + free(mon); + } + +@@ -641,6 +661,7 @@ createmon(void) + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; ++ m->tagmap = ecalloc(LENGTH(tags), sizeof(Pixmap)); + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; + } +@@ -1125,6 +1146,36 @@ motionnotify(XEvent *e) + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; ++ unsigned int i, x; ++ ++ if (ev->window == selmon->barwin) { ++ i = x = 0; ++ do ++ x += TEXTW(tags[i]); ++ while (ev->x >= x && ++i < LENGTH(tags)); ++ /* FIXME when hovering the mouse over the tags and we view the tag, ++ * the preview window get's in the preview shot */ ++ ++ if (i < LENGTH(tags)) { ++ if (selmon->previewshow != (i + 1) ++ && !(selmon->tagset[selmon->seltags] & 1 << i)) { ++ selmon->previewshow = i + 1; ++ showtagpreview(i); ++ } else if (selmon->tagset[selmon->seltags] & 1 << i) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } ++ } else if (selmon->previewshow) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } ++ } else if (ev->window == selmon->tagwin) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } else if (selmon->previewshow) { ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ } + + if (ev->window != root) + return; +@@ -1530,6 +1581,82 @@ setmfact(const Arg *arg) + arrange(selmon); + } + ++void ++showtagpreview(unsigned int i) ++{ ++ if (!selmon->previewshow || !selmon->tagmap[i]) { ++ XUnmapWindow(dpy, selmon->tagwin); ++ return; ++ } ++ ++ XSetWindowBackgroundPixmap(dpy, selmon->tagwin, selmon->tagmap[i]); ++ XCopyArea(dpy, selmon->tagmap[i], selmon->tagwin, drw->gc, 0, 0, ++ selmon->mw / scalepreview, selmon->mh / scalepreview, ++ 0, 0); ++ XSync(dpy, False); ++ XMapRaised(dpy, selmon->tagwin); ++} ++ ++void ++takepreview(void) ++{ ++ Client *c; ++ Imlib_Image image; ++ unsigned int occ = 0, i; ++ ++ for (c = selmon->clients; c; c = c->next) ++ occ |= c->tags; ++ //occ |= c->tags == 255 ? 0 : c->tags; /* hide vacants */ ++ ++ for (i = 0; i < LENGTH(tags); i++) { ++ /* searching for tags that are occupied && selected */ ++ if (!(occ & 1 << i) || !(selmon->tagset[selmon->seltags] & 1 << i)) ++ continue; ++ ++ if (selmon->tagmap[i]) { /* tagmap exist, clean it */ ++ XFreePixmap(dpy, selmon->tagmap[i]); ++ selmon->tagmap[i] = 0; ++ } ++ ++ /* try to unmap the window so it doesn't show the preview on the preview */ ++ selmon->previewshow = 0; ++ XUnmapWindow(dpy, selmon->tagwin); ++ XSync(dpy, False); ++ ++ if (!(image = imlib_create_image(sw, sh))) { ++ fprintf(stderr, "dwm: imlib: failed to create image, skipping."); ++ continue; ++ } ++ imlib_context_set_image(image); ++ imlib_context_set_display(dpy); ++ /* uncomment if using alpha patch */ ++ //imlib_image_set_has_alpha(1); ++ //imlib_context_set_blend(0); ++ //imlib_context_set_visual(visual); ++ imlib_context_set_visual(DefaultVisual(dpy, screen)); ++ imlib_context_set_drawable(root); ++ ++ if (previewbar) ++ imlib_copy_drawable_to_image(0, selmon->wx, selmon->wy, selmon->ww, selmon->wh, 0, 0, 1); ++ else ++ imlib_copy_drawable_to_image(0, selmon->mx, selmon->my, selmon->mw ,selmon->mh, 0, 0, 1); ++ selmon->tagmap[i] = XCreatePixmap(dpy, selmon->tagwin, selmon->mw / scalepreview, selmon->mh / scalepreview, DefaultDepth(dpy, screen)); ++ imlib_context_set_drawable(selmon->tagmap[i]); ++ imlib_render_image_part_on_drawable_at_size(0, 0, selmon->mw, selmon->mh, 0, 0, selmon->mw / scalepreview, selmon->mh / scalepreview); ++ imlib_free_image(); ++ } ++} ++ ++void ++previewtag(const Arg *arg) ++{ ++ if (selmon->previewshow != (arg->ui + 1)) ++ selmon->previewshow = arg->ui + 1; ++ else ++ selmon->previewshow = 0; ++ showtagpreview(arg->ui); ++} ++ + void + setup(void) + { +@@ -1746,6 +1873,7 @@ toggleview(const Arg *arg) + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { ++ takepreview(); + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); +@@ -1811,10 +1939,18 @@ updatebars(void) + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, +- .event_mask = ButtonPressMask|ExposureMask ++ .event_mask = ButtonPressMask|ExposureMask|PointerMotionMask + }; ++ + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { ++ if (!m->tagwin) { ++ m->tagwin = XCreateWindow(dpy, root, m->wx, m->by + bh, m->mw / scalepreview, ++ m->mh / scalepreview, 0, DefaultDepth(dpy, screen), CopyFromParent, ++ DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); ++ XDefineCursor(dpy, m->tagwin, cursor[CurNormal]->cursor); ++ XUnmapWindow(dpy, m->tagwin); ++ } + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), +@@ -2043,6 +2179,7 @@ view(const Arg *arg) + { + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; ++ takepreview(); + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; +-- +2.37.3 +