diff --git a/libweston/compositor-x11.c b/libweston/compositor-x11.c index 1d410864..fd948540 100644 --- a/libweston/compositor-x11.c +++ b/libweston/compositor-x11.c @@ -65,6 +65,12 @@ #define DEFAULT_AXIS_STEP_DISTANCE 10 +#define WINDOW_MIN_WIDTH 128 +#define WINDOW_MIN_HEIGHT 128 + +#define WINDOW_MAX_WIDTH 8192 +#define WINDOW_MAX_HEIGHT 8192 + struct x11_backend { struct weston_backend base; struct weston_compositor *compositor; @@ -114,6 +120,7 @@ struct x11_output { xcb_window_t window; struct weston_mode mode; + struct weston_mode native; struct wl_event_source *finish_frame_timer; xcb_gc_t gc; @@ -123,6 +130,8 @@ struct x11_output { void *buf; uint8_t depth; int32_t scale; + bool resize_pending; + bool window_resized; }; struct window_delete_data { @@ -772,6 +781,89 @@ x11_output_init_shm(struct x11_backend *b, struct x11_output *output, return 0; } +static int +x11_output_switch_mode(struct weston_output *base, struct weston_mode *mode) +{ + struct x11_backend *b; + struct x11_output *output; + static uint32_t values[2]; + int ret; + + if (base == NULL) { + weston_log("output is NULL.\n"); + return -1; + } + + if (mode == NULL) { + weston_log("mode is NULL.\n"); + return -1; + } + + b = to_x11_backend(base->compositor); + output = to_x11_output(base); + + if (mode->width == output->mode.width && + mode->height == output->mode.height) + return 0; + + if (mode->width < WINDOW_MIN_WIDTH || mode->width > WINDOW_MAX_WIDTH) + return -1; + + if (mode->height < WINDOW_MIN_HEIGHT || mode->height > WINDOW_MAX_HEIGHT) + return -1; + + /* xcb_configure_window will create an event, and we could end up + being called twice */ + output->resize_pending = true; + + /* window could've been resized by the user, so don't do it twice */ + if (!output->window_resized) { + values[0] = mode->width; + values[1] = mode->height; + xcb_configure_window(b->conn, output->window, XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT, values); + } + + output->mode.width = mode->width; + output->mode.height = mode->height; + + if (b->use_pixman) { + pixman_renderer_output_destroy(&output->base); + x11_output_deinit_shm(b, output); + + if (x11_output_init_shm(b, output, + output->base.current_mode->width, + output->base.current_mode->height) < 0) { + weston_log("Failed to initialize SHM for the X11 output\n"); + return -1; + } + + if (pixman_renderer_output_create(&output->base) < 0) { + weston_log("Failed to create pixman renderer for output\n"); + x11_output_deinit_shm(b, output); + return -1; + } + } else { + Window xid = (Window) output->window; + + gl_renderer->output_destroy(&output->base); + + ret = gl_renderer->output_window_create(&output->base, + (EGLNativeWindowType) output->window, + &xid, + gl_renderer->opaque_attribs, + NULL, + 0); + if (ret < 0) + return -1; + } + + output->resize_pending = false; + output->window_resized = false; + + return 0; +} + static int x11_output_disable(struct weston_output *base) { @@ -864,14 +956,13 @@ x11_output_enable(struct weston_output *base) XCB_ATOM_ATOM, 32, ARRAY_LENGTH(atom_list), atom_list); } else { - /* Don't resize me. */ memset(&normal_hints, 0, sizeof normal_hints); normal_hints.flags = WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE; - normal_hints.min_width = output->base.current_mode->width; - normal_hints.min_height = output->base.current_mode->height; - normal_hints.max_width = output->base.current_mode->width; - normal_hints.max_height = output->base.current_mode->height; + normal_hints.min_width = WINDOW_MIN_WIDTH; + normal_hints.min_height = WINDOW_MIN_HEIGHT; + normal_hints.max_width = WINDOW_MAX_WIDTH; + normal_hints.max_height = WINDOW_MAX_HEIGHT; xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window, b->atom.wm_normal_hints, b->atom.wm_size_hints, 32, @@ -945,7 +1036,7 @@ x11_output_enable(struct weston_output *base) output->base.assign_planes = NULL; output->base.set_backlight = NULL; output->base.set_dpms = NULL; - output->base.switch_mode = NULL; + output->base.switch_mode = x11_output_switch_mode; loop = wl_display_get_event_loop(b->compositor->wl_display); output->finish_frame_timer = @@ -978,13 +1069,13 @@ x11_output_set_size(struct weston_output *base, int width, int height) /* Make sure we have scale set. */ assert(output->base.scale); - if (width < 1) { + if (width < WINDOW_MIN_WIDTH) { weston_log("Invalid width \"%d\" for output %s\n", width, output->base.name); return -1; } - if (height < 1) { + if (height < WINDOW_MIN_HEIGHT) { weston_log("Invalid height \"%d\" for output %s\n", height, output->base.name); return -1; @@ -999,6 +1090,7 @@ x11_output_set_size(struct weston_output *base, int width, int height) output->mode.width = output_width; output->mode.height = output_height; output->mode.refresh = 60000; + output->native = output->mode; output->scale = output->base.scale; wl_list_insert(&output->base.mode_list, &output->mode.link); @@ -1006,6 +1098,9 @@ x11_output_set_size(struct weston_output *base, int width, int height) output->base.make = "weston-X11"; output->base.model = "none"; + output->base.native_mode = &output->native; + output->base.native_scale = output->base.scale; + output->base.mm_width = width * b->screen->width_in_millimeters / b->screen->width_in_pixels; output->base.mm_height = height * b->screen->height_in_millimeters / @@ -1319,6 +1414,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) xcb_keymap_notify_event_t *keymap_notify; xcb_focus_in_event_t *focus_in; xcb_expose_event_t *expose; + xcb_configure_notify_event_t *configure; xcb_atom_t atom; xcb_window_t window; uint32_t *k; @@ -1474,6 +1570,31 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) } break; + case XCB_CONFIGURE_NOTIFY: + configure = (struct xcb_configure_notify_event_t *) event; + struct x11_output *output = + x11_backend_find_output(b, configure->window); + + if (!output || output->resize_pending) + break; + + struct weston_mode mode = output->mode; + + if (mode.width == configure->width && + mode.height == configure->height) + break; + + output->window_resized = true; + + mode.width = configure->width; + mode.height = configure->height; + + if (weston_output_mode_set_native(&output->base, + &mode, output->scale) < 0) + weston_log("Mode switch failed\n"); + + break; + case XCB_FOCUS_IN: focus_in = (xcb_focus_in_event_t *) event; if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)