[PATCH v4 4/4] add detailed implementation for fullscreen support

juan.j.zhao at linux.intel.com juan.j.zhao at linux.intel.com
Mon Jan 16 08:47:57 PST 2012


From: Juan Zhao <juan.j.zhao at linux.intel.com>

Signed-off-by: Juan Zhao <juan.j.zhao at linux.intel.com>
Signed-off-by: Zhiwen Wu <zhiwen.wu at linux.intel.com>
---
 src/compositor-drm.c |  162 +++++++++++++++++++++++++++++++++++++++
 src/compositor.c     |  206 ++++++++++++++++++++++++++++++++++++++++++--------
 src/shell.c          |   11 +++
 3 files changed, 348 insertions(+), 31 deletions(-)

diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index 8f6c8fc..3048fd7 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -78,6 +78,23 @@ struct drm_output {
 	uint32_t pending_fs_surf_fb_id;
 };
 
+static struct drm_mode *
+choose_mode (struct drm_output *output, int32_t width, int32_t height, uint32_t refresh)
+{
+	struct drm_mode *tmp_mode = NULL, *mode, *next;
+
+	wl_list_for_each_safe(mode, next, &output->base.mode_list,base.link) {
+		if (mode->mode_info.hdisplay == width && mode->mode_info.vdisplay == height) {
+			if (mode->mode_info.vrefresh == refresh)
+				return mode;
+			else if (!tmp_mode)
+				tmp_mode = mode;
+		}
+	}
+
+	return tmp_mode;
+}
+
 static int
 drm_output_prepare_render(struct weston_output *output_base)
 {
@@ -258,6 +275,147 @@ out:
 	return ret;
 }
 
