diff --git a/clients/terminal.c b/clients/terminal.c
index dfddb7f..d1349d8 100644
--- a/clients/terminal.c
+++ b/clients/terminal.c
@@ -715,14 +715,12 @@ terminal_resize(struct terminal *terminal, int width, int height)
 	if (terminal->width == width && terminal->height == height)
 		return;
 
-	if (!terminal->fullscreen) {
-		pixel_width = width *
-			terminal->extents.max_x_advance + 2 * terminal->margin;
-		pixel_height = height *
-			terminal->extents.height + 2 * terminal->margin;
-		window_set_child_size(terminal->window,
-				      pixel_width, pixel_height);
-	}
+	pixel_width = width *
+		terminal->extents.max_x_advance + 2 * terminal->margin;
+	pixel_height = height *
+		terminal->extents.height + 2 * terminal->margin;
+	window_set_child_size(terminal->window,
+			      pixel_width, pixel_height);
 
 	window_schedule_redraw (terminal->window);
 
@@ -2283,7 +2281,6 @@ terminal_create(struct display *display, int fullscreen)
 	terminal->display = display;
 	terminal->margin = 5;
 
-	window_set_fullscreen(terminal->window, terminal->fullscreen);
 	window_set_user_data(terminal->window, terminal);
 	window_set_redraw_handler(terminal->window, redraw_handler);
 	window_set_resize_handler(terminal->window, resize_handler);
@@ -2314,6 +2311,7 @@ terminal_create(struct display *display, int fullscreen)
 	cairo_surface_destroy(surface);
 
 	terminal_resize(terminal, 80, 24);
+	window_set_fullscreen(terminal->window, terminal->fullscreen);
 	terminal_draw(terminal);
 
 	return terminal;
