diff --git a/platform/linux/surface.rs b/platform/linux/surface.rs index 231625a..ce375f6 100644 --- a/platform/linux/surface.rs +++ b/platform/linux/surface.rs @@ -13,24 +13,26 @@ use platform::surface::NativeSurfaceMethods; use texturegl::Texture; use geom::size::Size2D; -use opengles::glx::{GLXFBConfig, GLXDrawable, GLX_BIND_TO_TEXTURE_RGBA_EXT}; -use opengles::glx::{GLX_DEPTH_SIZE, GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT, GLX_RGBA}; +use opengles::glx::{GLXFBConfig, GLXDrawable}; +use opengles::glx::{GLX_BIND_TO_TEXTURE_RGBA_EXT}; +use opengles::glx::{GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT}; use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT}; -use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXChooseVisual, glXCreatePixmap, glXDestroyPixmap}; -use opengles::glx::{glXGetProcAddress, glXGetFBConfigAttrib, glXGetFBConfigs}; +use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXCreatePixmap, glXDestroyPixmap}; +use opengles::glx::{glXGetProcAddress, glXChooseFBConfig}; use opengles::glx::{glXGetVisualFromFBConfig}; -use opengles::glx; +use opengles::glx::{GLX_RGBA_BIT, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_DOUBLEBUFFER}; use opengles::gl2::NO_ERROR; use opengles::gl2; use std::cast; +use std::c_str::CString; use std::libc::{c_int, c_uint, c_void}; use std::ptr; -use xlib::xlib::{Display, Pixmap, XCloseDisplay, XCreateGC, XCreateImage, XCreatePixmap}; -use xlib::xlib::{XDefaultScreen, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow}; +use xlib::xlib::{Display, Pixmap, XCreateGC, XCreateImage, XCreatePixmap, XDefaultScreen}; +use xlib::xlib::{XDisplayString, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow}; use xlib::xlib::{XVisualInfo, ZPixmap}; -/// The display and visual info. This is needed in order to upload on the painting side. This holds -/// a *strong* reference to the display and will close it when done. +/// The display and visual info. This is needed in order to upload on the painting side. This +/// holds a weak reference to the display and will not close it when done. /// /// FIXME(pcwalton): Mark nonsendable and noncopyable. pub struct NativePaintingGraphicsContext { @@ -40,32 +42,14 @@ pub struct NativePaintingGraphicsContext { impl NativePaintingGraphicsContext { pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext { - unsafe { - let display = metadata.display.with_c_str(|c_str| { - XOpenDisplay(c_str) - }); - - if display.is_null() { - fail!("XOpenDisplay() failed!"); - } - - // FIXME(pcwalton): It would be more robust to actually have the compositor pass the - // visual. - let compositor_visual_info = - NativeCompositingGraphicsContext::compositor_visual_info(display); - - NativePaintingGraphicsContext { - display: display, - visual_info: compositor_visual_info, - } - } - } -} - -impl Drop for NativePaintingGraphicsContext { - fn drop(&mut self) { - unsafe { - let _ = XCloseDisplay(self.display); + // FIXME(pcwalton): It would be more robust to actually have the compositor pass the + // visual. + let (compositor_visual_info, _) = + NativeCompositingGraphicsContext::compositor_visual_info(metadata.display); + + NativePaintingGraphicsContext { + display: metadata.display, + visual_info: compositor_visual_info, } } } @@ -82,91 +66,82 @@ impl Drop for NativePaintingGraphicsContext { pub struct NativeCompositingGraphicsContext { display: *Display, visual_info: *XVisualInfo, - framebuffer_configuration: GLXFBConfig, + framebuffer_configuration: Option, } impl NativeCompositingGraphicsContext { /// Chooses the compositor visual info using the same algorithm that the compositor uses. /// /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual. - fn compositor_visual_info(display: *Display) -> *XVisualInfo { + fn compositor_visual_info(display: *Display) -> (*XVisualInfo, Option) { unsafe { - let glx_display: *glx::Display = cast::transmute(display); + let glx_display = cast::transmute(display); + + // CONSIDER: + // In skia, they compute the GLX_ALPHA_SIZE minimum and request + // that as well. + + let fbconfig_attributes = [ + GLX_DOUBLEBUFFER, 0, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT, + GLX_BIND_TO_TEXTURE_RGBA_EXT, 1, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + 0 + ]; + let screen = XDefaultScreen(display); - let attributes = [ GLX_RGBA, GLX_DEPTH_SIZE, 24, 0 ]; - let compositor_visual_info = glXChooseVisual(glx_display, screen, &attributes[0]); - cast::transmute(compositor_visual_info) + let mut configs = 0; + let fbconfigs = glXChooseFBConfig(glx_display, screen, + &fbconfig_attributes[0], &mut configs); + if (configs == 0) { + fail!("Unable to locate a GLX FB configuration that supports RGBA."); + } + + let fbconfig = *ptr::offset(fbconfigs, 0); + let vi = glXGetVisualFromFBConfig(glx_display, fbconfig); + (cast::transmute(vi), Some(fbconfig)) } } /// Creates a native graphics context from the given X display connection. This uses GLX. Only /// the compositor is allowed to call this. pub fn from_display(display: *Display) -> NativeCompositingGraphicsContext { - unsafe { - // FIXME(pcwalton): It would be more robust to actually have the compositor pass the - // visual. - let compositor_visual_info = - NativeCompositingGraphicsContext::compositor_visual_info(display); - let compositor_visual_id = (*compositor_visual_info).visualid; - - // Choose an FB config. - let glx_display = cast::transmute(display); - let screen = XDefaultScreen(display); - let mut fbconfig_count = 0; - let fbconfigs = glXGetFBConfigs(glx_display, screen, &mut fbconfig_count); - let mut fbconfig_index = None; - for i in range(0, fbconfig_count) { - let fbconfig = *ptr::offset(fbconfigs, i as int); - let visual_info = glXGetVisualFromFBConfig(glx_display, fbconfig); - let visual_info: *XVisualInfo = cast::transmute(visual_info); - if visual_info == ptr::null() || (*visual_info).visualid != compositor_visual_id { - continue - } - - let mut value = 0; - glXGetFBConfigAttrib(glx_display, fbconfig, GLX_DRAWABLE_TYPE, &mut value); - if (value & GLX_PIXMAP_BIT) == 0 { - continue - } - - glXGetFBConfigAttrib(glx_display, - fbconfig, - GLX_BIND_TO_TEXTURE_RGBA_EXT, - &mut value); - if value == 0 { - continue - } - - fbconfig_index = Some(i); - break - } - - let framebuffer_configuration = match fbconfig_index { - None => fail!("No appropriate framebuffer config found!"), - Some(index) => *ptr::offset(fbconfigs, index as int), - }; + let (visual_info, fbconfig) = NativeCompositingGraphicsContext::compositor_visual_info(display); - NativeCompositingGraphicsContext { - display: display, - visual_info: compositor_visual_info, - framebuffer_configuration: framebuffer_configuration, - } + NativeCompositingGraphicsContext { + display: display, + visual_info: visual_info, + framebuffer_configuration: fbconfig, } } } -/// The X display string. +/// The X display. #[deriving(Clone)] pub struct NativeGraphicsMetadata { - display: ~str, + display: *Display, } impl NativeGraphicsMetadata { /// Creates graphics metadata from a metadata descriptor. pub fn from_descriptor(descriptor: &NativeGraphicsMetadataDescriptor) -> NativeGraphicsMetadata { - NativeGraphicsMetadata { - display: descriptor.display.to_str(), + // WARNING: We currently rely on the X display connection being the + // same in both the Painting and Compositing contexts, as otherwise + // the X Pixmap will not be sharable across them. Using this + // method breaks that assumption. + unsafe { + let display = descriptor.display.with_c_str(|c_str| { + XOpenDisplay(c_str) + }); + + if display.is_null() { + fail!("XOpenDisplay() failed!"); + } + + NativeGraphicsMetadata { + display: display, + } } } } @@ -180,8 +155,11 @@ pub struct NativeGraphicsMetadataDescriptor { impl NativeGraphicsMetadataDescriptor { /// Creates a metadata descriptor from metadata. pub fn from_metadata(metadata: NativeGraphicsMetadata) -> NativeGraphicsMetadataDescriptor { - NativeGraphicsMetadataDescriptor { - display: metadata.display.to_str() + unsafe { + let c_str = CString::new(XDisplayString(metadata.display), false); + NativeGraphicsMetadataDescriptor { + display: c_str.as_str().unwrap().to_str(), + } } } } @@ -226,13 +204,11 @@ impl NativeSurfaceMethods for NativeSurface { // Create the pixmap. let screen = XDefaultScreen(native_context.display); let window = XRootWindow(native_context.display, screen); - let pixmap = XCreatePixmap(native_context.display, window, size.width as c_uint, size.height as c_uint, ((stride / size.width) * 8) as c_uint); - NativeSurface::from_pixmap(pixmap) } } @@ -251,9 +227,12 @@ impl NativeSurfaceMethods for NativeSurface { GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, 0 ]; + let glx_display = cast::transmute(native_context.display); + let glx_pixmap = glXCreatePixmap(glx_display, - native_context.framebuffer_configuration, + native_context.framebuffer_configuration.expect( + "GLX 1.3 should have a framebuffer_configuration"), self.pixmap, &pixmap_attributes[0]);