about summary refs log tree commit diff stats
path: root/layout.c
blob: 02ef559dbab21eaa1fcd92f9b9c1cb784a6c52a8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */
#include "dwm.h"

unsigned int blw = 0;
Layout *lt = NULL;

/* static */

static unsigned int nlayouts = 0;
static unsigned int masterw = MASTERWIDTH;
static unsigned int nmaster = NMASTER;

static void
tile(void) {
	unsigned int i, n, nx, ny, nw, nh, mw, mh, tw, th;
	Client *c;

	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
		n++;
	/* window geoms */
	mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1);
	mw = (n > nmaster) ? (waw * masterw) / 1000 : waw;
	th = (n > nmaster) ? wah / (n - nmaster) : 0;
	tw = waw - mw;

	for(i = 0, c = clients; c; c = c->next)
		if(isvisible(c)) {
			if(c->isbanned)
				XMoveWindow(dpy, c->win, c->x, c->y);
			c->isbanned = False;
			if(c->isversatile)
				continue;
			c->ismax = False;
			nx = wax;
			ny = way;
			if(i < nmaster) {
				ny += i * mh;
				nw = mw - 2 * BORDERPX;
				nh = mh - 2 * BORDERPX;
			}
			else {  /* tile window */
				nx += mw;
				nw = tw - 2 * BORDERPX;
				if(th > 2 * BORDERPX) {
					ny += (i - nmaster) * th;
					nh = th - 2 * BORDERPX;
				}
				else /* fallback if th <= 2 * BORDERPX */
					nh = wah - 2 * BORDERPX;
			}
			resize(c, nx, ny, nw, nh, False);
			i++;
		}
		else {
			c->isbanned = True;
			XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
		}
	if(!sel || !isvisible(sel)) {
		for(c = stack; c && !isvisible(c); c = c->snext);
		focus(c);
	}
	restack();
}

LAYOUTS

/* extern */

void
focusnext(Arg arg) {
	Client *c;
   
	if(!sel)
		return;
	for(c = sel->next; c && !isvisible(c); c = c->next);
	if(!c)
		for(c = clients; c && !isvisible(c); c = c->next);
	if(c) {
		focus(c);
		restack();
	}
}

void
focusprev(Arg arg) {
	Client *c;

	if(!sel)
		return;
	for(c = sel->prev; c && !isvisible(c); c = c->prev);
	if(!c) {
		for(c = clients; c && c->next; c = c->next);
		for(; c && !isvisible(c); c = c->prev);
	}
	if(c) {
		focus(c);
		restack();
	}
}

void
incmasterw(Arg arg) {
	if(lt->arrange != tile)
		return;
	if(arg.i == 0)
		masterw = MASTERWIDTH;
	else {
		if(waw * (masterw + arg.i) / 1000 >= waw - 2 * BORDERPX
		|| waw * (masterw + arg.i) / 1000 <= 2 * BORDERPX)
			return;
		masterw += arg.i;
	}
	lt->arrange();
}

void
incnmaster(Arg arg) {
	if((lt->arrange != tile) || (nmaster + arg.i < 1)
	|| (wah / (nmaster + arg.i) <= 2 * BORDERPX))
		return;
	nmaster += arg.i;
	if(sel)
		lt->arrange();
	else
		drawstatus();
}

void
initlayouts(void) {
	unsigned int i, w;

	lt = &layout[0];
	nlayouts = sizeof layout / sizeof layout[0];
	for(blw = i = 0; i < nlayouts; i++) {
		w = textw(layout[i].symbol);
		if(w > blw)
			blw = w;
	}
}

Client *
nexttiled(Client *c) {
	for(; c && (c->isversatile || !isvisible(c)); c = c->next);
	return c;
}

void
restack(void) {
	Client *c;
	XEvent ev;

	drawstatus();
	if(!sel)
		return;
	if(sel->isversatile || lt->arrange == versatile)
		XRaiseWindow(dpy, sel->win);
	if(lt->arrange != versatile) {
		if(!sel->isversatile)
			XLowerWindow(dpy, sel->win);
		for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
			if(c == sel)
				continue;
			XLowerWindow(dpy, c->win);
		}
	}
	XSync(dpy, False);
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
}

