diff --git a/clients/screenshot.c b/clients/screenshot.c index 574f525c..b6ca48e6 100644 --- a/clients/screenshot.c +++ b/clients/screenshot.c @@ -45,8 +45,24 @@ static int output_width, output_height; static void display_handle_geometry(void *data, - struct wl_output *output, - int32_t x, int32_t y, int32_t width, int32_t height) + struct wl_output *wl_output, + int x, + int y, + int physical_width, + int physical_height, + int subpixel, + const char *make, + const char *model) +{ +} + +static void +display_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int width, + int height, + int refresh) { output_width = width; output_height = height; @@ -54,6 +70,7 @@ display_handle_geometry(void *data, static const struct wl_output_listener output_listener = { display_handle_geometry, + display_handle_mode }; static void diff --git a/clients/window.c b/clients/window.c index 612e5b3a..4ef7b9e3 100644 --- a/clients/window.c +++ b/clients/window.c @@ -1573,19 +1573,37 @@ static const struct wl_compositor_listener compositor_listener = { static void display_handle_geometry(void *data, - struct wl_output *output, - int32_t x, int32_t y, int32_t width, int32_t height) + struct wl_output *wl_output, + int x, int y, + int physical_width, + int physical_height, + int subpixel, + const char *make, + const char *model) { struct display *display = data; display->screen_allocation.x = x; display->screen_allocation.y = y; +} + +static void +display_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int width, + int height, + int refresh) +{ + struct display *display = data; + display->screen_allocation.width = width; display->screen_allocation.height = height; } static const struct wl_output_listener output_listener = { display_handle_geometry, + display_handle_mode }; static void diff --git a/compositor/compositor-drm.c b/compositor/compositor-drm.c index 9fc5b49e..6e9de4af 100644 --- a/compositor/compositor-drm.c +++ b/compositor/compositor-drm.c @@ -49,10 +49,14 @@ struct drm_compositor { PFNEGLEXPORTDRMIMAGEMESA export_drm_image; }; +struct drm_mode { + struct wlsc_mode base; + drmModeModeInfo mode_info; +}; + struct drm_output { struct wlsc_output base; - drmModeModeInfo mode; uint32_t crtc_id; uint32_t connector_id; GLuint rbo[2]; @@ -146,8 +150,8 @@ drm_output_prepare_scanout_surface(struct wlsc_output *output_base, if (es->x != output->base.x || es->y != output->base.y || - es->width != output->base.width || - es->height != output->base.height || + es->width != output->base.current->width || + es->height != output->base.current->height || es->image == EGL_NO_IMAGE_KHR) return -1; @@ -158,7 +162,8 @@ drm_output_prepare_scanout_surface(struct wlsc_output *output_base, return -1; ret = drmModeAddFB(c->drm.fd, - output->base.width, output->base.height, + output->base.current->width, + output->base.current->height, 32, 32, stride, handle, &fb_id); if (ret) @@ -192,7 +197,8 @@ drm_output_set_cursor(struct wlsc_output *output_base, pixman_region32_intersect_rect(&cursor_region, &cursor_region, output->base.x, output->base.y, - output->base.width, output->base.height); + output->base.current->width, + output->base.current->height); if (!pixman_region32_not_empty(&cursor_region)) { ret = 0; @@ -319,6 +325,46 @@ static drmModeModeInfo builtin_1024x768 = { "1024x768" }; + +static int +drm_output_add_mode(struct drm_output *output, drmModeModeInfo *info) +{ + struct drm_mode *mode; + + mode = malloc(sizeof *mode); + if (mode == NULL) + return -1; + + mode->base.flags = 0; + mode->base.width = info->hdisplay; + mode->base.height = info->vdisplay; + mode->base.refresh = info->vrefresh; + mode->mode_info = *info; + wl_list_insert(output->base.mode_list.prev, &mode->base.link); + + return 0; +} + +static int +drm_subpixel_to_wayland(int drm_value) +{ + switch (drm_value) { + default: + case DRM_MODE_SUBPIXEL_UNKNOWN: + return WL_OUTPUT_SUBPIXEL_UNKNOWN; + case DRM_MODE_SUBPIXEL_NONE: + return WL_OUTPUT_SUBPIXEL_NONE; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; + } +} + static int create_output_for_connector(struct drm_compositor *ec, drmModeRes *resources, @@ -326,8 +372,8 @@ create_output_for_connector(struct drm_compositor *ec, int x, int y) { struct drm_output *output; + struct drm_mode *drm_mode; drmModeEncoder *encoder; - drmModeModeInfo *mode; int i, ret; EGLint handle, stride, attribs[] = { EGL_WIDTH, 0, @@ -337,15 +383,6 @@ create_output_for_connector(struct drm_compositor *ec, EGL_NONE }; - output = malloc(sizeof *output); - if (output == NULL) - return -1; - - if (connector->count_modes > 0) - mode = &connector->modes[0]; - else - mode = &builtin_1024x768; - encoder = drmModeGetEncoder(ec->drm.fd, connector->encoders[0]); if (encoder == NULL) { fprintf(stderr, "No encoder for connector.\n"); @@ -362,16 +399,39 @@ create_output_for_connector(struct drm_compositor *ec, return -1; } + output = malloc(sizeof *output); + if (output == NULL) + return -1; + memset(output, 0, sizeof *output); - wlsc_output_init(&output->base, &ec->base, x, y, - mode->hdisplay, mode->vdisplay, 0); + output->base.x = x; + output->base.y = y; + output->base.mm_width = connector->mmWidth; + output->base.mm_height = connector->mmHeight; + output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel); + output->base.make = "unknown"; + output->base.model = "unknown"; + wl_list_init(&output->base.mode_list); output->crtc_id = resources->crtcs[i]; ec->crtc_allocator |= (1 << output->crtc_id); - output->connector_id = connector->connector_id; ec->connector_allocator |= (1 << output->connector_id); - output->mode = *mode; + + for (i = 0; i < connector->count_modes; i++) + drm_output_add_mode(output, &connector->modes[i]); + if (connector->count_modes == 0) + drm_output_add_mode(output, &builtin_1024x768); + + drm_mode = container_of(output->base.mode_list.next, + struct drm_mode, base.link); + output->base.current = &drm_mode->base; + drm_mode->base.flags = + WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; + + wlsc_output_init(&output->base, &ec->base, x, y, 200, 100, 0); + + wl_list_insert(ec->base.output_list.prev, &output->base.link); drmModeFreeEncoder(encoder); @@ -379,8 +439,8 @@ create_output_for_connector(struct drm_compositor *ec, for (i = 0; i < 2; i++) { glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]); - attribs[1] = output->base.width; - attribs[3] = output->base.height; + attribs[1] = output->base.current->width; + attribs[3] = output->base.current->height; output->image[i] = ec->create_drm_image(ec->base.display, attribs); ec->base.image_target_renderbuffer_storage(GL_RENDERBUFFER, @@ -389,7 +449,8 @@ create_output_for_connector(struct drm_compositor *ec, NULL, &handle, &stride); ret = drmModeAddFB(ec->drm.fd, - output->base.width, output->base.height, + output->base.current->width, + output->base.current->height, 32, 32, stride, handle, &output->fb_id[i]); if (ret) { fprintf(stderr, "failed to add fb %d: %m\n", i); @@ -404,7 +465,8 @@ create_output_for_connector(struct drm_compositor *ec, output->rbo[output->current]); ret = drmModeSetCrtc(ec->drm.fd, output->crtc_id, output->fb_id[output->current ^ 1], 0, 0, - &output->connector_id, 1, &output->mode); + &output->connector_id, 1, + &drm_mode->mode_info); if (ret) { fprintf(stderr, "failed to set mode: %m\n"); return -1; @@ -417,8 +479,6 @@ create_output_for_connector(struct drm_compositor *ec, drm_output_prepare_scanout_surface; output->base.set_hardware_cursor = drm_output_set_cursor; - wl_list_insert(ec->base.output_list.prev, &output->base.link); - return 0; } @@ -449,7 +509,7 @@ create_outputs(struct drm_compositor *ec, int option_connector) return -1; x += container_of(ec->base.output_list.prev, struct wlsc_output, - link)->width; + link)->current->width; drmModeFreeConnector(connector); } @@ -530,7 +590,7 @@ update_outputs(struct drm_compositor *ec) /* XXX: not yet needed, we die with 0 outputs */ if (!wl_list_empty(&ec->base.output_list)) - x = last_output->x + last_output->width; + x = last_output->x + last_output->current->width; else x = 0; y = 0; @@ -558,7 +618,7 @@ update_outputs(struct drm_compositor *ec) disconnects &= ~(1 << output->connector_id); printf("connector %d disconnected\n", output->connector_id); - x_offset += output->base.width; + x_offset += output->base.current->width; destroy_output(output); } } diff --git a/compositor/compositor-wayland.c b/compositor/compositor-wayland.c index 750e3e7e..f547c624 100644 --- a/compositor/compositor-wayland.c +++ b/compositor/compositor-wayland.c @@ -67,6 +67,7 @@ struct wayland_output { struct wl_egl_window *egl_window; } parent; EGLSurface egl_surface; + struct wlsc_mode mode; }; struct wayland_input { @@ -222,8 +223,18 @@ wayland_compositor_create_output(struct wayland_compositor *c, return -1; memset(output, 0, sizeof *output); + output->mode.flags = + WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; + output->mode.width = width; + output->mode.height = height; + output->mode.refresh = 60; + wl_list_init(&output->base.mode_list); + wl_list_insert(&output->base.mode_list, &output->mode.link); + + output->base.current = &output->mode; wlsc_output_init(&output->base, &c->base, 0, 0, width, height, WL_OUTPUT_FLIPPED); + output->parent.surface = wl_compositor_create_surface(c->parent.compositor); wl_surface_set_user_data(output->parent.surface, output); @@ -281,20 +292,38 @@ cleanup_output: /* parent output interface */ static void display_handle_geometry(void *data, - struct wl_output *output, - int32_t x, int32_t y, - int32_t width, int32_t height) + struct wl_output *wl_output, + int x, + int y, + int physical_width, + int physical_height, + int subpixel, + const char *make, + const char *model) { struct wayland_compositor *c = data; c->parent.screen_allocation.x = x; c->parent.screen_allocation.y = y; +} + +static void +display_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int width, + int height, + int refresh) +{ + struct wayland_compositor *c = data; + c->parent.screen_allocation.width = width; c->parent.screen_allocation.height = height; } static const struct wl_output_listener output_listener = { display_handle_geometry, + display_handle_mode }; /* parent shell interface */ diff --git a/compositor/compositor-x11.c b/compositor/compositor-x11.c index 52d094bf..0e9fed70 100644 --- a/compositor/compositor-x11.c +++ b/compositor/compositor-x11.c @@ -71,6 +71,7 @@ struct x11_output { xcb_window_t window; EGLSurface egl_surface; + struct wlsc_mode mode; }; struct x11_input { @@ -330,6 +331,16 @@ x11_compositor_create_output(struct x11_compositor *c, return -1; memset(output, 0, sizeof *output); + + output->mode.flags = + WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; + output->mode.width = width; + output->mode.height = height; + output->mode.refresh = 60; + wl_list_init(&output->base.mode_list); + wl_list_insert(&output->base.mode_list, &output->mode.link); + + output->base.current = &output->mode; wlsc_output_init(&output->base, &c->base, 0, 0, width, height, WL_OUTPUT_FLIPPED); diff --git a/compositor/compositor.c b/compositor/compositor.c index 510f5565..5c14b495 100644 --- a/compositor/compositor.c +++ b/compositor/compositor.c @@ -402,7 +402,8 @@ background_create(struct wlsc_output *output, const char *filename) background = wlsc_surface_create(output->compositor, output->x, output->y, - output->width, output->height); + output->current->width, + output->current->height); if (background == NULL) return NULL; @@ -609,7 +610,8 @@ wlsc_output_damage(struct wlsc_output *output) pixman_region32_union_rect(&compositor->damage_region, &compositor->damage_region, output->x, output->y, - output->width, output->height); + output->current->width, + output->current->height); wlsc_compositor_schedule_repaint(compositor); } @@ -647,8 +649,8 @@ fade_output(struct wlsc_output *output, surface.compositor = compositor; surface.x = output->x; surface.y = output->y; - surface.width = output->width; - surface.height = output->height; + surface.width = output->current->width; + surface.height = output->current->height; surface.texture = GL_NONE; surface.transform = NULL; @@ -675,7 +677,7 @@ wlsc_output_repaint(struct wlsc_output *output) output->prepare_render(output); - glViewport(0, 0, output->width, output->height); + glViewport(0, 0, output->current->width, output->current->height); glUseProgram(ec->texture_shader.program); glUniformMatrix4fv(ec->texture_shader.proj_uniform, @@ -687,7 +689,8 @@ wlsc_output_repaint(struct wlsc_output *output) pixman_region32_intersect_rect(&new_damage, &ec->damage_region, output->x, output->y, - output->width, output->height); + output->current->width, + output->current->height); pixman_region32_subtract(&ec->damage_region, &ec->damage_region, &new_damage); pixman_region32_union(&total_damage, &new_damage, @@ -717,8 +720,8 @@ wlsc_output_repaint(struct wlsc_output *output) } if (es->fullscreen_output == output) { - if (es->width < output->width || - es->height < output->height) + if (es->width < output->current->width || + es->height < output->current->height) glClear(GL_COLOR_BUFFER_BIT); wlsc_surface_draw(es, output, &total_damage); } else { @@ -838,8 +841,8 @@ wlsc_surface_assign_output(struct wlsc_surface *es) es->output = NULL; wl_list_for_each(output, &ec->output_list, link) { - if (output->x < es->x && es->x < output->x + output->width && - output->y < es->y && es->y < output->y + output->height) { + if (output->x < es->x && es->x < output->x + output->current->width && + output->y < es->y && es->y < output->y + output->current->height) { if (output != tmp) printf("assiging surface %p to output %p\n", es, output); @@ -1091,10 +1094,10 @@ notify_motion(struct wl_input_device *device, uint32_t time, int x, int y) wlsc_compositor_wake(ec); wl_list_for_each(output, &ec->output_list, link) { - if (output->x <= x && x <= output->x + output->width) + if (output->x <= x && x <= output->x + output->current->width) x_valid = 1; - if (output->y <= y && y <= output->y + output->height) + if (output->y <= y && y <= output->y + output->current->height) y_valid = 1; /* FIXME: calculate this only on output addition/deletion */ @@ -1103,10 +1106,10 @@ notify_motion(struct wl_input_device *device, uint32_t time, int x, int y) if (output->y < min_y) min_y = output->y; - if (output->x + output->width > max_x) - max_x = output->x + output->width; - if (output->y + output->height > max_y) - max_y = output->y + output->height; + if (output->x + output->current->width > max_x) + max_x = output->x + output->current->width; + if (output->y + output->current->height > max_y) + max_y = output->y + output->current->height; } if (!x_valid) { @@ -1461,11 +1464,23 @@ wlsc_output_post_geometry(struct wl_client *client, { struct wlsc_output *output = container_of(global, struct wlsc_output, object); + struct wlsc_mode *mode; wl_client_post_event(client, global, WL_OUTPUT_GEOMETRY, - output->x, output->y, - output->width, output->height); + output->x, + output->y, + output->mm_width, + output->mm_height, + output->subpixel, + output->make, output->model); + + wl_list_for_each (mode, &output->mode_list, link) { + wl_client_post_event(client, global, + WL_OUTPUT_MODE, + mode->flags, + mode->width, mode->height, mode->refresh); + } } static const char vertex_shader[] = @@ -1603,17 +1618,19 @@ wlsc_output_move(struct wlsc_output *output, int x, int y) wlsc_matrix_init(&output->matrix); wlsc_matrix_translate(&output->matrix, - -output->x - output->width / 2.0, - -output->y - output->height / 2.0, 0); + -output->x - output->current->width / 2.0, + -output->y - output->current->height / 2.0, 0); flip = (output->flags & WL_OUTPUT_FLIPPED) ? -1 : 1; wlsc_matrix_scale(&output->matrix, - 2.0 / output->width, - flip * 2.0 / output->height, 1); + 2.0 / output->current->width, + flip * 2.0 / output->current->height, 1); pixman_region32_union_rect(&c->damage_region, &c->damage_region, - x, y, output->width, output->height); + x, y, + output->current->width, + output->current->height); } WL_EXPORT void @@ -1623,8 +1640,8 @@ wlsc_output_init(struct wlsc_output *output, struct wlsc_compositor *c, output->compositor = c; output->x = x; output->y = y; - output->width = width; - output->height = height; + output->mm_width = width; + output->mm_height = height; output->background = background_create(output, option_background); diff --git a/compositor/compositor.h b/compositor/compositor.h index 2546d58f..d68eb9a3 100644 --- a/compositor/compositor.h +++ b/compositor/compositor.h @@ -53,18 +53,31 @@ struct wlsc_transform { struct wlsc_surface; struct wlsc_input_device; +struct wlsc_mode { + uint32_t flags; + int32_t width, height; + uint32_t refresh; + struct wl_list link; +}; + struct wlsc_output { struct wl_object object; struct wl_list link; struct wlsc_compositor *compositor; struct wlsc_surface *background; struct wlsc_matrix matrix; - int32_t x, y, width, height; + int32_t x, y, mm_width, mm_height; pixman_region32_t previous_damage_region; uint32_t flags; int repaint_needed; int finished; + char *make, *model; + uint32_t subpixel; + + struct wlsc_mode *current; + struct wl_list mode_list; + int (*prepare_render)(struct wlsc_output *output); int (*present)(struct wlsc_output *output); int (*prepare_scanout_surface)(struct wlsc_output *output, diff --git a/compositor/screenshooter.c b/compositor/screenshooter.c index ee1ccf65..4d7660d3 100644 --- a/compositor/screenshooter.c +++ b/compositor/screenshooter.c @@ -36,11 +36,12 @@ screenshooter_shoot(struct wl_client *client, if (!wl_buffer_is_shm(buffer)) return; - if (buffer->width < output->width || buffer->height < output->height) + if (buffer->width < output->current->width || + buffer->height < output->current->height) return; glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(0, 0, output->width, output->height, + glReadPixels(0, 0, output->current->width, output->current->height, GL_RGBA, GL_UNSIGNED_BYTE, wl_shm_buffer_get_data(buffer)); } diff --git a/compositor/shell.c b/compositor/shell.c index 577aa578..388ccac6 100644 --- a/compositor/shell.c +++ b/compositor/shell.c @@ -301,8 +301,8 @@ shell_set_fullscreen(struct wl_client *client, es->saved_x = es->x; es->saved_y = es->y; - es->x = (output->width - es->width) / 2; - es->y = (output->height - es->height) / 2; + es->x = (output->current->width - es->width) / 2; + es->y = (output->current->height - es->height) / 2; es->fullscreen_output = output; wlsc_surface_damage(es); es->map_type = WLSC_SURFACE_MAP_FULLSCREEN; @@ -807,8 +807,8 @@ static void attach(struct wlsc_shell *shell, struct wlsc_surface *es) { if (es->map_type == WLSC_SURFACE_MAP_FULLSCREEN) { - es->x = (es->fullscreen_output->width - es->width) / 2; - es->y = (es->fullscreen_output->height - es->height) / 2; + es->x = (es->fullscreen_output->current->width - es->width) / 2; + es->y = (es->fullscreen_output->current->height - es->height) / 2; } }