+static int
+drm_output_set_mode (struct weston_output *output_base, int32_t width, int32_t height, uint32_t refresh)
+{
+	struct drm_output *output;
+	struct drm_mode *drm_mode;
+	int i, ret;
+	unsigned handle, stride;
+	struct drm_compositor *ec;
+
+	if (output_base == NULL) {
+		fprintf(stderr, "output is NULL.\n");
+		return -1;
+	}
+
+	ec = (struct drm_compositor *)output_base->compositor;
+	output = (struct drm_output *)output_base;
+	drm_mode  = (struct drm_mode *)(output->base.current);
+
+	printf ("current mode: %s, w=%d, h=%d, refresh=%u, flags=%u\n",
+			drm_mode->mode_info.name, drm_mode->base.width,
+			drm_mode->base.height, drm_mode->base.refresh,
+			drm_mode->base.flags);
+
+	drm_mode  = choose_mode (output, width, height, refresh);
+
+	if (!drm_mode) {
+		printf ("%s, invalid resolution:%dx%d\n", __FUNCTION__, width, height);
+		return -1;
+	}
+
+	printf ("next mode: %s, w=%d, h=%d, refresh=%u, flags=%u\n",
+			drm_mode->mode_info.name, drm_mode->base.width,
+			drm_mode->base.height, drm_mode->base.refresh,
+			drm_mode->base.flags);
+
+
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+				  GL_COLOR_ATTACHMENT0,
+				  GL_RENDERBUFFER, 0);
+
+
+	/* Destroy output buffers */
+	for (i = 0; i < 2; i++) {
+		drmModeRmFB(ec->drm.fd, output->fb_id[i]);
+		ec->base.destroy_image(ec->base.display, output->image[i]);
+		gbm_bo_destroy(output->bo[i]);
+	}
+
+	glBindRenderbuffer(GL_RENDERBUFFER, 0);
+	glDeleteRenderbuffers(2, output->rbo);
+	weston_output_destroy(output_base);
+
+	output->base.current = &drm_mode->base;
+	drm_mode->base.flags =
+		WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+	/* create new output buffers */
+	glGenRenderbuffers(2, output->rbo);
+	for (i = 0; i < 2; i++) {
+		glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]);
+
+		output->bo[i] =
+		gbm_bo_create(ec->gbm,
+			      output->base.current->width,
+			      output->base.current->height,
+			      GBM_BO_FORMAT_XRGB8888,
+			      GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+
+		if (!output->bo[i])
+			goto err_bufs;
+
+		output->image[i] = ec->base.create_image(ec->base.display,
+							 NULL,
+							 EGL_NATIVE_PIXMAP_KHR,
+							 output->bo[i], NULL);
+		if (!output->image[i])
+			goto err_bufs;
+
+		ec->base.image_target_renderbuffer_storage(GL_RENDERBUFFER,
+							   output->image[i]);
+		stride = gbm_bo_get_pitch(output->bo[i]);
+		handle = gbm_bo_get_handle(output->bo[i]).u32;
+
+		ret = drmModeAddFB(ec->drm.fd,
+				   output->base.current->width,
+				   output->base.current->height,
+				   24, 32, stride, handle, &output->fb_id[i]);
+
+		if (ret) {
+			fprintf(stderr, "failed to add fb %d: %m\n", i);
+			goto err_bufs;
+		}
+	}
+
+	output->current = 0;
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+				  GL_COLOR_ATTACHMENT0,
+				  GL_RENDERBUFFER,
+				  output->rbo[output->current]);
+
+	ret = drmModeSetCrtc(ec->drm.fd,
+			     output->crtc_id,
+			     output->fb_id[output->current ^ 1], 0, 0,
+			     &output->connector_id, 1, &drm_mode->mode_info);
+
+	if (ret) {
+		fprintf(stderr, "failed to set mode: %m\n");
+		goto err_fb;
+	}
+
+	output->base.x = 0;
+	output->base.y = 0;
+	output->base.mm_width = output->base.current->width;
+	output->base.mm_height = output->base.current->height;
+	weston_output_move(&output->base, 0, 0);
+
+	printf ("%s: ret 0\n", __FUNCTION__);
+	return 0;
+
+err_fb:
+	glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+				  GL_COLOR_ATTACHMENT0,
+				  GL_RENDERBUFFER, 0);
+err_bufs:
+	for (i = 0; i < 2; i++) {
+		if (output->fb_id[i] != -1)
+			drmModeRmFB(ec->drm.fd, output->fb_id[i]);
+		if (output->image[i])
+			ec->base.destroy_image(ec->base.display,
+					       output->image[i]);
+		if (output->bo[i])
+			gbm_bo_destroy(output->bo[i]);
+	}
+	glBindRenderbuffer(GL_RENDERBUFFER, 0);
+	glDeleteRenderbuffers(2, output->rbo);
+
+	printf ("%s: ret -1\n", __FUNCTION__);
+	return -1;
+}
+
+
 static void
 drm_output_destroy(struct weston_output *output_base)
 {
@@ -488,7 +646,10 @@ create_output_for_connector(struct drm_compositor *ec,
 
 	drm_mode = container_of(output->base.mode_list.next,
 				struct drm_mode, base.link);
+
 	output->base.current = &drm_mode->base;
+	output->base.saved_mode = NULL;
+	output->base.fs_dirty = NULL;
 	drm_mode->base.flags =
 		WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
 
@@ -553,6 +714,7 @@ create_output_for_connector(struct drm_compositor *ec,
 	output->base.prepare_scanout_surface =
 		drm_output_prepare_scanout_surface;
 	output->base.set_hardware_cursor = drm_output_set_cursor;
+	output->base.set_mode = drm_output_set_mode;
 	output->base.destroy = drm_output_destroy;
 
 	return 0;
diff --git a/src/compositor.c b/src/compositor.c
index 1f0cb2d..3529ca1 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -75,6 +75,29 @@ sigchld_handler(int signal_number, void *data)
 	return 1;
 }
 
+static int
+weston_output_restore_mode (struct weston_output *output)
+{
+	int ret;
+
+	if(!output->saved_mode ||
+	    (output->saved_mode->width == output->current->width &&
+	     output->saved_mode->height == output->current->height))
+		return 0;
+
+	if (output->set_mode) {
+		if ((ret = output->set_mode(output, output->saved_mode->width, output->saved_mode->height, output->saved_mode->refresh)) == 0)
+		printf("set mode '%dx%d', ret %d\n",  output->saved_mode->width, output->saved_mode->height, ret);
+			output->saved_mode = NULL;
+	}
+	else {
+		fprintf(stderr, "Can't restore display mode\n");
+		ret = -1;
+	}
+
+	return ret;
+}
+
 WL_EXPORT void
 weston_watch_process(struct weston_process *process)
 {
@@ -229,8 +252,11 @@ weston_surface_create(struct weston_compositor *compositor,
 	surface->alpha = 255;
 
 	surface->fs_support.fullscreen_output = NULL;
+	surface->fs_support.fs_method = WESTON_SURFACE_FULLSCREEN_DEFAULT;
+	surface->fs_support.fs_transform = NULL;
 	surface->buffer = NULL;
 	surface->output = NULL;
+	surface->type = WESTON_SURFACE_TYPE_GENERAL;
 
 	pixman_region32_init(&surface->damage);
 	pixman_region32_init(&surface->opaque);
@@ -321,7 +347,65 @@ int
 weston_surface_set_fullscreen( struct weston_output *output,
 			     struct weston_surface *surface)
 {
-	/*do the real set fullscreen*/
+	struct weston_transform *fs_transform=surface->fs_support.fs_transform;
+
+	switch(surface->fs_support.fs_method) {
+		case WESTON_SURFACE_FULLSCREEN_DEFAULT:
+			output->fs_dirty = 1;
+			weston_surface_center_on_output(surface, output);
+			weston_surface_damage(surface);
+			break;
+		case WESTON_SURFACE_FULLSCREEN_SCALE:
+			if(output->current->width == surface->width &&
+			    output->current->height == surface->height)
+				return 0;
+			output->fs_dirty = 1;
+			/*do not forgot to release them, Juan*/
+			if(!fs_transform) {
+				fs_transform = malloc(sizeof *fs_transform);
+				surface->fs_support.fs_transform = fs_transform;
+			}
+			weston_matrix_init(&fs_transform->matrix);
+			/*preserve aspect ratio*/
+			weston_matrix_scale(&fs_transform->matrix,
+					  (float)output->current->width/(float)surface->width,
+					  (float)output->current->width/(float)surface->width,
+					  1.0);
+			weston_surface_damage(surface);
+			break;
+		case WESTON_SURFACE_FULLSCREEN_DRIVER:
+			if(output->current->width == surface->width &&
+			    output->current->height == surface->height)
+				return 0;
+			output->saved_mode = output->current;
+			uint32_t refresh = surface->fs_support.framerate;
+			if (!output->set_mode) {
+				output->saved_mode = NULL;
+				fprintf(stderr,
+					"set_mode not implemented,   \
+					 fallback to FILL method\n");
+			} else if(output->set_mode(output, surface->width, surface->height, refresh) == 0) {
+				weston_surface_damage(surface);
+				break;
+			} else {
+				/*did not find the mode, fall back */
+				output->current = output->saved_mode;
+				output->saved_mode = NULL;
+				fprintf(stderr,
+					"failed to set mode,         \
+					 fallback to FILL method\n");
+			}
+		case WESTON_SURFACE_FULLSCREEN_FILL:
+			/*make it on the center at first*/
+			output->fs_dirty = 1;
+			weston_surface_center_on_output(surface, output);
+			weston_surface_damage(surface);
+			break;
+		default:
+			break;
+	}
+
+	return 0;
 }
 
 static void
@@ -509,7 +593,7 @@ texture_region(struct weston_surface *es, pixman_region32_t *region)
 
 static void
 transform_vertex(struct weston_surface *surface,
-		 GLfloat x, GLfloat y, GLfloat u, GLfloat v, GLfloat *r)
+		 GLfloat x, GLfloat y, GLfloat u, GLfloat v, GLfloat *r, uint32_t fs_scale)
 {
 	struct weston_vector t;
 
@@ -518,7 +602,11 @@ transform_vertex(struct weston_surface *surface,
 	t.f[2] = 0.0;
 	t.f[3] = 1.0;
 
-	weston_matrix_transform(&surface->transform->matrix, &t);
+	if(!fs_scale)
+		weston_matrix_transform(&surface->transform->matrix, &t);
+	else
+		weston_matrix_transform(&surface->fs_support.fs_transform->matrix,
+				      &t);
 
 	r[ 0] = t.f[0];
 	r[ 1] = t.f[1];
@@ -527,7 +615,7 @@ transform_vertex(struct weston_surface *surface,
 }
 
 static int
-texture_transformed_surface(struct weston_surface *es)
+texture_transformed_surface(struct weston_surface *es, uint32_t fs_scale)
 {
 	struct weston_compositor *ec = es->compositor;
 	GLfloat *v;
@@ -536,11 +624,11 @@ texture_transformed_surface(struct weston_surface *es)
 	v = wl_array_add(&ec->vertices, 16 * sizeof *v);
 	p = wl_array_add(&ec->indices, 6 * sizeof *p);
 
-	transform_vertex(es, es->x, es->y, 0.0, 0.0, &v[0]);
-	transform_vertex(es, es->x, es->y + es->height, 0.0, 1.0, &v[4]);
-	transform_vertex(es, es->x + es->width, es->y, 1.0, 0.0, &v[8]);
+	transform_vertex(es, es->x, es->y, 0.0, 0.0, &v[0], fs_scale);
+	transform_vertex(es, es->x, es->y + es->height, 0.0, 1.0, &v[4], fs_scale);
+	transform_vertex(es, es->x + es->width, es->y, 1.0, 0.0, &v[8], fs_scale);
 	transform_vertex(es, es->x + es->width, es->y + es->height,
-			 1.0, 1.0, &v[12]);
+			 1.0, 1.0, &v[12], fs_scale);
 
 	p[0] = 0;
 	p[1] = 1;
@@ -565,7 +653,7 @@ weston_surface_draw(struct weston_surface *es,
 	pixman_region32_init_rect(&repaint,
 				  es->x, es->y, es->width, es->height);
 	pixman_region32_intersect(&repaint, &repaint, clip);
-	if (!pixman_region32_not_empty(&repaint))
+	if (!pixman_region32_not_empty(&repaint) || !ec)
 		return;
 
 	switch (es->visual) {
@@ -587,14 +675,18 @@ weston_surface_draw(struct weston_surface *es,
 		ec->current_alpha = es->alpha;
 	}
 
-	if (es->transform == NULL) {
+	if ((es->transform == NULL) && (es->fs_support.fs_transform == NULL)) {
 		filter = GL_NEAREST;
 		n = texture_region(es, &repaint);
+	} else if (es->fs_support.fs_transform) {
+		filter = GL_LINEAR;
+		n = texture_transformed_surface(es, 1);
 	} else {
 		filter = GL_LINEAR;
-		n = texture_transformed_surface(es);
+		n = texture_transformed_surface(es, 0);
 	}
 
+
 	glBindTexture(GL_TEXTURE_2D, es->texture);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
@@ -696,6 +788,11 @@ fade_output(struct weston_output *output,
 	surface.height = output->current->height;
 	surface.texture = GL_NONE;
 	surface.transform = NULL;
+	surface.fs_support.fullscreen_output = NULL;
+	surface.fs_support.fs_method = WESTON_SURFACE_FULLSCREEN_DEFAULT;
+	surface.fs_support.fs_transform = NULL;
+	surface.type = WESTON_SURFACE_TYPE_GENERAL;
+
 	surface.alpha = compositor->current_alpha;
 
 	if (tint <= 1.0)
@@ -781,13 +878,11 @@ static void
 weston_output_repaint(struct weston_output *output)
 {
 	struct weston_compositor *ec = output->compositor;
-	struct weston_surface *es;
+	struct weston_surface *es, *tmp_es = NULL, *fs_es = NULL;
 	pixman_region32_t opaque, new_damage, total_damage, repaint;
 
 	output->prepare_render(output);
 
-	glViewport(0, 0, output->current->width, output->current->height);
-
 	glUseProgram(ec->texture_shader.program);
 	glUniformMatrix4fv(ec->texture_shader.proj_uniform,
 			   1, GL_FALSE, output->matrix.d);
@@ -814,31 +909,79 @@ weston_output_repaint(struct weston_output *output)
 	pixman_region32_fini(&opaque);
 	pixman_region32_fini(&new_damage);
 
+
 	es = container_of(ec->surface_list.next, struct weston_surface, link);
 
 	if (setup_scanout_surface(output, es) == 0)
 		/* We're drawing nothing, just let the damage accumulate */
 		return;
 
-	if (es->fs_support.fullscreen_output == output) {
-		if (es->width < output->current->width ||
-		    es->height < output->current->height)
-			glClear(GL_COLOR_BUFFER_BIT);
-		weston_surface_draw(es, output, &total_damage);
-	} else {
-		wl_list_for_each(es, &ec->surface_list, link) {
-			pixman_region32_copy(&es->damage, &total_damage);
-			pixman_region32_subtract(&total_damage, &total_damage, &es->opaque);
+	wl_list_for_each(tmp_es, &ec->surface_list, link) {
+		if ( tmp_es->type == WESTON_SURFACE_TYPE_GENERAL ) {
+			if( tmp_es->fs_support.fullscreen_output == output ){
+				fs_es = tmp_es;
+			}
+			break;
 		}
+	}
 
-		wl_list_for_each_reverse(es, &ec->surface_list, link) {
-			pixman_region32_init(&repaint);
-			pixman_region32_intersect(&repaint, &output->region,
-						  &es->damage);
-			weston_surface_draw(es, output, &repaint);
-			pixman_region32_subtract(&es->damage,
-						 &es->damage, &output->region);
-			pixman_region32_fini(&repaint);
+	if (fs_es) {
+		if (fs_es->width != output->current->width ||
+		    fs_es->height != output->current->height) {
+			weston_surface_set_fullscreen(output, fs_es);
+			output->prepare_render(output);
+			glViewport( 0, 0, (GLint) output->current->width,
+				   (GLint) output->current->height);
+			glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+		}
+
+		/* draw all the surface above fullscreen es except panel */
+		wl_list_for_each_reverse(tmp_es, fs_es->link.next, link) {
+			if (tmp_es->type != WESTON_SURFACE_TYPE_PANEL) {
+				weston_surface_draw(tmp_es, output, &total_damage);
+			}
+			if (tmp_es == es)
+				break;
+		}
+	} else {
+		if( output->saved_mode &&
+		    (output->saved_mode->width!=output->current->width ||
+		     output->saved_mode->height!=output->current->height) ) {
+			weston_output_restore_mode(output);
+			output->saved_mode = NULL;
+			output->fs_dirty = 0;
+			output->prepare_render(output);
+			glViewport( 0, 0,
+				    (GLint) output->current->width,
+				    (GLint) output->current->height);
+			glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+			weston_output_damage(output);
+			weston_output_repaint(output);
+		} else if (output->fs_dirty) {
+			output->fs_dirty = 0;
+			weston_output_damage(output);
+			weston_output_repaint(output);
+		} else {
+			glViewport(0, 0,
+				   output->current->width,
+				   output->current->height);
+			wl_list_for_each(es, &ec->surface_list, link) {
+				pixman_region32_copy(&es->damage,
+						     &total_damage);
+				pixman_region32_subtract(&total_damage,
+							 &total_damage,
+							 &es->opaque);
+			}
+			wl_list_for_each_reverse(es, &ec->surface_list, link) {
+				pixman_region32_init(&repaint);
+				pixman_region32_intersect(&repaint,
+							  &output->region,
+							  &es->damage);
+				weston_surface_draw(es, output, &repaint);
+				pixman_region32_subtract(&es->damage,
+							 &es->damage,
+							 &output->region);
+			}
 		}
 	}
 
@@ -1563,6 +1706,7 @@ input_device_attach(struct wl_client *client,
 			weston_surface_create(compositor,
 					    device->input_device.x,
 					    device->input_device.y, 32, 32);
+		device->sprite->type = WESTON_SURFACE_TYPE_CURSOR;
 		wl_list_init(&device->sprite->link);
 	}
 
diff --git a/src/shell.c b/src/shell.c
index a131ae5..f57f660 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -331,6 +331,10 @@ reset_shell_surface_type(struct shell_surface *surface)
 		surface->surface->x = surface->saved_x;
 		surface->surface->y = surface->saved_y;
 		surface->surface->fs_support.fullscreen_output = NULL;
+		if(surface->surface->fs_support.fs_transform) {
+			free(surface->surface->fs_support.fs_transform);
+			surface->surface->fs_support.fs_transform = NULL;
+		}
 		break;
 	case SHELL_SURFACE_PANEL:
 	case SHELL_SURFACE_BACKGROUND:
@@ -345,6 +349,10 @@ reset_shell_surface_type(struct shell_surface *surface)
 		return -1;
 	case SHELL_SURFACE_NONE:
 	case SHELL_SURFACE_TOPLEVEL:
+		if(surface->saved_x || surface->saved_y) {
+			surface->surface->x = surface->saved_x;
+			surface->surface->y = surface->saved_y;
+		}
 	case SHELL_SURFACE_TRANSIENT:
 	case SHELL_SURFACE_POPUP:
 		break;
@@ -366,6 +374,8 @@ shell_surface_set_toplevel(struct wl_client *client,
 
 	weston_surface_damage(surface->surface);
 	surface->type = SHELL_SURFACE_TOPLEVEL;
+	if(surface->surface->output)
+		surface->surface->output->fs_dirty = 1;
 }
 
 static void
@@ -780,6 +790,7 @@ desktop_shell_set_panel(struct wl_client *client,
 	}
 
 	shsurf->type = SHELL_SURFACE_PANEL;
+	surface->type = WESTON_SURFACE_TYPE_PANEL;
 	shsurf->output = output_resource->data;
 
 	wl_list_insert(&shell->panels, &shsurf->link);
-- 
1.7.5.4



More information about the wayland-devel mailing list