Set close on exec flag FD_CLOEXEC
[mesa.git] / src / gallium / state_trackers / egl / fbdev / native_fbdev.c
index 49e3728535dbd21f755dff414849b92be02d1691..b45ab5c4f2e86b5b3375616e870d9060a8ca250b 100644 (file)
  *    Chia-I Wu <olv@lunarg.com>
  */
 
+/**
+ * Considering fbdev as an in-kernel window system,
+ *
+ *  - opening a device opens a connection
+ *  - there is only one window: the framebuffer
+ *  - fb_var_screeninfo decides window position, size, and even color format
+ *  - there is no pixmap
+ *
+ * Now EGL is built on top of this window system.  So we should have
+ *
+ *  - the fd as the handle of the native display
+ *  - reject all but one native window: NULL
+ *  - no pixmap support
+ */
+
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -48,13 +63,10 @@ struct fbdev_display {
    const struct native_event_handler *event_handler;
 
    struct fb_fix_screeninfo finfo;
-   struct fb_var_screeninfo vinfo;
-
+   struct fb_var_screeninfo config_vinfo;
    struct native_config config;
-   struct native_connector connector;
-   struct native_mode mode;
 
-   struct fbdev_surface *current_surface;
+   boolean assume_fixed_vinfo;
 };
 
 struct fbdev_surface {
@@ -66,7 +78,7 @@ struct fbdev_surface {
 
    unsigned int sequence_number;
 
-   boolean is_current;
+   struct fbdev_sw_drawable drawable;
 };
 
 static INLINE struct fbdev_display *
@@ -103,61 +115,113 @@ fbdev_surface_validate(struct native_surface *nsurf, uint attachment_mask,
    return TRUE;
 }
 
-static boolean
-fbdev_surface_flush_frontbuffer(struct native_surface *nsurf)
+static enum pipe_format
+vinfo_to_format(const struct fb_var_screeninfo *vinfo)
 {
-   struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
+   enum pipe_format format = PIPE_FORMAT_NONE;
 
-   if (!fbsurf->is_current)
-      return TRUE;
+   /* should also check channel offsets... */
+   switch (vinfo->bits_per_pixel) {
+   case 32:
+      if (vinfo->red.length == 8 &&
+          vinfo->green.length == 8 &&
+          vinfo->blue.length == 8) {
+         format = (vinfo->transp.length == 8) ?
+            PIPE_FORMAT_B8G8R8A8_UNORM : PIPE_FORMAT_B8G8R8X8_UNORM;
+      }
+      break;
+   case 16:
+      if (vinfo->red.length == 5 &&
+          vinfo->green.length == 6 &&
+          vinfo->blue.length == 5 &&
+          vinfo->transp.length == 0)
+         format = PIPE_FORMAT_B5G6R5_UNORM;
+      break;
+   default:
+      break;
+   }
 
-   return resource_surface_present(fbsurf->rsurf,
-         NATIVE_ATTACHMENT_FRONT_LEFT, NULL);
+   return format;
 }
 
 static boolean
-fbdev_surface_swap_buffers(struct native_surface *nsurf)
+fbdev_surface_update_drawable(struct native_surface *nsurf,
+                              const struct fb_var_screeninfo *vinfo)
 {
    struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
-   struct fbdev_display *fbdpy = fbsurf->fbdpy;
-   boolean ret = TRUE;
-
-   if (fbsurf->is_current) {
-      ret = resource_surface_present(fbsurf->rsurf,
-            NATIVE_ATTACHMENT_BACK_LEFT, NULL);
+   unsigned x, y, width, height;
+
+   x = vinfo->xoffset;
+   y = vinfo->yoffset;
+   width = MIN2(vinfo->xres, fbsurf->width);
+   height = MIN2(vinfo->yres, fbsurf->height);
+
+   /* sanitize the values */
+   if (x + width > vinfo->xres_virtual) {
+      if (x > vinfo->xres_virtual)
+         width = 0;
+      else
+         width = vinfo->xres_virtual - x;
+   }
+   if (y + height > vinfo->yres_virtual) {
+      if (y > vinfo->yres_virtual)
+         height = 0;
+      else
+         height = vinfo->yres_virtual - y;
    }
 
-   resource_surface_swap_buffers(fbsurf->rsurf,
-         NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, TRUE);
-   /* the front/back textures are swapped */
-   fbsurf->sequence_number++;
-   fbdpy->event_handler->invalid_surface(&fbdpy->base,
-         &fbsurf->base, fbsurf->sequence_number);
+   fbsurf->drawable.format = vinfo_to_format(vinfo);
+   fbsurf->drawable.x = vinfo->xoffset;
+   fbsurf->drawable.y = vinfo->yoffset;
+   fbsurf->drawable.width = vinfo->xres;
+   fbsurf->drawable.height = vinfo->yres;
 
-   return ret;
+   return (fbsurf->drawable.format != PIPE_FORMAT_NONE &&
+           fbsurf->drawable.width &&
+           fbsurf->drawable.height);
 }
 
 static boolean
 fbdev_surface_present(struct native_surface *nsurf,
-                      enum native_attachment natt,
-                      boolean preserve,
-                      uint swap_interval)
+                      const struct native_present_control *ctrl)
 {
-   boolean ret;
+   struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
+   struct fbdev_display *fbdpy = fbsurf->fbdpy;
+   boolean ret = FALSE;
 
-   if (preserve || swap_interval)
+   if (ctrl->swap_interval)
+      return FALSE;
+   if (ctrl->natt != NATIVE_ATTACHMENT_BACK_LEFT)
       return FALSE;
 
-   switch (natt) {
-   case NATIVE_ATTACHMENT_FRONT_LEFT:
-      ret = fbdev_surface_flush_frontbuffer(nsurf);
-      break;
-   case NATIVE_ATTACHMENT_BACK_LEFT:
-      ret = fbdev_surface_swap_buffers(nsurf);
-      break;
-   default:
-      ret = FALSE;
-      break;
+   if (!fbdpy->assume_fixed_vinfo) {
+      struct fb_var_screeninfo vinfo;
+
+      memset(&vinfo, 0, sizeof(vinfo));
+      if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo))
+         return FALSE;
+
+      /* present the surface */
+      if (fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) {
+         ret = resource_surface_present(fbsurf->rsurf,
+               ctrl->natt, (void *) &fbsurf->drawable);
+      }
+
+      fbsurf->width = vinfo.xres;
+      fbsurf->height = vinfo.yres;
+
+      if (resource_surface_set_size(fbsurf->rsurf,
+               fbsurf->width, fbsurf->height)) {
+         /* surface resized */
+         fbsurf->sequence_number++;
+         fbdpy->event_handler->invalid_surface(&fbdpy->base,
+               &fbsurf->base, fbsurf->sequence_number);
+      }
+   }
+   else {
+      /* the drawable never changes */
+      ret = resource_surface_present(fbsurf->rsurf,
+            ctrl->natt, (void *) &fbsurf->drawable);
    }
 
    return ret;
@@ -179,26 +243,48 @@ fbdev_surface_destroy(struct native_surface *nsurf)
 }
 
 static struct native_surface *