diff --git a/clients/window.c b/clients/window.c
index da9055c..955db66 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -99,7 +99,6 @@ struct window {
 	struct input *keyboard_device;
 	uint32_t name;
 	enum window_buffer_type buffer_type;
-	int mapped;
 
 	EGLImageKHR *image;
 	cairo_surface_t *cairo_surface, *pending_surface;
@@ -599,15 +598,14 @@ window_attach_surface(struct window *window)
 	wl_surface_attach(window->surface, buffer, x, y);
 	wl_display_sync_callback(display->display, free_surface, window);
 
-	if (!window->mapped) {
-		if (!window->parent)
-			wl_surface_map_toplevel(window->surface);
-		else
-			wl_surface_map_transient(window->surface,
-						 window->parent->surface,
-						 window->x, window->y, 0);
-		window->mapped = 1;
-	}
+	if (window->fullscreen)
+		wl_surface_map_fullscreen(window->surface);
+	else if (!window->parent)
+		wl_surface_map_toplevel(window->surface);
+	else
+		wl_surface_map_transient(window->surface,
+					 window->parent->surface,
+					 window->x, window->y, 0);
 
 	wl_surface_damage(window->surface, 0, 0,
 			  window->allocation.width,
@@ -1086,10 +1084,10 @@ handle_configure(void *data, struct wl_shell *shell,
 	struct window *window = wl_surface_get_user_data(surface);
 	int32_t child_width, child_height;
 
-	/* FIXME this is probably the wrong place to check for width or
-	   height <= 0, but it prevents the compositor from crashing
-	*/
-	if(width <= 0 || height <= 0)
+	/* FIXME: this is probably the wrong place to check for width
+	 * or height <= 0, but it prevents the compositor from crashing
+	 */
+	if (width <= 0 || height <= 0)
 		return;
 
 	window->resize_edges = edges;
@@ -1118,7 +1116,7 @@ void
 window_get_child_allocation(struct window *window,
 			    struct rectangle *allocation)
 {
-	if (window->fullscreen && !window->decoration) {
+	if (window->fullscreen || !window->decoration) {
 		*allocation = window->allocation;
 	} else {
 		allocation->x = window->margin + 10;
@@ -1134,8 +1132,15 @@ void
 window_set_child_size(struct window *window, int32_t width, int32_t height)
 {
 	if (!window->fullscreen) {
+		window->allocation.x = 20 + window->margin;
+		window->allocation.y = 60 + window->margin;
 		window->allocation.width = width + 20 + window->margin * 2;
 		window->allocation.height = height + 60 + window->margin * 2;
+	} else {
+		window->allocation.x = 0;
+		window->allocation.y = 0;
+		window->allocation.width = width;
+		window->allocation.height = height;
 	}
 }
 
@@ -1163,13 +1168,22 @@ window_schedule_redraw(struct window *window)
 void
 window_set_fullscreen(struct window *window, int fullscreen)
 {
+	int32_t width, height;
+
+	if (window->fullscreen == fullscreen)
+		return;
+
 	window->fullscreen = fullscreen;
 	if (window->fullscreen) {
 		window->saved_allocation = window->allocation;
-		window->allocation = window->display->screen_allocation;
+		width = window->display->screen_allocation.width;
+		height = window->display->screen_allocation.height;
 	} else {
-		window->allocation = window->saved_allocation;
+		width = window->saved_allocation.width - 20 - window->margin * 2;
+		height = window->saved_allocation.height - 60 - window->margin * 2;
 	}
+
+	(*window->resize_handler)(window, width, height, window->user_data);
 }
 
 void
diff --git a/compositor/compositor.c b/compositor/compositor.c
index b6cdbe6..1d6aa24 100644
--- a/compositor/compositor.c
+++ b/compositor/compositor.c
@@ -131,7 +131,7 @@ wlsc_surface_create(struct wlsc_compositor *compositor,
 
 	wl_list_init(&surface->surface.destroy_listener_list);
 	wl_list_init(&surface->link);
-	surface->mapped = 0;
+	surface->map_type = WLSC_SURFACE_MAP_UNMAPPED;
 
 	glGenTextures(1, &surface->texture);
 	glBindTexture(GL_TEXTURE_2D, surface->texture);
@@ -403,13 +403,19 @@ wlsc_output_repaint(struct wlsc_output *output)
 
 	glViewport(0, 0, output->width, output->height);
 
-	if (output->background)
-		wlsc_surface_draw(output->background, output);
-	else
+	if (output->fullscreen_surface) {
+		glClearColor(0.4, 0, 0, 1);
 		glClear(GL_COLOR_BUFFER_BIT);
+		wlsc_surface_draw(output->fullscreen_surface, output);
+	} else {
+		if (output->background)
+			wlsc_surface_draw(output->background, output);
+		else
+			glClear(GL_COLOR_BUFFER_BIT);
 
-	wl_list_for_each_reverse(es, &ec->surface_list, link)
-		wlsc_surface_draw(es, output);
+		wl_list_for_each_reverse(es, &ec->surface_list, link)
+			wlsc_surface_draw(es, output);
+	}
 
 	if (ec->focus)
 		wl_list_for_each(eid, &ec->input_device_list, link)
@@ -474,17 +480,31 @@ surface_map_toplevel(struct wl_client *client,
 		     struct wl_surface *surface)
 {
 	struct wlsc_surface *es = (struct wlsc_surface *) surface;
+	struct wlsc_output *output;
 
-	if (es->mapped)
+	switch (es->map_type) {
+	case WLSC_SURFACE_MAP_UNMAPPED:
+		es->x = 10 + random() % 400;
+		es->y = 10 + random() % 400;
+		wlsc_surface_update_matrix(es);
+		wl_list_insert(&es->compositor->surface_list, &es->link);
+		break;
+	case WLSC_SURFACE_MAP_TOPLEVEL:
 		return;
+	default:
+		break;
+	}
 
-	es->x = 10 + random() % 400;
-	es->y = 10 + random() % 400;
+	wl_list_for_each(output, &es->compositor->output_list, link)
+		if (output->fullscreen_surface == es) {
+			output->fullscreen_surface = NULL;
+			es->x = es->saved_x;
+			es->y = es->saved_y;
+			wlsc_surface_update_matrix(es);
+		}
 
-	wlsc_surface_update_matrix(es);
-	wl_list_insert(&es->compositor->surface_list, &es->link);
 	wlsc_compositor_schedule_repaint(es->compositor);
-	es->mapped = 1;
+	es->map_type = WLSC_SURFACE_MAP_TOPLEVEL;
 }
 
 static void
@@ -494,17 +514,59 @@ surface_map_transient(struct wl_client *client,
 {
 	struct wlsc_surface *es = (struct wlsc_surface *) surface;
 	struct wlsc_surface *pes = (struct wlsc_surface *) parent;
+	struct wlsc_output *output;
 
-	if (es->mapped)
-		return;
+	switch (es->map_type) {
+	case WLSC_SURFACE_MAP_UNMAPPED:
+		wl_list_insert(&es->compositor->surface_list, &es->link);
+		break;
+	default:
+		break;
+	}
 
 	es->x = pes->x + x;
 	es->y = pes->y + y;
 
+	wl_list_for_each(output, &es->compositor->output_list, link)
+		if (output->fullscreen_surface == es)
+			output->fullscreen_surface = NULL;
+
+	wlsc_surface_update_matrix(es);
+	wlsc_compositor_schedule_repaint(es->compositor);
+	es->map_type = WLSC_SURFACE_MAP_TRANSIENT;
+}
+
+static void
+surface_map_fullscreen(struct wl_client *client, struct wl_surface *surface)
+{
+	struct wlsc_surface *es = (struct wlsc_surface *) surface;
+	struct wlsc_output *output;
+
+	switch (es->map_type) {
+	case WLSC_SURFACE_MAP_UNMAPPED:
+		es->x = 10 + random() % 400;
+		es->y = 10 + random() % 400;
+		wl_list_insert(&es->compositor->surface_list, &es->link);
+		break;
+	case WLSC_SURFACE_MAP_FULLSCREEN:
+		return;
+	default:
+		break;
+	}
+
+	/* FIXME: Fullscreen on first output */
+	/* FIXME: Handle surface going away */
+	output = container_of(es->compositor->output_list.next,
+			      struct wlsc_output, link);
+
+	es->saved_x = es->x;
+	es->saved_y = es->y;
+	es->x = (output->width - es->width) / 2;
+	es->y = (output->height - es->height) / 2;
+	output->fullscreen_surface = es;
 	wlsc_surface_update_matrix(es);
-	wl_list_insert(&es->compositor->surface_list, &es->link);
 	wlsc_compositor_schedule_repaint(es->compositor);
-	es->mapped = 1;
+	es->map_type = WLSC_SURFACE_MAP_FULLSCREEN;
 }
 
 static void
@@ -523,6 +585,7 @@ const static struct wl_surface_interface surface_interface = {
 	surface_attach,
 	surface_map_toplevel,
 	surface_map_transient,
+	surface_map_fullscreen,
 	surface_damage
 };
 
diff --git a/compositor/compositor.h b/compositor/compositor.h
index 3418b53..bc3b9f7 100644
--- a/compositor/compositor.h
+++ b/compositor/compositor.h
@@ -47,6 +47,7 @@ struct wlsc_output {
 	struct wlsc_surface *background;
 	struct wlsc_matrix matrix;
 	int32_t x, y, width, height;
+	struct wlsc_surface *fullscreen_surface;
 };
 
 enum wlsc_pointer_type {
@@ -130,17 +131,25 @@ struct wlsc_vector {
 	GLfloat f[4];
 };
 
+enum wlsc_surface_map_type {
+	WLSC_SURFACE_MAP_UNMAPPED,
+	WLSC_SURFACE_MAP_TOPLEVEL,
+	WLSC_SURFACE_MAP_TRANSIENT,
+	WLSC_SURFACE_MAP_FULLSCREEN
+};
+
 struct wlsc_surface {
 	struct wl_surface surface;
 	struct wlsc_compositor *compositor;
 	GLuint texture;
 	int32_t x, y, width, height;
+	int32_t saved_x, saved_y;
 	struct wl_list link;
 	struct wlsc_matrix matrix;
 	struct wlsc_matrix matrix_inv;
 	struct wl_visual *visual;
 	struct wl_buffer *buffer;
-	int mapped;
+	enum wlsc_surface_map_type map_type;
 };
 
 void
diff --git a/compositor/shell.c b/compositor/shell.c
index 2699b22..1ff32e9 100644
--- a/compositor/shell.c
+++ b/compositor/shell.c
@@ -80,6 +80,8 @@ shell_move(struct wl_client *client, struct wl_shell *shell,
 	struct wlsc_surface *es = (struct wlsc_surface *) surface;
 	struct wlsc_move_grab *move;
 
+	/* FIXME: Reject if fullscreen */
+
 	move = malloc(sizeof *move);
 	if (!move) {
 		wl_client_post_no_memory(client);
@@ -174,6 +176,8 @@ shell_resize(struct wl_client *client, struct wl_shell *shell,
 	enum wlsc_pointer_type pointer = WLSC_POINTER_LEFT_PTR;
 	struct wlsc_surface *es = (struct wlsc_surface *) surface;
 
+	/* FIXME: Reject if fullscreen */
+
 	resize = malloc(sizeof *resize);
 	if (!resize) {
 		wl_client_post_no_memory(client);
diff --git a/protocol/wayland.xml b/protocol/wayland.xml
index 13c71bd..a9f96b6 100644
--- a/protocol/wayland.xml
+++ b/protocol/wayland.xml
@@ -368,6 +368,16 @@
       <arg name="flags" type="uint"/>
     </request>
 
+    <!-- Map the surface as a fullscreen surface.  There are a number
+         of options here: on which output? if the surface size doesn't
+         match the output size, do we scale, change resolution, or add
+         black borders? is that something the client controls?  what
+         about transient surfaces, do they float on top of the
+         fullscreen? what if there's already a fullscreen surface on
+         the output, maybe you can only go fullscreen if you're
+         active?  -->
+    <request name="map_fullscreen"/>
+
     <!-- Notify the server that the attached buffer's contents have
          changed, and request a redraw. The arguments allow you to
          damage only a part of the surface, but the server may ignore