void
setlayout(Arg arg) {
	unsigned int i;

	if(arg.i == -1) {
		for(i = 0; i < nlayouts && lt != &layout[i]; i++);
		if(i == nlayouts - 1)
			lt = &layout[0];
		else
			lt = &layout[++i];
	}
	else {
		if(arg.i < 0 || arg.i >= nlayouts)
			return;
		lt = &layout[arg.i];
	}
	if(sel)
		lt->arrange();
	else
		drawstatus();
}

void
togglemax(Arg arg) {
	XEvent ev;

	if(!sel || (lt->arrange != versatile && !sel->isversatile) || sel->isfixed)
		return;
	if((sel->ismax = !sel->ismax)) {
		sel->rx = sel->x;
		sel->ry = sel->y;
		sel->rw = sel->w;
		sel->rh = sel->h;
		resize(sel, wax, way, waw - 2 * BORDERPX, wah - 2 * BORDERPX, True);
	}
	else
		resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
}

void
versatile(void) {
	Client *c;

	for(c = clients; c; c = c->next) {
		if(isvisible(c)) {
			if(c->isbanned)
				XMoveWindow(dpy, c->win, c->x, c->y);
			c->isbanned = False;
			resize(c, c->x, c->y, c->w, c->h, True);
		}
		else {
			c->isbanned = True;
			XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
		}
	}
	if(!sel || !isvisible(sel)) {
		for(c = stack; c && !isvisible(c); c = c->snext);
		focus(c);
	}
	restack();
}

void
zoom(Arg arg) {
	unsigned int n;
	Client *c;

	if(!sel || lt->arrange != tile || sel->isversatile)
		return;
	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
		n++;
	if((c = sel) == nexttiled(clients))
		if(!(c = nexttiled(c->next)))
			return;
	detach(c);
	attach(c);
	focus(c);
	lt->arrange();
}
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
                                                                           
                          






                                                                              
 
                     
           
                   

 
                    

               

 
                                                                                               
             
             
                      
   

                          
                                                                            

          

                                             
                                                  

                                                            

 
                                                                              
             
             
                                                                        
                                    
                                             










                                                                                                                  
             
                      
                 
                                                  
                         
   
                                         
                  
                                  
                 

                                                
                      
                                                              
                                                  
                                                       

          
                                                

 
                                                                      
             
             
   
                                             
                 


                         
                    
                          

 
                                                                           
             
             

                             
   
                                         
                  
                                






                         
                                                                                                         
             
             
                      
                                                          
   

                          
                                                                                     
          
   

                                             
                                                         




                                                
                                                           

 

                                                                                                    
             
                                     
                                                   

















                                                                                            
                                                                             
             
             
   
                                             



                          
                          
 
 
                                                                                                                                         
             
             

                                                       
                             
                                    
   
                                             



                        
                                      

                                                   
                                 
                                                  
                                              

                         
   
                                         
                  
                                  
                                         
                                                






                                      

                                                  
                           

                                                     
                       
                 
   
                                         
                  
                                  


                               
                                        
                  
            
 
# Wrappers around file system primitives that take a 'resources' object and
# are thus easier to test.
#
# - start-reading - asynchronously open a file, returning a channel source for
#   receiving the results
# - start-writing - asynchronously open a file, returning a channel sink for
#   the data to write
# - slurp - synchronously read from a file
# - dump - synchronously write to a file

container resources [
  lock:bool
  data:&:@:resource
]

container resource [
  name:text
  contents:text
]

def start-reading resources:&:resources, filename:text -> contents:&:source:char, error?:bool [
  local-scope
  load-inputs
  error? <- copy false
  {
    break-unless resources
    # fake file system
    contents, error? <- start-reading-from-fake-resource resources, filename
    return
  }
  # real file system
  file:num <- $open-file-for-reading filename
  return-unless file, null/no-contents, true/error
  contents:&:source:char, sink:&:sink:char <- new-channel 30
  start-running receive-from-file file, sink
]

def slurp resources:&:resources, filename:text -> contents:text, error?:bool [
  local-scope
  load-inputs
  source:&:source:char, error?:bool <- start-reading resources, filename
  return-if error?, null/no-contents
  buf:&:buffer:char <- new-buffer 30/capacity
  {
    c:char, done?:bool, source <- read source
    break-if done?
    buf <- append buf, c
    loop
  }
  contents <- buffer-to-array buf
]