-fbdev_display_create_scanout_surface(struct native_display *ndpy,
-                                   const struct native_config *nconf,
-                                   uint width, uint height)
+fbdev_display_create_window_surface(struct native_display *ndpy,
+                                    EGLNativeWindowType win,
+                                    const struct native_config *nconf)
 {
    struct fbdev_display *fbdpy = fbdev_display(ndpy);
    struct fbdev_surface *fbsurf;
+   struct fb_var_screeninfo vinfo;
+
+   /* there is only one native window: NULL */
+   if (win)
+      return NULL;
 
    fbsurf = CALLOC_STRUCT(fbdev_surface);
    if (!fbsurf)
       return NULL;
 
    fbsurf->fbdpy = fbdpy;
-   fbsurf->width = width;
-   fbsurf->height = height;
+
+   /* get current vinfo */
+   if (fbdpy->assume_fixed_vinfo) {
+      vinfo = fbdpy->config_vinfo;
+   }
+   else {
+      memset(&vinfo, 0, sizeof(vinfo));
+      if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo)) {
+         FREE(fbsurf);
+         return NULL;
+      }
+   }
+
+   fbsurf->width = vinfo.xres;
+   fbsurf->height = vinfo.yres;
+
+   if (!fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) {
+      FREE(fbsurf);
+      return NULL;
+   }
 
    fbsurf->rsurf = resource_surface_create(fbdpy->base.screen,
          nconf->color_format,
          PIPE_BIND_RENDER_TARGET |
-         PIPE_BIND_DISPLAY_TARGET |
-         PIPE_BIND_SCANOUT);
+         PIPE_BIND_DISPLAY_TARGET);
    if (!fbsurf->rsurf) {
       FREE(fbsurf);
       return NULL;
@@ -214,42 +300,43 @@ fbdev_display_create_scanout_surface(struct native_display *ndpy,
    return &fbsurf->base;
 }
 
+static struct native_surface *
+fbdev_display_create_scanout_surface(struct native_display *ndpy,
+                                     const struct native_config *nconf,
+                                     uint width, uint height)
+{
+   return fbdev_display_create_window_surface(ndpy,
+         (EGLNativeWindowType) NULL, nconf);
+}
+
 static boolean
 fbdev_display_program(struct native_display *ndpy, int crtc_idx,
                       struct native_surface *nsurf, uint x, uint y,
                       const struct native_connector **nconns, int num_nconns,
                       const struct native_mode *nmode)
 {
-   struct fbdev_display *fbdpy = fbdev_display(ndpy);
-   struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
-
-   if (x || y)
-      return FALSE;
-
-   if (fbdpy->current_surface) {
-      if (fbdpy->current_surface == fbsurf)
-         return TRUE;
-      fbdpy->current_surface->is_current = FALSE;
-   }
-
-   if (fbsurf)
-      fbsurf->is_current = TRUE;
-   fbdpy->current_surface = fbsurf;
-
    return TRUE;
 }
 
 static const struct native_mode **
 fbdev_display_get_modes(struct native_display *ndpy,
-                      const struct native_connector *nconn,
-                      int *num_modes)
+                        const struct native_connector *nconn,
+                        int *num_modes)
 {
-   struct fbdev_display *fbdpy = fbdev_display(ndpy);
+   static struct native_mode mode;
    const struct native_mode **modes;
 
+   if (!mode.desc) {
+      struct fbdev_display *fbdpy = fbdev_display(ndpy);
+      mode.desc = "Current Mode";
+      mode.width = fbdpy->config_vinfo.xres;
+      mode.height = fbdpy->config_vinfo.yres;
+      mode.refresh_rate = 60 * 1000; /* dummy */
+   }
+
    modes = MALLOC(sizeof(*modes));
    if (modes) {
-      modes[0] = &fbdpy->mode;
+      modes[0] = &mode;
       if (num_modes)
          *num_modes = 1;
    }
@@ -261,12 +348,12 @@ static const struct native_connector **
 fbdev_display_get_connectors(struct native_display *ndpy, int *num_connectors,
                            int *num_crtc)
 {
-   struct fbdev_display *fbdpy = fbdev_display(ndpy);
+   static struct native_connector connector;
    const struct native_connector **connectors;
 
    connectors = MALLOC(sizeof(*connectors));
    if (connectors) {
-      connectors[0] = &fbdpy->connector;
+      connectors[0] = &connector;
       if (num_connectors)
          *num_connectors = 1;
    }
@@ -274,7 +361,8 @@ fbdev_display_get_connectors(struct native_display *ndpy, int *num_connectors,
    return connectors;
 }
 
-static struct native_display_modeset fbdev_display_modeset = {
+/* remove modeset support one day! */
+static const struct native_display_modeset fbdev_display_modeset = {
    .get_connectors = fbdev_display_get_connectors,
    .get_modes = fbdev_display_get_modes,
    .create_scanout_surface = fbdev_display_create_scanout_surface,
@@ -304,8 +392,10 @@ fbdev_display_get_param(struct native_display *ndpy,
    int val;
 
    switch (param) {
-   case NATIVE_PARAM_USE_NATIVE_BUFFER:
    case NATIVE_PARAM_PRESERVE_BUFFER:
+      val = 1;
+      break;
+   case NATIVE_PARAM_USE_NATIVE_BUFFER:
    case NATIVE_PARAM_MAX_SWAP_INTERVAL:
    default:
       val = 0;
@@ -325,80 +415,13 @@ fbdev_display_destroy(struct native_display *ndpy)
    FREE(fbdpy);
 }
 
-static boolean
-fbdev_display_init_modes(struct native_display *ndpy)
-{
-   struct fbdev_display *fbdpy = fbdev_display(ndpy);
-   struct native_mode *nmode = &fbdpy->mode;
-
-   nmode->desc = "Current Mode";
-   nmode->width = fbdpy->vinfo.xres;
-   nmode->height = fbdpy->vinfo.yres;
-   nmode->refresh_rate = 60 * 1000; /* dummy */
-
-   return TRUE;
-}
-
-static boolean
-fbdev_display_init_connectors(struct native_display *ndpy)
-{
-   return TRUE;
-}
-
-static enum pipe_format
-vinfo_to_format(const struct fb_var_screeninfo *vinfo)
-{
-   enum pipe_format format = PIPE_FORMAT_NONE;
-
-   switch (vinfo->bits_per_pixel) {
-   case 32:
-      if (vinfo->red.length == 8 &&
-          vinfo->green.length == 8 &&
-          vinfo->blue.length == 8) {
-         format = (vinfo->transp.length == 8) ?
-            PIPE_FORMAT_B8G8R8A8_UNORM : PIPE_FORMAT_B8G8R8X8_UNORM;
-      }
-      break;
-   case 16:
-      if (vinfo->red.length == 5 &&
-          vinfo->green.length == 6 &&
-          vinfo->blue.length == 5 &&
-          vinfo->transp.length == 0)
-         format = PIPE_FORMAT_B5G6R5_UNORM;
-      break;
-   default:
-      break;
-   }
-
-   return format;
-}
-
-static boolean
-fbdev_display_init_configs(struct native_display *ndpy)
-{
-   struct fbdev_display *fbdpy = fbdev_display(ndpy);
-   struct native_config *nconf = &fbdpy->config;
-
-   nconf->color_format = vinfo_to_format(&fbdpy->vinfo);
-   if (nconf->color_format == PIPE_FORMAT_NONE)
-      return FALSE;
-
-   nconf->buffer_mask =
-      (1 << NATIVE_ATTACHMENT_FRONT_LEFT) |
-      (1 << NATIVE_ATTACHMENT_BACK_LEFT);
-
-   nconf->scanout_bit = TRUE;
-
-   return TRUE;
-}
-
 static boolean
 fbdev_display_init_screen(struct native_display *ndpy)
 {
    struct fbdev_display *fbdpy = fbdev_display(ndpy);
    struct sw_winsys *ws;
 
-   ws = fbdev_create_sw_winsys(fbdpy->fd, fbdpy->config.color_format);
+   ws = fbdev_create_sw_winsys(fbdpy->fd);
    if (!ws)
       return FALSE;
 
@@ -420,6 +443,26 @@ fbdev_display_init_screen(struct native_display *ndpy)
    return TRUE;
 }
 
+static boolean
+fbdev_display_init_config(struct native_display *ndpy)
+{
+   struct fbdev_display *fbdpy = fbdev_display(ndpy);
+   struct native_config *nconf = &fbdpy->config;
+
+   if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &fbdpy->config_vinfo))
+      return FALSE;
+
+   nconf->color_format = vinfo_to_format(&fbdpy->config_vinfo);
+   if (nconf->color_format == PIPE_FORMAT_NONE)
+      return FALSE;
+
+   nconf->buffer_mask = (1 << NATIVE_ATTACHMENT_BACK_LEFT);
+
+   nconf->window_bit = TRUE;
+
+   return TRUE;
+}
+
 static struct native_display *
 fbdev_display_create(int fd, const struct native_event_handler *event_handler)
 {
@@ -435,23 +478,24 @@ fbdev_display_create(int fd, const struct native_event_handler *event_handler)
    if (ioctl(fbdpy->fd, FBIOGET_FSCREENINFO, &fbdpy->finfo))
       goto fail;
 
-   if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &fbdpy->vinfo))
-      goto fail;
-
    if (fbdpy->finfo.visual != FB_VISUAL_TRUECOLOR ||
        fbdpy->finfo.type != FB_TYPE_PACKED_PIXELS)
       goto fail;
 
-   if (!fbdev_display_init_configs(&fbdpy->base) ||
-       !fbdev_display_init_connectors(&fbdpy->base) ||
-       !fbdev_display_init_modes(&fbdpy->base))
+   if (!fbdev_display_init_config(&fbdpy->base))
       goto fail;
 
+   fbdpy->assume_fixed_vinfo = TRUE;
+
    fbdpy->base.init_screen = fbdev_display_init_screen;
    fbdpy->base.destroy = fbdev_display_destroy;
    fbdpy->base.get_param = fbdev_display_get_param;
    fbdpy->base.get_configs = fbdev_display_get_configs;
 
+   fbdpy->base.create_window_surface = fbdev_display_create_window_surface;
+
+   /* we'd like to remove modeset support one day */
+   fbdpy->config.scanout_bit = TRUE;
    fbdpy->base.modeset = &fbdev_display_modeset;
 
    return &fbdpy->base;
@@ -471,7 +515,16 @@ native_create_display(void *dpy, boolean use_sw)
 
    /* well, this makes fd 0 being ignored */
    if (!dpy) {
-      fd = open("/dev/fb0", O_RDWR);
+      const char *device_name="/dev/fb0";
+#ifdef O_CLOEXEC
+      fd = open(device_name, O_RDWR | O_CLOEXEC);
+      if (fd == -1 && errno == EINVAL)
+#endif
+      {
+         fd = open(device_name, O_RDWR);
+         if (fd != -1)
+            fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+      }
    }
    else {
       fd = dup((int) pointer_to_intptr(dpy));