[dev][dwm] Floating window center position fix
I have a rule in config.h for MPlayer to always float. The bug was observed when I played a 720p video whose window was 1280 pixels so it filled the width of my laptop screen. The MPlayer window was supposed to be centered, but a border line on the left was clearly visible. For further confirmation, I set borderpx to 20. My observation was obviously right. The patch is appended at the end. Another thing I've observed but didn't care to fix when playing the video: If the MPlayer window is always created in tag 4 (as per a rule), but I run the mplayer command in xterm in tag 1, the visibility of the MPlayer window border depends on some timing: 1) If I switch from tag 1 to tag 4 _after_ the MPlayer window is created, the border is visible. 2) If I switch from tag 1 to tag 4 _before_ the MPlayer window is created, the border is invisible. It seems for dwm the border exists, just not drawn. If I switch to another tag and back, or do a focusstack() using the default mod1+j combo or pretty about anything, the border will then be drawn. Relevant entries in my test config.h are: static const unsigned int borderpx = 20; static const Rule rules[] = { { "MPlayer", NULL, NULL, 1 << 3, True, -1 }, }; And following is my patch for the center position bug: Patch for dwm 5.9 diff -up a/dwm.c b/dwm.c --- a/dwm.c 1970-01-01 00:00:00.0 + +++ b/dwm.c 1970-01-01 00:00:00.0 + @@ -624,9 +624,9 @@ configurerequest(XEvent *e) { if(ev->value_mask & CWHeight) c->h = ev->height; if((c->x + c->w) > m->mx + m->mw && c->isfloating) - c->x = m->mx + (m->mw / 2 - c->w / 2); /* center in x direction */ + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ if((c->y + c->h) > m->my + m->mh && c->isfloating) - c->y = m->my + (m->mh / 2 - c->h / 2); /* center in y direction */ + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ if((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) configure(c); if(ISVISIBLE(c))
[dev][dmenu] loadfont() segmentation fault patch
To reproduce the segmentation fault: dmenu -fn "-windows-montecarlo-medium-r-normal--11-110-72-72-c-60-microsoft-cp1252" <<< hello The culprit is loadfont(). This font[1] triggers XLoadQueryFont(), and loadfont() fails in the for loop. At least that's what my quick debug indicated. I did a diff between dmenu 4.3.1 and 4.4. Do we really want this twisted pretentious piece of code inside loadfont()? I for one like the 4.3.1 code better. And it worked. Patch attached inline. I do it the humble way. [1] The font is MonteCarlo: http://bok.net/MonteCarlo/downloads/MonteCarlo-PCF.tgz --- dmenu-4.4-a/draw.c 2011-07-19 20:31:28.0 + +++ dmenu-4.4-b/draw.c 2011-07-22 12:34:35.026736893 + @@ -121,23 +121,25 @@ initfont(DC *dc, const char *fontstr) { Bool loadfont(DC *dc, const char *fontstr) { char *def, **missing, **names; - int i, n = 1; + int i, n; XFontStruct **xfonts; if(!*fontstr) return False; - if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) + if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) { n = XFontsOfFontSet(dc->font.set, &xfonts, &names); - else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) - xfonts = &dc->font.xfont; - else - n = 0; - - for(i = 0; i < n; i++) { - dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent); - dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent); - dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width); + for(i = 0; i < n; i++) { + dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent); + dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent); + dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width); + } + } + else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) { + dc->font.ascent = dc->font.xfont->ascent; + dc->font.descent = dc->font.xfont->descent; + dc->font.width = dc->font.xfont->max_bounds.width; } + if(missing) XFreeStringList(missing); return (dc->font.set || dc->font.xfont);
[dev] [dmenu] dmenu_run improvements
dmenu_run doesn't really run the user input as a shell command line. For instance, run dmenu_run from a terminal, then in the menu type: echo hello\ world The terminal output is "hello\ world" instead of "hello world". `eval' solves the problem, but when it comes to eval, I can't really be sure, so please point out the possible errors/risks. `LC_ALL=C' is for sort. diff -up a/dmenu_run b/dmenu_run --- a/dmenu_run 1970-01-01 00:00:00.0 + +++ b/dmenu_run 1970-01-01 00:00:00.0 + @@ -1,9 +1,9 @@ #!/bin/sh CACHE=${XDG_CACHE_HOME:-"$HOME/.cache"}/dmenu_run ( - IFS=: + IFS=: LC_ALL=C if test "`ls -dt $PATH "$CACHE" 2> /dev/null | sed 1q`" != "$CACHE"; then mkdir -p "`dirname "$CACHE"`" && lsx $PATH | sort -u > "$CACHE" fi ) -cmd=`dmenu "$@" < "$CACHE"` && exec $cmd +cmd=`dmenu "$@" < "$CACHE"` && eval exec "$cmd"
Re: [dev] [dmenu] dmenu_run improvements
On 07/24/11 at 01:34am, Dave Reisner wrote: > #!/bin/sh > > CACHE=${XDG_CACHE_HOME:-"$HOME/.cache"}/dmenu_run > IFS=: > LC_COLLLATE=C You got a typo here: LC_COLLATE > > gencache() { > lsx $PATH | sort -u >"$CACHE" > } Since LC_COLLATE is only used for sort, we might as well just use: LC_COLLATE=C sort > > if [ ! -e "$CACHE" ]; then > mkdir -p "${CACHE%/*}" > gencache > fi > > for path in $PATH; do > if [ "$path" -nt "$CACHE" ]; then > gencache > break > fi > done Much better than `ls -t', congratulations. If $CACHE does not exist, there's no need to compare timestamps, so let's puts an `else' there. > unset IFS > > cmd=$(dmenu "$@" < "$CACHE") && eval exec "$cmd" Yeah, I see you just hate backticks :P
Re: [dev] [dmenu] dmenu_run improvements
On 07/24/11 at 11:38am, anonymous wrote: > On Sun, Jul 24, 2011 at 02:34:22PM +0800, lolilolicon wrote: > > > unset IFS > > > > > > cmd=$(dmenu "$@" < "$CACHE") && eval exec "$cmd" > > Yeah, I see you just hate backticks :P > > > > There is a difference: > > % echo `echo '\\'` > \ > % echo $(echo '\\') > \\ You're right. The backquoted form is "broken" even with no nesting. Now I'm officially a member of the anti-backticks club. Thank you.
Re: [dev] [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 4:40 PM, Anselm R Garbe wrote: > On 24 July 2011 08:38, anonymous wrote: >> On Sun, Jul 24, 2011 at 02:34:22PM +0800, lolilolicon wrote: >>> > unset IFS >>> > >>> > cmd=$(dmenu "$@" < "$CACHE") && eval exec "$cmd" >>> Yeah, I see you just hate backticks :P >>> >> >> There is a difference: >> >> % echo `echo '\\'` >> \ >> % echo $(echo '\\') >> \\ > > Yes, but bash'isms are a NO GO in suckless.org shell scripts :) > > Cheers, > Anselm > > This is no bashism. AFAIK, $(command) is in the POSIX shell specification: http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_03
Re: [dev] [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 4:39 PM, Anselm R Garbe wrote: > On 24 July 2011 06:34, Dave Reisner wrote: >> On Sun, Jul 24, 2011 at 12:35:19PM +0800, lolilolicon wrote: >>> dmenu_run doesn't really run the user input as a shell command line. >>> For instance, run dmenu_run from a terminal, then in the menu type: >>> >>> echo hello\ world >>> >>> The terminal output is "hello\ world" instead of "hello world". >>> >>> `eval' solves the problem, but when it comes to eval, I can't really >>> be sure, so please point out the possible errors/risks. >> >> Assuredly the least of the evils in this script -- the eval is >> necessary. More noteworthy is the parsing of ls here, which can easily >> be refactored out and still be /bin/sh compat. It also explodes on first >> run, and then there's the needless invocation of mkdir every time the >> cache is regenerated. suckless could suck a lot less at shell scripting. >> dmenu_run has gone through several iterations of bad and worse. >> >> d >> >> >> #!/bin/sh >> >> CACHE=${XDG_CACHE_HOME:-"$HOME/.cache"}/dmenu_run >> IFS=: >> LC_COLLLATE=C >> >> gencache() { >> lsx $PATH | sort -u >"$CACHE" >> } >> >> if [ ! -e "$CACHE" ]; then >> mkdir -p "${CACHE%/*}" >> gencache >> fi >> >> for path in $PATH; do >> if [ "$path" -nt "$CACHE" ]; then >> gencache >> break >> fi >> done > > Am I right that the previous loop runs gencache n times, under the > assumption that each path component contains newer files? > > Kind regards, > Anselm > > No, note the `break'.
Re: [dev] [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 5:06 PM, Anselm R Garbe wrote: > Nevertheless, I can see that script should be improved (not using the > ls -dt invocation), but I prefer the back ticks (even if $(..) is > POSIX, there were a couple of issues in the past that I barely > remember) and I want explicit test calls, I don't like the [ ... ] > syntax sugar. > > Thanks, > Anselm > > Fine, as long as it doesn't break anything, I can live with these two backticks. Since I'm the thread starter, may I conclude this with the following? #!/bin/sh CACHE=${XDG_CACHE_HOME:-$HOME/.cache}/dmenu_run ( IFS=: gen_cache() { lsx $PATH | LC_COLLATE=C sort -u > "$CACHE" } if test -e "$CACHE"; then for path in $PATH; do if test "$path" -nt "$CACHE"; then gen_cache break fi done else mkdir -p "${CACHE%/*}" gen_cache fi ) cmd=`dmenu "$@" < "$CACHE"` && eval exec "$cmd" NOTE: The (subshell) is used so the declarations won't get inherented, but the user still has access to the $CACHE and $cmd environments.
Re: [dev] [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 6:41 PM, lolilolicon wrote: > cmd=`dmenu "$@" < "$CACHE"` && eval exec "$cmd" > > NOTE: > The (subshell) is used so the declarations won't get inherented, but > the user still has access to the $CACHE and $cmd environments. > Hmm, maybe instead of `eval exec', we could just use `exec sh -c' instead? cmd=`dmenu "$@" < "$CACHE"` || exit $? exec sh -c "$cmd" The benefits are: 1. Only exported environment variables will be available to the user command. 2. The user can run any shell command, including, e.g., if true; then echo hello\ world; fi Correct me if I'm missing something here. Thanks.
Re: [dev] [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 9:09 PM, Connor Lane Smith wrote: > On 24 July 2011 06:34, Dave Reisner wrote: >> if [ "$path" -nt "$CACHE" ]; then > > 'test -nt' is non-portable. I think you've just discovered why we use > the 'ls -dt' hack. > > I agree that dmenu_run isn't the nicest script in existence. But > because of the tedious limitations of POSIX we don't have much choice. Sorry, but can you give an example where `test -nt' is not available? Or can you point out what do you refer to to determine the portability of a shell script? According to Greg's wiki [1], `test -nt' is not supported by dash, although it works here with dash 0.5.6.1. Even if you must support the older versions that does not know about `test -nt', you can use find: test `find "$path" -prune -newer "$CACHE"` as a drop-in replacement, which is better than `ls -dt' after all. [1] http://mywiki.wooledge.org/Bashism
Re: [dev] Re: [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 10:08 PM, Christian Neukirchen wrote: > anonymous writes: > >> On Sun, Jul 24, 2011 at 02:34:22PM +0800, lolilolicon wrote: >>> > unset IFS >>> > >>> > cmd=$(dmenu "$@" < "$CACHE") && eval exec "$cmd" >>> Yeah, I see you just hate backticks :P >>> >> >> There is a difference: >> >> % echo `echo '\\'` >> \ >> % echo $(echo '\\') >> \\ > > That must be a bashism, can't reproduce in dash, mksh, zsh. > > -- > Christian Neukirchen http://chneukirchen.org > > > The builtin echo may differ from /bin/echo. For example, here in dash: $ echo '\\' \ $ echo `echo '\\'` \ $ echo $(echo '\\') \ $ /bin/echo '\\' \\ $ /bin/echo `/bin/echo '\\'` \ $ /bin/echo $(/bin/echo '\\') \\ In fact the builtin echo really sucks: $ echo \\ \ $ echo \
Re: [dev] [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 10:09 PM, Dave Reisner wrote: > On Sun, Jul 24, 2011 at 10:00:09PM +0800, lolilolicon wrote: >> Sorry, but can you give an example where `test -nt' is not available? >> Or can you point out what do you refer to to determine the portability >> of a shell script? > > I usually reference opengroup for POSIX util specs. I do love the > wooledge wiki, though. > > http://pubs.opengroup.org/onlinepubs/95399/utilities/test.html > > d Thanks Dave for the tip. No wonder, I was looking at the Shell Command Language page. Cheers.
Re: [dev] Re: [dmenu] dmenu_run improvements
On Sun, Jul 24, 2011 at 10:30 PM, lolilolicon wrote: > On Sun, Jul 24, 2011 at 10:08 PM, Christian Neukirchen > wrote: >> anonymous writes: >> >>> On Sun, Jul 24, 2011 at 02:34:22PM +0800, lolilolicon wrote: >>>> > unset IFS >>>> > >>>> > cmd=$(dmenu "$@" < "$CACHE") && eval exec "$cmd" >>>> Yeah, I see you just hate backticks :P >>>> >>> >>> There is a difference: >>> >>> % echo `echo '\\'` >>> \ >>> % echo $(echo '\\') >>> \\ >> >> That must be a bashism, can't reproduce in dash, mksh, zsh. >> >> -- >> Christian Neukirchen http://chneukirchen.org >> >> >> > > The builtin echo may differ from /bin/echo. For example, here in dash: > > $ echo '\\' > \ > $ echo `echo '\\'` > \ > $ echo $(echo '\\') > \ > > $ /bin/echo '\\' > \\ > $ /bin/echo `/bin/echo '\\'` > \ > $ /bin/echo $(/bin/echo '\\') > \\ > > In fact the builtin echo really sucks: > > $ echo \\ > \ > $ echo > \ > Sorry, but please allow me to rephrase the issue. The `echo' builtin in dash by default behaves like `echo -e' in bash. So it's a bit tricky to reproduce the difference in dash: $ echo `echo ''` \ $ echo $(echo '') \\ Or, just use `printf %s': $ printf '%s\n' `printf '%s\n' '\\'` \ $ printf '%s\n' $(printf '%s\n' '\\') \\ For the record, the sucky `echo' is what POSIX defines: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html And thank you Szabolcs Nagy for pointing me to the lastes spec. Cheers.
Re: [dev] Ranger, a textbased filemanager
On Mon, Oct 24, 2011 at 6:01 AM, Bjartur Thorlacius wrote: >> if the task is 'waste a bunch of screen real estate' then no, ranger >> is a far better choice >> > The current combination of a shell and a terminal emulator is > horrible. I've found myself doing stuff like: > > exec /dev/tty2 2>/dev/tty1 > #optionally stty -echo > while read > do clear > $REPLY > done > > Why in Mímir's name do error messages and output get written to where > my input is echoed? > > This could to be an interesting idea. I'm thinking 3 column terminal (stdin, stdout, stderr), as well as remote control...
[dev] A general approach to master-slave layouts
The idea of having more than one master windows is brilliant. The `tile' layout in current hg tip basically splits the master and slave areas vertically, and tiles windows in each of the two areas using a vertical stacking algorithm. The `ncol' layout does it slightly differently by tilling the master windows using a horizontal stacking algorithm. The `n?bstack' layout also does it slightly differently, by splitting the master and slave areas horizontally, and tiles windows in each of them using a horizontal stacking algorithm. All of these layouts fit in a generalized model. Basically, there're two things that differentiate one master-slave layout from another: 1. How the master and slave areas are separated. Practically, the most useful way to do this is simply split the screen in two parts, either vertically, or horizontally. 2. What layout is used in each of the master and slave areas. We can use different layouts for the masters and the slaves. We should reuse the layout algorithms when we can. With this model in mind, I came up with the function apply_mslts(), i.e. "apply master-slave layouts", which takes care of the two things stated above. The actual layout algorithms are defined in the lt_* functions: lt_hstack tiles windows in a horizontal stack, i.e. columns. lt_vstack tiles windows in a vertical stack, i.e. rows. lt_grid tiles windows in a (gapless) grid. lt_monocle stacks windows, each maximized to fill the "booth". The "booth" is the box in which the layout is performed. Such functions are simpler and easier to read. And they are reusable. To define a master-slave layout is as simple as: static void grid(Monitor *m) { apply_mslts(m, False, lt_vstack, lt_grid); } Here `False` means split master and slave areas vertically; lt_vstack is applied to the master area, and lt_grid to the slaves. I've never seen such a layout before, you may find it interesting too. To test this out, simply grab the hg tip, #include mslts.c in config.h, add some example layouts such as `grid', bind a key combo, `make`, and run. Adjust the nmaster value with Mod + d/i, and mfact with Mod + h/j to see the effects. Also, this approach accepts nmaster being 0, in which case, all windows are slaves, and are thus tiled using the slaves' layout algorithm. This should be the case for the current hg tip, too, IMO. Finally, I'm not really good at C yet, so the code could probably use a whole lot of improvement. I'd really appreciate it if you could review the code and help me improve it, because I really like this idea. The code is in the attachment. typedef struct { int x, y, w, h; } Booth; static void apply_lt(Monitor *m, void (*ltf)(Client **, Booth *, unsigned int)); static void apply_mslts(Monitor *m, Bool hsplit, void (*mltf)(Client **, Booth *, unsigned int), /* master layout func */ void (*sltf)(Client **, Booth *, unsigned int)); /* slave layout func */ static void lt_hstack(Client **c, Booth *b, unsigned int n); static void lt_vstack(Client **c, Booth *b, unsigned int n); static void lt_grid(Client **c, Booth *b, unsigned int n); static void lt_monocle(Client **c, Booth *b, unsigned int n); /* Example master-slave layouts */ static void ntile(Monitor *m); static void ncol(Monitor *m); static void bstack(Monitor *m); static void grid(Monitor *m); static void ntile(Monitor *m) { apply_mslts(m, False, lt_vstack, lt_vstack); } static void ncol(Monitor *m) { apply_mslts(m, False, lt_hstack, lt_vstack); } static void bstack(Monitor *m) { apply_mslts(m, True, lt_hstack, lt_hstack); } static void grid(Monitor *m) { apply_mslts(m, False, lt_vstack, lt_grid); } /* Functions that apply layouts */ static void apply_lt(Monitor *m, void (*ltf)(Client **, Booth *, unsigned int)) { unsigned int n; Client *c; for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); if(n == 0) return; Booth b = { .x = m->wx, .y = m->wy, .w = m->ww, .h = m->wh }; (*ltf)(&c, &b, n); } static void apply_mslts(Monitor *m, Bool hsplit, void (*mltf)(Client **, Booth *, unsigned int), void (*sltf)(Client **, Booth *, unsigned int)) { unsigned int nm, n; Client *c; for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); if(n == 0) return; nm = MIN(n, m->nmaster); /* number of masters */ if (nm == 0) { /* all slaves */ c = m->clients; Booth b = { .x = m->wx, .y = m->wy, .w = m->ww, .h = m->wh }; (*sltf)(&c, &b, n); } else if(n > nm) { /* masters and slaves */ c = m->clients; if(hsplit) { /* masters above slaves */ Booth b = { .x = m->wx, .y = m->wy, .w = m->ww, .h = m->wh * m->mfact }; (*mltf)(&c, &b, nm); b.y += b.h; b.h = m->wh - b.h; (*sltf)(&c, &b, n - nm); } else { /* masters at the left of slaves */ Booth b = { .x = m->wx, .y = m->wy, .w = m->ww * m->mfact, .h = m->wh }; (*mltf)(&c, &b, nm); b.x += b.w; b.w = m->ww - b.w; (*sltf)(&c, &b, n - nm); } } else { /* a
[dev] [dwm] A general approach to master-slave layouts
The idea of having more than one master window is brilliant. The `tile' layout in current hg tip basically splits the master and slave areas vertically, and tiles windows in each of the two areas using a vertical stacking algorithm. The `ncol' layout does it slightly differently by tilling the master windows using a horizontal stacking algorithm. The `n?bstack' layout also does it slightly differently, by splitting the master and slave areas horizontally, and tiles windows in each of them using a horizontal stacking algorithm. All of these layouts fit in a generalized model. Basically, there're two things that differentiate one master-slave layout from another: 1. How the master and slave areas are separated. Practically, the most useful way to do this is simply split the screen in two parts, either vertically, or horizontally. 2. What layout is used in each of the master and slave areas. We can use different layouts for the masters and the slaves. We should reuse the layout algorithms when we can. With this model in mind, I came up with the function apply_mslts(), i.e. "apply master-slave layouts", which takes care of the two things stated above. The actual layout algorithms are defined in the lt_* functions: lt_hstack tiles windows in a horizontal stack, i.e. columns. lt_vstack tiles windows in a vertical stack, i.e. rows. lt_grid tiles windows in a (gapless) grid. lt_monocle stacks windows, each maximized to fill the "booth". The "booth" is the box in which the layout is performed. Such functions are simpler and easier to read. And they are reusable. To define a master-slave layout is as simple as: static void grid(Monitor *m) { apply_mslts(m, False, lt_vstack, lt_grid); } Here `False` means split master and slave areas vertically; lt_vstack is applied to the master area, and lt_grid to the slaves. I've never seen such a layout before, you may find it interesting too. To test this out, simply grab the hg tip, #include mslts.c in config.h, add some example layouts such as `grid', bind a key combo, `make`, and run. Adjust the nmaster value with Mod + d/i, and mfact with Mod + h/l to see the effects. Also, this approach accepts nmaster being 0, in which case, all windows are slaves, and are thus tiled using the slaves' layout algorithm. This should be the case for the current hg tip, too, IMO. Finally, I'm not really good at C yet, so the code could probably use a whole lot of improvement. I'd really appreciate it if you could review the code and help me improve it, because I really like this idea. The code is in the attachment. /* Resend this due to missing [dwm] tag in the subject, and an error in the code. Sorry for the inconvenience. */
[dev] Re: [dwm] A general approach to master-slave layouts
On Mon, Oct 31, 2011 at 9:49 PM, lolilolicon wrote: > > The code is in the attachment. > *huge facepalm* Forgot to attach the fixed code. Attached here. typedef struct { int x, y, w, h; } Booth; static void apply_lt(Monitor *m, void (*ltf)(Client **, Booth *, unsigned int)); static void apply_mslts(Monitor *m, Bool hsplit, void (*mltf)(Client **, Booth *, unsigned int), /* master layout func */ void (*sltf)(Client **, Booth *, unsigned int)); /* slave layout func */ static void lt_hstack(Client **c, Booth *b, unsigned int n); static void lt_vstack(Client **c, Booth *b, unsigned int n); static void lt_grid(Client **c, Booth *b, unsigned int n); static void lt_monocle(Client **c, Booth *b, unsigned int n); /* Example master-slave layouts */ static void ntile(Monitor *m); static void ncol(Monitor *m); static void bstack(Monitor *m); static void grid(Monitor *m); static void ntile(Monitor *m) { apply_mslts(m, False, lt_vstack, lt_vstack); } static void ncol(Monitor *m) { apply_mslts(m, False, lt_hstack, lt_vstack); } static void bstack(Monitor *m) { apply_mslts(m, True, lt_hstack, lt_hstack); } static void grid(Monitor *m) { apply_mslts(m, False, lt_vstack, lt_grid); } /* Functions that apply layouts */ static void apply_lt(Monitor *m, void (*ltf)(Client **, Booth *, unsigned int)) { unsigned int n; Client *c; for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); if(n == 0) return; c = m->clients; Booth b = { .x = m->wx, .y = m->wy, .w = m->ww, .h = m->wh }; (*ltf)(&c, &b, n); } static void apply_mslts(Monitor *m, Bool hsplit, void (*mltf)(Client **, Booth *, unsigned int), void (*sltf)(Client **, Booth *, unsigned int)) { unsigned int nm, n; Client *c; for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); if(n == 0) return; nm = MIN(n, m->nmaster); /* number of masters */ if (nm == 0) { /* all slaves */ c = m->clients; Booth b = { .x = m->wx, .y = m->wy, .w = m->ww, .h = m->wh }; (*sltf)(&c, &b, n); } else if(n > nm) { /* masters and slaves */ c = m->clients; if(hsplit) { /* masters above slaves */ Booth b = { .x = m->wx, .y = m->wy, .w = m->ww, .h = m->wh * m->mfact }; (*mltf)(&c, &b, nm); b.y += b.h; b.h = m->wh - b.h; (*sltf)(&c, &b, n - nm); } else { /* masters at the left of slaves */ Booth b = { .x = m->wx, .y = m->wy, .w = m->ww * m->mfact, .h = m->wh }; (*mltf)(&c, &b, nm); b.x += b.w; b.w = m->ww - b.w; (*sltf)(&c, &b, n - nm); } } else { /* all masters */ c = m->clients; Booth b = { .x = m->wx, .y = m->wy, .w = m->ww, .h = m->wh }; (*mltf)(&c, &b, n); } } /* Layout functions: the actual algorithms */ static void lt_hstack(Client **c, Booth *b, unsigned int n) { unsigned int i; int x, y, w, h; x = b->x; /* x offset of the next cell */ y = b->y; h = b->h; for(i = 0, *c = nexttiled(*c); *c && i < n; *c = nexttiled((*c)->next), i++) { w = (b->x + b->w - x) / (n - i); resize(*c, x, y, w - 2 * (*c)->bw, h - 2 * (*c)->bw, False); x += WIDTH(*c); } } static void lt_vstack(Client **c, Booth *b, unsigned int n) { unsigned int i; int x, y, w, h; x = b->x; y = b->y; /* y offset of the next cell */ w = b->w; for(i = 0, *c = nexttiled(*c); *c && i < n; *c = nexttiled((*c)->next), i++) { h = (b->y + b->h - y) / (n - i); resize(*c, x, y, w - 2 * (*c)->bw, h - 2 * (*c)->bw, False); y += HEIGHT(*c); } } static void lt_monocle(Client **c, Booth *b, unsigned int n) { unsigned int i; for(i = 0, *c = nexttiled(*c); *c && i < n; *c = nexttiled((*c)->next), i++) resize(*c, b->x, b->y, b->w - 2 * (*c)->bw, b->h - 2 * (*c)->bw, False); } static void lt_grid(Client **c, Booth *b, unsigned int n) { unsigned int cols, rows, cn, rn, i, cx, cy, cw, ch; /* grid dimensions */ for(cols = 0; cols <= n / 2; cols++) if(cols * cols >= n) break; if(n == 5) /* set layout against the general rule: not 1:2:2, but 2:3 */ cols = 2; rows = n / cols; /* window geometries */ cw = cols ? b->w / cols : b->w; cn = 0; /* current column number */ rn = 0; /* current row number */ for(i = 0, *c = nexttiled(*c); *c && i < n; *c = nexttiled((*c)->next), i++) { if(i / rows + 1 > cols - n % cols) rows = n / cols + 1; ch = rows ? b->h / rows : b->h; cx = b->x + cn * cw; cy = b->y + rn * ch; resize(*c, cx, cy, cw - 2 * (*c)->bw, ch - 2 * (*c)->bw, False); rn++; if(rn >= rows) { rn = 0; cn++; } } }
Re: [dev] Re: [dwm] A general approach to master-slave layouts
On Mon, Oct 31, 2011 at 10:19 PM, Thomas Dahms wrote: > > That looks interesting. I have one suggestion for a simplification: > I guess you can get rid of the functions combining the master and > slave layouts by modifying setlayout() to take three arguments (the > two layouts and the direction of master/slave splitting). This way you > could combine any two layouts in the key binding section of config.h. I don't think we can do this with the current Arg. I also don't see the benefit of this over defining the layouts. If the user wants to combine two layout algorithms, he can always define a layout in as few as three lines in config.h. Please elaborate if I misunderstand. Also, the code is so far in no way in conflict with the dwm code. This general approach is also more flexible. For example, the example `ntile' layout can replace `tile' in dwm.c, and `monocle' in dwm.c can be defined via `apply_lt` using `lt_monocle`. Actually, `apply_lt` can be removed, since `apply_mslts` does the same thing when nmaster == 0. I'm thinking of making nmaster a member of the Layout structure, instead of the current Monitor. This way, we can make all windows slaves in the `grid' layout, while having two masters when we switch to `tile'. The same applies to mfact- the `ncol' layout certainly needs a bigger mfact than `tile', for example. Finally, neither nmaster nor mfact makes sense in non-master-slave layouts (if you still use those, that is ;). Oh, and it would be nice if mfact increased when nmaster increased in the `ncol' layout, too. Maybe another function as a member of Layout, which would be executed to set mfact as a hook whenever nmaster changes? Since the current tile, monocle, and proposed bstack layouts are each a special case of the mslts approach, and mslts is more powerful (and I do think is simpler and easier to read), and if you care, mslts will likely decrease the SLOC due to removal of duplicated code, I think mslts could be accepted into mainline. What do you think? > > -- > Thomas Dahms > >
Re: [dev] Re: [dwm] A general approach to master-slave layouts
On Tue, Nov 1, 2011 at 12:11 AM, lolilolicon wrote: > > Actually, `apply_lt` can be removed, since `apply_mslts` does the same > thing when nmaster == 0. I'm thinking of making nmaster a member of > the Layout structure, instead of the current Monitor. This way, we can > make all windows slaves in the `grid' layout, while having two masters > when we switch to `tile'. The same applies to mfact- the `ncol' layout > certainly needs a bigger mfact than `tile', for example. Finally, neither > nmaster nor mfact makes sense in non-master-slave layouts (if you still > use those, that is ;). Oh, and it would be nice if mfact increased when > nmaster increased in the `ncol' layout, too. Maybe another function > as a member of Layout, which would be executed to set mfact as a hook > whenever nmaster changes? > Indeed mfact and nmaster being members of Layout does make more sense, and I made a patch which includes this change. Since I don't know much about hg, I just did a `hg export`, and attach the produced patch as attachment instead of inline. The commit message should describe what has been done, and the code should explain itself. Note that this may seem to add some SLOCs, but it actually reduces the amount of code required to implement the same layouts by avoiding code duplication. See how tile, bstack and col are each defined using just a one-liner. By defining two layout algorithms `lt_vstack` and `lt_hstack`, in combination with the hsplit switch, one can define 2 ** 2 * 2 = 8 such layouts, and if you count the (masters|slaves)-only layouts as separate ones, we got 10. Add a third layout algorithm, and you have 3 ** 2 * 2 + 3 = 21. Sure, not all layouts are useful for everyone, but hopefully this will produce some interesting layouts suitable for your particular setup. # HG changeset patch # User lolilolicon # Date 1320098001 -28800 # Node ID f35ce5cc96363a813f91e64f6eda30504052eeed # Parent 904e923827cb010abb7a31298264548946616d92 A general approach to master-slave layouts This makes the actual code implementing layout algorithms reusable. Apply two separate layout algorithms to the master area and the slave area. The master and slave areas are separated by either vertical or horizontal split. The `tile' algorithm is split out as `lt_vstack` and the `tile' function is now just a one-liner. Due to the reusability, the `bstack' and `col' layouts are added with few lines of code. Other interesting layout combinations can be easily added in the same manner. Move mfact and nmaster to the Layout structure, so that each layout can have its own mfact and nmaster. This makes sense when using several master-slave layouts, e.g., the `col' layout will usually use a mfact larger than `tile'. diff --git a/config.def.h b/config.def.h --- a/config.def.h +++ b/config.def.h @@ -23,15 +23,15 @@ }; /* layout(s) */ -static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ -static const int nmaster = 1;/* number of clients in master area */ static const Bool resizehints = True; /* True means respect size hints in tiled resizals */ -static const Layout layouts[] = { - /* symbol arrange function */ - { "[]=", tile },/* first entry is default */ - { "><>", NULL },/* no layout function means floating behavior */ - { "[M]", monocle }, +static Layout layouts[] = { + /* symbol arrange function mfactnmaster */ + { "[]=", tile,0.55,1 }, /* first entry is default */ + { "><>", NULL,0.5, 0 }, /* no layout function means floating behavior */ + { "[M]", monocle, 0.5, 0 }, + { "TTT", bstack, 0.55,1 }, + { "|||", col, 0.75,2 }, }; /* key definitions */ @@ -66,6 +66,8 @@ { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_s, setlayout, {.v = &layouts[3]} }, + { MODKEY, XK_c, setlayout, {.v = &layouts[4]} }, { MODKEY, XK_space, setlayout, {0} }, { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, { MODKEY, XK_0, view, {.ui = ~0 } }, diff --git a/dwm.c b/dwm.c --- a/dwm.c +++ b/dwm.c @@ -71,6 +71,10 @@ } Arg; typedef struct { + int x, y, w, h; +} Booth; + +typedef struct { unsigned int click; unsigned int mask; unsigned int button; @@ -120,12 +124,12 @@ typedef struct { const char *symbol; void (*arrange)(Monitor *); + float mfact;
Re: [dev] Re: [dwm] A general approach to master-slave layouts
On Tue, Nov 1, 2011 at 7:36 AM, Rob wrote: > > I don't have much time today, or possibly tomorrow, but I'm interested > in this patch, it sounds almost like it recurses on each sub-section of > the total area, applying a different layout function each time, except > it's limited to two calls, one for the master area and one for the > slave. It's only one patch, which I sent in my last mail, so not much really :) The core is the `apply_mslts' funtion. It walks all clients like any tiling layout would do, e.g., the `tile' function defined in the patch simply calls `apply_mslts(m, False, lt_vstack, lt_vstack)`, and it should do the layout exactly the same way the original `tile' would do, no extra wasted work. The code should be clear, but maybe note that the mltf/sltf functions modify `c'. > Either way, I'm hoping to try out your patch(es) at some point > this week, and hoping to mess around with the key bindings, I assume you > can change the master layout while keeping the slave one the same with a > binding, right? Not really. DWM knows only about a single layout per monitor, even if we set the masters to temporarily use a different layout algorithm, but not update the currently selected layout name, DWM will rearrange the masters back to the selected layout whenever arrangemon() is called, which happens probably before you realize it ;). That said, you can always define two layouts which differ only in the master layout, (e.g. `tile' and `col'). Or if we feel progressive, let's make DWM aware of the master layout as well as the slave layout, although I don't see practical need so far. Similarly, I also considered to enable toggling master/slave splitting direction, effectively "rotating" the layout, or even allow "flipping", but then thought it not useful in practice... > > Cheers, > Rob >
Re: [dev] A general approach to master-slave layouts
On Tue, Nov 1, 2011 at 8:41 AM, Connor Lane Smith wrote: > Hey, > > On 31/10/2011, lolilolicon wrote: >> The idea of having more than one master windows is brilliant. The `tile' >> layout in current hg tip basically splits the master and slave areas >> vertically, and tiles windows in each of the two areas using a vertical >> stacking algorithm. > > I'll be interested to see where this goes. The code is still a bit raw > at the moment, but I like the idea. I wonder how simple we can get > this patch... Indeed, I post it here for all your suckless energy to do my money laundry ;) > > cls > >
Re: [dev] Re: [dwm] A general approach to master-slave layouts
On Tue, Nov 1, 2011 at 6:20 PM, Anselm R Garbe wrote: > On 1 November 2011 00:07, lolilolicon wrote: >> Indeed mfact and nmaster being members of Layout does make more sense, and >> I made a patch which includes this change. >> >> Note that this may seem to add some SLOCs, but it actually reduces the >> amount of code required to implement the same layouts by avoiding code >> duplication. See how tile, bstack and col are each defined using just a >> one-liner. By defining two layout algorithms `lt_vstack` and `lt_hstack`, >> in combination with the hsplit switch, one can define 2 ** 2 * 2 = 8 such >> layouts, and if you count the (masters|slaves)-only layouts as separate >> ones, we got 10. Add a third layout algorithm, and you have >> 3 ** 2 * 2 + 3 = 21. Sure, not all layouts are useful for everyone, but >> hopefully this will produce some interesting layouts suitable for your >> particular setup. > > Thanks for you patch, I looked at it and it is indeed interesting. > However it needs further testing and review in order to be a candidate > for mainline at some point. > Can't agree more. > Some remarks: > > The change of the Layout struct makes it a lot harder to define > layouts, as now one also has to understand the variables > nmaster/mfact. Also nmaster/mfact are now layout specific variables > that might not be used by other layouts. This lacks a bit conceptual > clarity imho. > I also agree with what you said here, but let me clarify my intention. I really think it more useful to make mfact/nmaster layout-specific, otherwise I wounldn't have made the change to the Layout struct. For example, on my 1280x800 screen, mfact == 0.75 combined with nmaster == 2 in the n?col layout makes a nice layout, but the combination is very bad for the tile layout. As such, sharing mfact/nmaster across layouts isn't exactly nice, nor is it "dynamic" enough. But now I realize another problem with moving mfact/nmaster to Layout. The issue is two monitors should be able to use different mfact/nmaster values for the same layout; also, the setmfact/incnmaster functions will not update the unselected monitor, but will have their effects all of a sudden next time that monitor is arranged. This makes me want to make nmaster/mfact specific to the monitor *and* the layout. And I also prefer achieving this in the least intrusive way possible. > What I'd really prefer is keeping the interface intact we had, a > layout is just a function -- I have no objections that this function > calls other functions or set up some variables to fit its needs. This > would keep it equally simple to the user to define Layouts and leave > the interface to be a function, rather than a function + variables. > You are absolutely right. Now that I think of it, we can temporarily set m->mfact and/or m->nmaster in a layout function before calling apply_mslts, and restore the values afterwards. For example, define the col layout like this: /* int term_width is the width of a terminal (e.g. 80 characters) */ void col(Monitor *m) { float mfact = m->mfact; int nmaster = m->nmaster; /* masters will be term_width wide */ m->nmaster = MIN(nmaster, m->ww / term_width); m->mfact = (float)term_width * m->nmaster / m->ww; apply_mslts(m, False, lt_hstack, lt_vstack); m->mfact = mfact; m->nmaster = nmaster; } A bit back-and-forth with the mfact calculation (since we will calculate back to the width in apply_mslts), but it's a fair compromise, I guess. > Also I'm not absolutely happy about the introduction of the Booth > struct, I would rename that into Rect as we have used a similar name > in other areas. Having said this, I'm in favor of *not* using > XRectangle where possible, in order to keep the core code of dwm X > agnostic (which is one 6.0 goal btw). > Bah, Booth is cute! Just kidding; I knew it would sound strange and probably have to be renamed. Here we go, Rect it is. > Cheers, > Anselm > >
Re: [dev] Re: [dwm] A general approach to master-slave layouts
On Tue, Nov 1, 2011 at 11:38 PM, Anselm R Garbe wrote: > On 1 November 2011 16:27, lolilolicon wrote: >> But now I realize another problem with moving mfact/nmaster to Layout. >> The issue is two monitors should be able to use different mfact/nmaster >> values for the same layout; also, the setmfact/incnmaster functions >> will not update the unselected monitor, but will have their effects all >> of a sudden next time that monitor is arranged. >> This makes me want to make nmaster/mfact specific to the monitor *and* >> the layout. And I also prefer achieving this in the least intrusive >> way possible. > > Exactly this is the main problem. You could work around it with > changing monitor as follows: > > struct Monitor { > char ltsymbol[16]; > - float mfact; > - int nmaster; > + float mfact[LENGTH(layouts)]; > + int nmaster[LENGTH(layouts)]; > int num; > int by; /* bar geometry */ > int mx, my, mw, mh; /* screen size */ > > however this would require some reshuffling of the config.h inclusion > and also changes in various places. So I doubt it would be necessary > at all. Just stick with nmaster/mfact in Monitor. > Hmm, I think the difficulty is to track the index of the current layout in the layouts array. I'm not sure how. I'd like to stick with the current Monitor struct. >> You are absolutely right. Now that I think of it, we can temporarily >> set m->mfact and/or m->nmaster in a layout function before calling >> apply_mslts, and restore the values afterwards. For example, define >> the col layout like this: >> >> /* int term_width is the width of a terminal (e.g. 80 characters) */ >> void >> col(Monitor *m) { >> float mfact = m->mfact; >> int nmaster = m->nmaster; >> /* masters will be term_width wide */ >> m->nmaster = MIN(nmaster, m->ww / term_width); >> m->mfact = (float)term_width * m->nmaster / m->ww; >> apply_mslts(m, False, lt_hstack, lt_vstack); >> m->mfact = mfact; >> m->nmaster = nmaster; >> } >> >> A bit back-and-forth with the mfact calculation (since we will calculate >> back to the width in apply_mslts), but it's a fair compromise, I guess. > > This could work for a patch, but I don't think this is a great way of > programming for mainline, since manipulating and restoring > nmaster/mfact for the time being is quite ugly. ;) Well, it's a compromise. Would you feel it's less ugly if I define the apply_mslts function like this: tatic void apply_mslts(Monitor *m, Bool hsplit, float mfact, int nmaster, void (*mltf)(Client **, Booth *, unsigned int), void (*sltf)(Client **, Booth *, unsigned int)) { /* uses mfact instead of m->mfact, nmaster intead of m->nmaster */ } and define the layouts like: void col(Monitor *m) { int nmaster = MIN(m->nmaster, m->ww / term_width); float mfact = (float)term_width * nmaster / m->ww; apply_mslts(m, False, mfact, nmaster, lt_hstack, lt_vstack); } void tile(Monitor *m) { apply_mslts(m, False, m->mfact, m->nmaster, lt_vstack, lt_vstack); } Basically the same thing, I guess it's still ugly, but does it feel less ugly? :D > > Cheers, > Anselm > >
Re: [dev] Re: [dwm] A general approach to master-slave layouts
I believe every master-slave layout, i.e., layouts where mfact/nmaster make sense, should own its own mfact/nmaster value, not to be disturbed by other layouts. As said before, `col' and `tile' for example just can't share the same mfact and still both look good. Consequently, I decided to update the patch so mfact and nmaster are now monitor- and layout-specific. This should make the master-slave layouts play nice with each other and thus make the idea more complete. Also, the nice thing about this approach is that the config.h interface is left unchanged. mfact and nmaster values of each layout are initialized to the global value mfact and nmaster; the user can also implement layouts that force rules on mfact and nmaster if desired. diff -r 904e923827cb -r 983f8ffd9f7c config.def.h --- a/config.def.h Mon Oct 31 20:09:27 2011 +0100 +++ b/config.def.h Wed Nov 02 20:15:22 2011 +0800 @@ -32,6 +32,8 @@ { "[]=", tile },/* first entry is default */ { "><>", NULL },/* no layout function means floating behavior */ { "[M]", monocle }, + { "TTT", bstack }, + { "|||", col }, }; /* key definitions */ @@ -66,6 +68,8 @@ { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_s, setlayout, {.v = &layouts[3]} }, + { MODKEY, XK_c, setlayout, {.v = &layouts[4]} }, { MODKEY, XK_space, setlayout, {0} }, { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, { MODKEY, XK_0, view, {.ui = ~0 } }, diff -r 904e923827cb -r 983f8ffd9f7c dwm.c --- a/dwm.c Mon Oct 31 20:09:27 2011 +0100 +++ b/dwm.c Wed Nov 02 20:15:22 2011 +0800 @@ -122,26 +122,9 @@ void (*arrange)(Monitor *); } Layout; -struct Monitor { - char ltsymbol[16]; - float mfact; - int nmaster; - int num; - int by; /* bar geometry */ - int mx, my, mw, mh; /* screen size */ - int wx, wy, ww, wh; /* window area */ - unsigned int seltags; - unsigned int sellt; - unsigned int tagset[2]; - Bool showbar; - Bool topbar; - Client *clients; - Client *sel; - Client *stack; - Monitor *next; - Window barwin; - const Layout *lt[2]; -}; +typedef struct { + int x, y, w, h; +} Rect; typedef struct { const char *class; @@ -153,18 +136,23 @@ } Rule; /* function declarations */ +static void apply_mslts(Monitor *m, Bool hsplit, + void (*mltf)(Client **, Rect *, unsigned int), /* master layout function */ + void (*sltf)(Client **, Rect *, unsigned int)); /* slave layout function */ static void applyrules(Client *c); static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact); static void arrange(Monitor *m); static void arrangemon(Monitor *m); static void attach(Client *c); static void attachstack(Client *c); +static void bstack(Monitor *); static void buttonpress(XEvent *e); static void checkotherwm(void); static void cleanup(void); static void cleanupmon(Monitor *mon); static void clearurgent(Client *c); static void clientmessage(XEvent *e); +static void col(Monitor *); static void configure(Client *c); static void configurenotify(XEvent *e); static void configurerequest(XEvent *e); @@ -185,6 +173,7 @@ static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static unsigned long getcolor(const char *colstr); +static unsigned int getlayoutindex(const Layout *lt); static Bool getrootptr(int *x, int *y); static long getstate(Window w); static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); @@ -194,6 +183,8 @@ static void initfont(const char *fontstr); static void keypress(XEvent *e); static void killclient(const Arg *arg); +static void lt_hstack(Client **c, Rect *r, unsigned int n); +static void lt_vstack(Client **c, Rect *r, unsigned int n); static void manage(Window w, XWindowAttributes *wa); static void mappingnotify(XEvent *e); static void maprequest(XEvent *e); @@ -281,11 +272,92 @@ /* configuration, allows nested code to access above variables */ #include "config.h" +struct Monitor { + char ltsymbol[16]; + float mfact[LENGTH(layouts)]; + int nmaster[LENGTH(layouts)]; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + Bool showbar; + Bool topbar; + Client *clients; + Client *sel;
Re: [dev] A general approach to master-slave layouts
On Mon, Nov 7, 2011 at 3:28 AM, Connor Lane Smith wrote: > Hey, > > I've been thinking about this patch for a while, and I've knocked > together a patch which takes an alternative approach, which seems to > result in a simpler diff. > > In my patch each layout has three arrangement functions, one for the > master, one the slave, and one a 'meta-layout' which defines how the > master and slave "booths" are laid out. So dwm's tile() is achieved > with an htile() meta-layout and vtile() master and slave. > > The patch is +12 lines over dwm tip. > > Thanks, > cls > This is nice. I've played around with it, and have had some progress: * Fixed the nmaster == 0 case. The original patch would create an empty master area. * Expoit the mfact < 0 possibility. When mfact is set to negative, use the slave area as master area, and vice versa. This makes it possible to put masters on the right in tile, for example. Adjusting setmfact() for this, for now, but we should probably make this more flexible... * Port my original spiral layout to play with this patch. I've attached the patch and the spiral layout as attachments. And a question. What is your plan on dealing with mfact/nmaster? As I've always insisted, they should be both monitor- and layout-specific. What's your opinion, and how would you approach this? Thanks diff -r ee36ffbd4252 config.def.h --- a/config.def.h Sun Nov 06 20:36:23 2011 +0100 +++ b/config.def.h Mon Nov 07 11:03:20 2011 +0800 @@ -29,9 +29,9 @@ static const Layout layouts[] = { /* symbol arrange function */ - { "[]=", tile },/* first entry is default */ - { "><>", NULL },/* no layout function means floating behavior */ - { "[M]", monocle }, + { "[]=", tileh, tilev, tilev }, /* first entry is default */ + { "><>", NULL,NULL,NULL },/* no layout function means floating behavior */ + { "[M]", monocle, monocle, monocle }, }; /* key definitions */ diff -r ee36ffbd4252 dwm.c --- a/dwm.c Sun Nov 06 20:36:23 2011 +0100 +++ b/dwm.c Mon Nov 07 11:03:20 2011 +0800 @@ -121,7 +121,9 @@ typedef struct { const char *symbol; - void (*arrange)(Monitor *); + void (*arrange)(Client *, float, XRectangle *, XRectangle *); + void (*master)(Client *, float, XRectangle *, XRectangle *); + void (*slave)(Client *, float, XRectangle *, XRectangle *); } Layout; struct Monitor { @@ -199,7 +201,7 @@ static void manage(Window w, XWindowAttributes *wa); static void mappingnotify(XEvent *e); static void maprequest(XEvent *e); -static void monocle(Monitor *m); +static void monocle(Client *c, float fact, XRectangle *r, XRectangle *rp); static void movemouse(const Arg *arg); static Client *nexttiled(Client *c); static void pop(Client *); @@ -226,7 +228,8 @@ static void tag(const Arg *arg); static void tagmon(const Arg *arg); static int textnw(const char *text, unsigned int len); -static void tile(Monitor *); +static void tileh(Client *c, float fact, XRectangle *r, XRectangle *rp); +static void tilev(Client *c, float fact, XRectangle *r, XRectangle *rp); static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); static void toggletag(const Arg *arg); @@ -403,9 +406,28 @@ void arrangemon(Monitor *m) { + XRectangle r, rm, rt = { m->wx, m->wy, m->ww, m->wh }; + Client *c; + float f; + int i, n; + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); - if(m->lt[m->sellt]->arrange) - m->lt[m->sellt]->arrange(m); + if(m->lt[m->sellt]->arrange) { + for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if(n > 0) { + if((f = (n > m->nmaster) ? (m->nmaster == 0 ? 0 : m->mfact) : 1) < 0) { +rm = rt; +m->lt[m->sellt]->arrange(NULL, 1 + f, &rm, &rt); + } + else +m->lt[m->sellt]->arrange(NULL, f, &rt, &rm); + for(i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) +if(i < m->nmaster) + m->lt[m->sellt]->master(c, 1.0 / (MIN(n, m->nmaster) - i), &rm, &r); +else + m->lt[m->sellt]->slave(c, 1.0 / (n - i), &rt, &r); + } + } restack(m); } @@ -1188,17 +1210,13 @@ } void -monocle(Monitor *m) { - unsigned int n = 0; - Client *c; - - for(c = m->clients; c; c = c->next) - if(ISVISIBLE(c)) - n++; - if(n > 0) /* override layout symbol */ - snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); - for(c = nexttiled(m->clients); c; c = nexttiled(c->next)) - resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False); +monocle(Client *c, float fact, XRectangle *r, XRectangle *rp) { + rp->x = r->x; + rp->y = r->y; + rp->width = r->width; + rp->height = r->height; + if(c) + resize(c, r->x, r->y, r->width - (2*c->bw), r->height - (2*c->bw), False); } void @@ -1553,13 +1571,15 @@ void setmfact(const Arg *arg) { float f; + int i; if(!arg || !selmon->lt[selmon->sellt]->arrange) return; - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + i = selmon->mfact < 0 ? -1 : 1; + f = a
Re: [dev] A general approach to master-slave layouts
On Mon, Nov 7, 2011 at 11:15 AM, lolilolicon wrote: > > * Port my original spiral layout to play with this patch. I've simplified the spiral layout by rusing tile{h,v}. Nice. void spiral(Client *c, float fact, XRectangle *r, XRectangle *rp) { if(fact == 0) { rp->width = rp->height = 0; return; } static int pos = 1; float f = fact == 1 ? 1 : 0.5; switch(pos) { case 0: /* left */ tileh(c, f, r, rp); pos = 1; break; case 1: /* top */ tilev(c, f, r, rp); pos = 2; break; case 2: /* right */ tileh(c, f, &((XRectangle){ r->x + r->width * (1 - f), r->y, r->width, r->height }), rp); r->width -= rp->width; pos = 3; break; case 3: /* bottom */ tilev(c, f, &((XRectangle){ r->x, r->y + r->height * (1 - f), r->width, r->height }), rp); r->height -= rp->height; pos = 0; break; } if(fact == 1) pos = 1; /* reset to initial value */ }