def start-reading-from-fake-resource resources:&:resources, resource:text -> contents:&:source:char, error?:bool [
  local-scope
  load-inputs
  error? <- copy false
  i:num <- copy 0
  data:&:@:resource <- get *resources, data:offset
  len:num <- length *data
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    tmp:resource <- index *data, i
    i <- add i, 1
    curr-resource:text <- get tmp, name:offset
    found?:bool <- equal resource, curr-resource
    loop-unless found?
    contents:&:source:char, sink:&:sink:char <- new-channel 30
    curr-contents:text <- get tmp, contents:offset
    start-running receive-from-text curr-contents, sink
    return
  }
  return null/no-such-resource, true/error-found
]

def receive-from-file file:num, sink:&:sink:char -> sink:&:sink:char [
  local-scope
  load-inputs
  {
    c:char, eof?:bool <- $read-from-file file
    break-if eof?
    sink <- write sink, c
    loop
  }
  sink <- close sink
  file <- $close-file file
]

def receive-from-text contents:text, sink:&:sink:char -> sink:&:sink:char [
  local-scope
  load-inputs
  i:num <- copy 0
  len:num <- length *contents
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    c:char <- index *contents, i
    sink <- write sink, c
    i <- add i, 1
    loop
  }
  sink <- close sink
]

def start-writing resources:&:resources, filename:text -> sink:&:sink:char, routine-id:num, error?:bool [
  local-scope
  load-inputs
  error? <- copy false
  source:&:source:char, sink:&:sink:char <- new-channel 30
  {
    break-unless resources
    # fake file system
    routine-id <- start-running transmit-to-fake-resource resources, filename, source
    return
  }
  # real file system
  file:num <- $open-file-for-writing filename
  return-unless file, null/sink, 0/routine-id, true/error
  {
    break-if file
    msg:text <- append [no such file: ] filename
    assert file, msg
  }
  routine-id <- start-running transmit-to-file file, source
]

def dump resources:&:resources, filename:text, contents:text -> resources:&:resources, error?:bool [
  local-scope
  load-inputs
  # todo: really create an empty file
  return-unless contents, resources, false/no-error
  sink-file:&:sink:char, write-routine:num, error?:bool <- start-writing resources, filename
  return-if error?
  i:num <- copy 0
  len:num <- length *contents
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    c:char <- index *contents, i
    sink-file <- write sink-file, c
    i <- add i, 1
    loop
  }
  close sink-file
  # make sure to wait for the file to be actually written to disk
  # (Mu practices structured concurrency: http://250bpm.com/blog:71)
  wait-for-routine write-routine
]

def transmit-to-file file:num, source:&:source:char -> source:&:source:char [
  local-scope
  load-inputs
  {
    c:char, done?:bool, source <- read source
    break-if done?
    $write-to-file file, c
    loop
  }
  file <- $close-file file
]

def transmit-to-fake-resource resources:&:resources, filename:text, source:&:source:char -> resources:&:resources, source:&:source:char [
  local-scope
  load-inputs
  lock:location <- get-location *resources, lock:offset
  wait-for-reset-then-set lock
  # compute new file contents
  buf:&:buffer:char <- new-buffer 30
  {
    c:char, done?:bool, source <- read source
    break-if done?
    buf <- append buf, c
    loop
  }
  contents:text <- buffer-to-array buf
  new-resource:resource <- merge filename, contents
  # write to resources
  curr-filename:text <- copy null
  data:&:@:resource <- get *resources, data:offset
  # replace file contents if it already exists
  i:num <- copy 0
  len:num <- length *data
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    tmp:resource <- index *data, i
    curr-filename <- get tmp, name:offset
    found?:bool <- equal filename, curr-filename
    {
      break-unless found?
      put-index *data, i, new-resource
      jump +unlock-and-exit
    }
    i <- add i, 1
    loop
  }
  # if file didn't already exist, make room for it
  new-len:num <- add len, 1
  new-data:&:@:resource <- new resource:type, new-len
  put *resources, data:offset, new-data
  # copy over old files
  i:num <- copy 0
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    tmp:resource <- index *data, i
    put-index *new-data, i, tmp
  }
  # write new file
  put-index *new-data, len, new-resource
  +unlock-and-exit
  reset lock
]