## putting the environment together out of editors # # Consists of one editor on the left for recipes and one on the right for the # sandbox. def! main [ local-scope open-console clear-screen null/screen # non-scrolling app env:&:environment <- new-programming-environment null/filesystem, null/screen render-all null/screen, env, render event-loop null/screen, null/console, env, null/filesystem ] container environment [ recipes:&:editor current-sandbox:&:editor sandbox-in-focus?:bool # false => cursor in recipes; true => cursor in current-sandbox ] def new-programming-environment resources:&:resources, screen:&:screen, test-sandbox-editor-contents:text -> result:&:environment [ local-scope load-inputs width:num <- screen-width screen result <- new environment:type # recipe editor on the left initial-recipe-contents:text <- slurp resources, [lesson/recipes.mu] # ignore errors divider:num, _ <- divide-with-remainder width, 2 recipes:&:editor <- new-editor initial-recipe-contents, 0/left, divider/right # sandbox editor on the right sandbox-left:num <- add divider, 1 current-sandbox:&:editor <- new-editor test-sandbox-editor-contents, sandbox-left, width/right *result <- put *result, recipes:offset, recipes *result <- put *result, current-sandbox:offset, current-sandbox *result <- put *result, sandbox-in-focus?:offset, false ] def event-loop screen:&:screen, console:&:console, env:&:environment, resources:&:resources -> screen:&:screen, console:&:console, env:&:environment, resources:&:resources [ local-scope load-inputs recipes:&:editor <- get *env, recipes:offset current-sandbox:&:editor <- get *env, current-sandbox:offset sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset # if we fall behind we'll stop updating the screen, but then we have to # render the entire screen when we catch up. # todo: test this render-recipes-on-no-more-events?:bool <- copy false render-sandboxes-on-no-more-events?:bool <- copy false { # looping over each (keyboard or touch) event as it occurs +next-event e:event, found?:bool, quit?:bool, console <- read-event console loop-unless found? break-if quit? # only in tests trace 10, [app], [next-event] # check for global events that will trigger regardless of which editor has focus { k:num, is-keycode?:bool <- maybe-convert e:event, keycode:variant break-unless is-keycode? } { c:char, is-unicode?:bool <- maybe-convert e:event, text:variant break-unless is-unicode? } # 'touch' event - send to both sides, see what picks it up { t:touch-event, is-touch?:bool <- maybe-convert e:event, touch:variant break-unless is-touch? # ignore all but 'left-click' events for now # todo: test this touch-type:num <- get t, type:offset is-left-click?:bool <- equal touch-type, 65513/mouse-left loop-unless is-left-click?, +next-event click-row:num <- get t, row:offset click-column:num <- get t, column:offset # later exceptions for non-editor touches will go here # send to both editors _ <- move-cursor recipes, screen, t sandbox-in-focus?:bool <- move-cursor current-sandbox, screen, t *env <- put *env, sandbox-in-focus?:offset, sandbox-in-focus? screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env loop +next-event } # 'resize' event - redraw editor # todo: test this after supporting resize in assume-console { r:resize-event, is-resize?:bool <- maybe-convert e:event, resize:variant break-unless is-resize? env, screen <- resize screen, env screen <- render-all screen, env, render-without-moving-cursor loop +next-event } # if it's not global and not a touch event, send to appropriate editor { sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset { break-if sandbox-in-focus? render?:bool <- handle-keyboard-event screen, recipes, e:event render-recipes-on-no-more-events? <- or render?, render-recipes-on-no-more-events? } { break-unless sandbox-in-focus? render?:bool <- handle-keyboard-event screen, current-sandbox, e:event render-sandboxes-on-no-more-events? <- or render?, render-sandboxes-on-no-more-events? } more-events?:bool <- has-more-events? console { break-if more-events? { break-unless render-recipes-on-no-more-events? render-recipes-on-no-more-events? <- copy false screen <- render-recipes screen, env, render } { break-unless render-sandboxes-on-no-more-events? render-sandboxes-on-no-more-events? <- copy false screen <- render-sandbox-side screen, env, render } } screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env } loop } ] def resize screen:&:screen, env:&:environment -> env:&:environment, screen:&:screen [ local-scope load-inputs clear-screen screen # update screen dimensions width:num <- screen-width screen divider:num, _ <- divide-with-remainder width, 2 # update recipe editor recipes:&:editor <- get *env, recipes:offset right:num <- subtract divider, 1 *recipes <- put *recipes, right:offset, right # reset cursor (later we'll try to preserve its position) *recipes <- put *recipes, cursor-row:offset, 1 *recipes <- put *recipes, cursor-column:offset, 0 # update sandbox editor current-sandbox:&:editor <- get *env, current-sandbox:offset left:num <- add divider, 1 *current-sandbox <- put *current-sandbox, left:offset, left right:num <- subtract width, 1 *current-sandbox <- put *current-sandbox, right:offset, right # reset cursor (later we'll try to preserve its position) *current-sandbox <- put *current-sandbox, cursor-row:offset, 1 *current-sandbox <- put *current-sandbox, cursor-column:offset, left ] # Variant of 'render' that updates cursor-row and cursor-column based on # before-cursor (rather than the other way around). If before-cursor moves # off-screen, it resets cursor-row and cursor-column. def render-without-moving-cursor screen:&:screen, editor:&:editor -> last-row:num, last-column:num, screen:&:screen, editor:&:editor [ local-scope load-inputs return-unless editor, 1/top, 0/left left:num <- get *editor, left:offset screen-height:num <- screen-height screen right:num <- get *editor, right:offset curr:&:duplex-list:char <- get *editor, top-of-screen:offset prev:&:duplex-list:char <- copy curr # just in case curr becomes null and we can't compute prev curr <- next curr color:num <- copy 7/white row:num <- copy 1/top column:num <- copy left # save before-cursor old-before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset # initialze cursor-row/cursor-column/before-cursor to the top of the screen # by default *editor <- put *editor, cursor-row:offset, row *editor <- put *editor, cursor-column:offset, column top-of-screen:&:duplex-list:char <- get *editor, top-of-screen:offset *editor <- put *editor, before-cursor:offset, top-of-screen screen <- move-cursor screen, row, column { +next-character break-unless curr off-screen?:bool <- greater-or-equal row, screen-height break-if off-screen? # if we find old-before-cursor still on the new resized screen, update # editor.cursor-row and editor.cursor-column based on # old-before-cursor { at-cursor?:bool <- equal old-before-cursor, prev break-unless at-cursor? *editor <- put *editor, cursor-row:offset, row *editor <- put *editor, cursor-column:offset, column *editor <- put *editor, before-cursor:offset, old-before-cursor } c:char <- get *curr, value:offset { # newline? move to left rather than 0 newline?:bool <- equal c, 10/newline break-unless newline? # clear rest of line in this window clear-line-until screen, right # skip to next line row <- add row, 1 column <- copy left screen <- move-cursor screen, row, column curr <- next curr prev <- next prev loop +next-character } { # at right? wrap. even if there's only one more letter left; we need # room for clicking on the cursor after it. at-right?:bool <- equal column, right break-unless at-right? # print wrap icon wrap-icon:char <- copy 8617/loop-back-to-left print screen, wrap-icon, 245/grey column <- copy left row <- add row, 1 screen <- move-cursor screen, row, column # don't increment curr loop +next-character } print screen, c, color curr <- next curr prev <- next prev column <- add column, 1 loop } # save first character off-screen *editor <- put *editor, bottom-of-screen:offset, curr *editor <- put *editor, bottom:offset, row return row, column ] scenario point-at-multiple-editors [ local-scope trace-until 100/app # trace too long assume-screen 30/width, 5/height # initialize both halves of screen assume-resources [ [lesson/recipes.mu] <- [ |abc| ] ] env:&:environment <- new-programming-environment resources, screen, [def] # contents of sandbox editor # focus on both sides assume-console [ left-click 1, 1 left-click 1, 17 ] # check cursor column in each run [ event-loop screen, console, env, resources recipes:&:editor <- get *env, recipes:offset 5:num/raw <- get *recipes, cursor-column:offset sandbox:&:editor <- get *env, current-sandbox:offset 7:num/raw <- get *sandbox, cursor-column:offset ] memory-should-contain [ 5 <- 1 7 <- 17 ] ] scenario edit-multiple-editors [ local-scope trace-until 100/app # trace too long assume-screen 30/width, 5/height # initialize both halves of screen assume-resources [ [lesson/recipes.mu] <- [ |abc| ] ] env:&:environment <- new-programming-environment resources, screen, [def] # contents of sandbox render-all screen, env, render # type one letter in each of them assume-console [ left-click 1, 1 type [0] left-click 1, 17 type [1] ] run [ event-loop screen, console, env, resources recipes:&:editor <- get *env, recipes:offset 5:num/raw <- get *recipes, cursor-column:offset sandbox:&:editor <- get *env, current-sandbox:offset 7:num/raw <- get *sandbox, cursor-column:offset ] screen-should-contain [ . run (F4) . # this line has a different background, but we don't test that yet .a0bc ┊d1ef . . ┊──────────────. .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] memory-should-contain [ 5 <- 2 # cursor column of recipe editor 7 <- 18 # cursor column of sandbox editor ] # show the cursor at the right window run [ cursor:char <- copy 9251/␣ print screen, cursor ] screen-should-contain [ . run (F4) . .a0bc ┊d1␣f . . ┊──────────────. .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] ] scenario editor-in-focus-keeps-cursor [ local-scope trace-until 100/app # trace too long assume-screen 30/width, 5/height assume-resources [ [lesson/recipes.mu] <- [ |abc| ] ] env:&:environment <- new-programming-environment resources, screen, [def] render-all screen, env, render # ihlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<html>
	<head>
		<title>dwm - dynamic window manager</title>
		<meta name="author" content="Anselm R. Garbe">
		<meta name="generator" content="ed">
		<meta name="copyright" content="(C)opyright 2006 by Anselm R. Garbe">
		<style type="text/css">
			body {
				color: #000000;
				font-family: sans-serif;
				margin: 20px 20px 20px 20px;
			}
		</style>
	</head>
	<body>
		<center>
			<img src="dwm.png"/><br />
			<h3>dynamic window manager</h3>
		</center>
		<h3>Description</h3>
		<p>
		dwm is a dynamic window manager for X11.
		</p>
		<h4>Background</h4>
		<p>
		As founder and main developer of wmii I came to the conclusion that
		wmii is too clunky for my needs. I don't need so many funky features
		and all this hype about remote control through a 9P service, I only
		want to manage my windows in a simple, but dynamic way. wmii never got
		finished because I listened to users, who proposed arbitrary ideas I
		considered useful. This resulted in an extreme <a
		href="http://www.jwz.org/doc/cadt.html">CADT</a> development model,
		which was a mistake. Thus the philosophy of dwm is simply <i>to fit my
		needs</i> (maybe yours as well). That's it.
		</p>
		<h4>Differences to ion, larswm, and wmii</h4>
		<p>
		In contrast to ion, larswm, and wmii, dwm is much smaller, faster and simpler.
		</p>
		<ul>
			<li>
			dwm has no Lua integration, no 9P support, no editable
			tagbars, no shell-based configuration, no remote control, and comes
			without any additional tools like printing the selection or warping
			the mouse.
			</li>
			<li>
			dwm is only a single binary, it's source code is intended to never
			exceed 2000 SLOC.
			</li>
			<li>
			dwm is based on tagging and dynamic window management (however
			simpler than ion, wmii or larswm). It manages windows in
			tiling and floating modes. Either mode can be applied dynamically,
			depending on the application in use and the task performed.
			</li>
			<li>
			dwm don't distinguishes between layers, there is no floating or
			tiled layer. Wether the clients of currently selected tag are in
			tiled mode or not, you can re-arrange all clients on the fly.
			Popup- and fixed-size windows are treated floati