[Cogl] [PATCH] onscreen: Adds support for resizeable windows

Robert Bragg robert at sixbynine.org
Wed Jun 20 11:23:28 PDT 2012


From: Robert Bragg <robert at linux.intel.com>

This adds api to be able to request that the window system allows a
given onscreen framebuffer to be resizeable, and api to add and remove
resize handlers to be called whenever the framebuffer does actually
change size.

The new functions are:
  cogl_onscreen_{get,set}_resizeable()
  cogl_onscreen_{add,remove}_resize_handler()

The examples cogl-hello and cogl-x11-foreign have been updated to use
the new api. To smoke test how Cogl updates the viewport automatically
in response to window resizes the cogl-hello test doesn't explicitly
respond to resize events by setting the viewport and cogl-x11-foreign
responds by setting a viewport that is offset by a quarter of the
window's width/height and half the width and height of the window.
---
 cogl/cogl-glx-display-private.h       |    1 +
 cogl/cogl-onscreen-private.h          |   19 +++++
 cogl/cogl-onscreen.c                  |   99 ++++++++++++++++++++++-
 cogl/cogl-onscreen.h                  |  138 +++++++++++++++++++++++++++++++++
 cogl/cogl-sdl.c                       |    2 +
 cogl/winsys/cogl-winsys-egl-private.h |    4 +
 cogl/winsys/cogl-winsys-egl-x11.c     |  118 +++++++++++++++++++++++++---
 cogl/winsys/cogl-winsys-glx.c         |   99 ++++++++++++++++++++----
 cogl/winsys/cogl-winsys-private.h     |    3 +
 cogl/winsys/cogl-winsys-sdl.c         |   92 +++++++++++++++++++++-
 examples/cogl-hello.c                 |    2 +
 examples/cogl-x11-foreign.c           |   24 +++++-
 12 files changed, 563 insertions(+), 38 deletions(-)

diff --git a/cogl/cogl-glx-display-private.h b/cogl/cogl-glx-display-private.h
index f5d4e05..1ffcd32 100644
--- a/cogl/cogl-glx-display-private.h
+++ b/cogl/cogl-glx-display-private.h
@@ -51,6 +51,7 @@ typedef struct _CoglGLXDisplay
   GLXWindow dummy_glxwin;
   Window dummy_xwin;
   CoglBool pending_swap_notify;
+  CoglBool pending_resize_notify;
 } CoglGLXDisplay;
 
 #endif /* __COGL_DISPLAY_GLX_PRIVATE_H */
diff --git a/cogl/cogl-onscreen-private.h b/cogl/cogl-onscreen-private.h
index 08774db..f8de1a7 100644
--- a/cogl/cogl-onscreen-private.h
+++ b/cogl/cogl-onscreen-private.h
@@ -46,6 +46,19 @@ struct _CoglSwapBuffersNotifyEntry
   unsigned int id;
 };
 
+typedef struct _CoglResizeNotifyEntry CoglResizeNotifyEntry;
+
+COGL_TAILQ_HEAD (CoglResizeNotifyList, CoglResizeNotifyEntry);
+
+struct _CoglResizeNotifyEntry
+{
+  COGL_TAILQ_ENTRY (CoglResizeNotifyEntry) list_node;
+
+  CoglOnscreenResizeCallback callback;
+  void *user_data;
+  unsigned int id;
+};
+
 struct _CoglOnscreen
 {
   CoglFramebuffer  _parent;
@@ -64,6 +77,9 @@ struct _CoglOnscreen
 
   CoglSwapBuffersNotifyList swap_callbacks;
 
+  CoglBool resizeable;
+  CoglResizeNotifyList resize_callbacks;
+
   void *winsys;
 };
 
@@ -77,4 +93,7 @@ _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer,
 void
 _cogl_onscreen_notify_swap_buffers (CoglOnscreen *onscreen);
 
+void
+_cogl_onscreen_notify_resize (CoglOnscreen *onscreen);
+
 #endif /* __COGL_ONSCREEN_PRIVATE_H */
diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c
index 242754c..dda7d4c 100644
--- a/cogl/cogl-onscreen.c
+++ b/cogl/cogl-onscreen.c
@@ -46,6 +46,7 @@ _cogl_onscreen_init_from_template (CoglOnscreen *onscreen,
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
 
   COGL_TAILQ_INIT (&onscreen->swap_callbacks);
+  COGL_TAILQ_INIT (&onscreen->resize_callbacks);
 
   framebuffer->config = onscreen_template->config;
   cogl_object_ref (framebuffer->config.swap_chain);
@@ -113,6 +114,13 @@ _cogl_onscreen_free (CoglOnscreen *onscreen)
 {
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
   const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer);
+  CoglResizeNotifyEntry *resize_entry;
+
+  while ((resize_entry = COGL_TAILQ_FIRST (&onscreen->resize_callbacks)))
+    {
+      COGL_TAILQ_REMOVE (&onscreen->resize_callbacks, resize_entry, list_node);
+      g_slice_free (CoglResizeNotifyEntry, resize_entry);
+    }
 
   if (framebuffer->context->window_buffer == COGL_FRAMEBUFFER (onscreen))
     framebuffer->context->window_buffer = NULL;
@@ -347,6 +355,22 @@ _cogl_onscreen_notify_swap_buffers (CoglOnscreen *onscreen)
 }
 
 void
+_cogl_onscreen_notify_resize (CoglOnscreen *onscreen)
+{
+  CoglResizeNotifyEntry *entry, *tmp;
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+
+  COGL_TAILQ_FOREACH_SAFE (entry,
+                           &onscreen->resize_callbacks,
+                           list_node,
+                           tmp)
+    entry->callback (onscreen,
+                     framebuffer->width,
+                     framebuffer->height,
+                     entry->user_data);
+}
+
+void
 _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer,
                                       int width, int height)
 {
@@ -356,11 +380,78 @@ _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer,
   framebuffer->width = width;
   framebuffer->height = height;
 
-  /* The framebuffer geometry can affect the GL viewport so if the
-   * framebuffer being updated is the current framebuffer we mark the
-   * viewport state as changed so it will be updated the next time
-   * _cogl_framebuffer_flush_state() is called. */
+  framebuffer->viewport_x = 0;
+  framebuffer->viewport_y = 0;
+  framebuffer->viewport_width = width;
+  framebuffer->viewport_height = height;
+
+  /* If the framebuffer being updated is the current framebuffer we
+   * mark the viewport state as changed so it will be updated the next
+   * time _cogl_framebuffer_flush_state() is called. */
   if (framebuffer->context->current_draw_buffer == framebuffer)
     framebuffer->context->current_draw_buffer_changes |=
       COGL_FRAMEBUFFER_STATE_VIEWPORT;
 }
+
+void
+cogl_onscreen_set_resizeable (CoglOnscreen *onscreen,
+                              CoglBool resizeable)
+{
+  CoglFramebuffer *framebuffer;
+  const CoglWinsysVtable *winsys;
+
+  if (onscreen->resizeable == resizeable)
+    return;
+
+  onscreen->resizeable = resizeable;
+
+  framebuffer = COGL_FRAMEBUFFER (onscreen);
+  if (framebuffer->allocated)
+    {
+      winsys = _cogl_framebuffer_get_winsys (COGL_FRAMEBUFFER (onscreen));
+
+      if (winsys->onscreen_set_resizeable)
+        winsys->onscreen_set_resizeable (onscreen, resizeable);
+    }
+}
+
+CoglBool
+cogl_onscreen_get_resizeable (CoglOnscreen *onscreen)
+{
+  return onscreen->resizeable;
+}
+
+unsigned int
+cogl_onscreen_add_resize_handler (CoglOnscreen *onscreen,
+                                  CoglOnscreenResizeCallback callback,
+                                  void *user_data)
+{
+  CoglResizeNotifyEntry *entry = g_slice_new (CoglResizeNotifyEntry);
+  static int next_resize_callback_id = 0;
+
+  entry->callback = callback;
+  entry->user_data = user_data;
+  entry->id = next_resize_callback_id++;
+
+  COGL_TAILQ_INSERT_TAIL (&onscreen->resize_callbacks, entry, list_node);
+
+  return entry->id;
+}
+
+void
+cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen,
+                                     unsigned int id)
+{
+  CoglResizeNotifyEntry *entry;
+
+  COGL_TAILQ_FOREACH (entry, &onscreen->resize_callbacks, list_node)
+    {
+      if (entry->id == id)
+        {
+          COGL_TAILQ_REMOVE (&onscreen->resize_callbacks, entry, list_node);
+          g_slice_free (CoglResizeNotifyEntry, entry);
+          break;
+        }
+    }
+}
+
diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h
index 339bdf3..ed0eb12 100644
--- a/cogl/cogl-onscreen.h
+++ b/cogl/cogl-onscreen.h
@@ -381,6 +381,144 @@ cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen,
                                             unsigned int id);
 
 /**
+ * cogl_onscreen_set_resizeable:
+ * @onscreen: A #CoglOnscreen framebuffer
+ *
+ * Lets you request Cogl to mark an @onscreen framebuffer as
+ * resizeable or not.
+ *
+ * By default, if possible, a @onscreen will be created by Cogl
+ * as non resizeable, but it is not guaranteed that this is always
+ * possible for all window systems.
+ *
+ * <note>Cogl does not know whether marking the @onscreen framebuffer
+ * is truly meaningful for your current window system (consider
+ * applications being run fullscreen on a phone or TV) so this
+ * function may not have any useful effect. If you are running on a
+ * multi windowing system such as X11 or Win32 or OSX then Cogl will
+ * request to the window system that users be allowed to resize the
+ * @onscreen, although it's still possible that some other window
+ * management policy will block this possibility.</note>
+ *
+ * <note>Whenever an @onscreen framebuffer is resized the viewport
+ * will be automatically updated to match the new size of the
+ * framebuffer with an origin of (0,0). If your application needs more
+ * specialized control of the viewport it will need to register a
+ * resize handler using cogl_onscreen_add_resize_handler() so that it
+ * can track when the viewport has been changed automatically.</note>
+ *
+ * Since: 2.0
+ */
+void
+cogl_onscreen_set_resizeable (CoglOnscreen *onscreen,
+                              CoglBool resizeable);
+
+/**
+ * cogl_onscreen_get_resizeable:
+ * @onscreen: A #CoglOnscreen framebuffer
+ *
+ * Lets you query whether @onscreen has been marked as resizeable via
+ * the cogl_onscreen_set_resizeable() api.
+ *
+ * By default, if possible, a @onscreen will be created by Cogl
+ * as non resizeable, but it is not guaranteed that this is always
+ * possible for all window systems.
+ *
+ * <note>If cogl_onscreen_set_resizeable(@onscreen, %TRUE) has been
+ * previously called then this function will return %TRUE, but it's
+ * possible that the current windowing system being used does not
+ * support window resizing (consider fullscreen windows on a phone or
+ * a TV). This function is not aware of whether resizing is truly
+ * meaningful with your window system, only whether the @onscreen has
+ * been marked as resizeable.</note>
+ *
+ * Return value: Returns whether @onscreen has been marked as
+ *               resizeable or not.
+ * Since: 2.0
+ */
+CoglBool
+cogl_onscreen_get_resizeable (CoglOnscreen *onscreen);
+
+/**
+ * CoglOnscreenResizeCallback:
+ * @onscreen: A #CoglOnscreen framebuffer that was resized
+ * @width: The new width of @onscreen
+ * @height: The new height of @onscreen
+ * @user_data: The private passed to
+ *             cogl_onscreen_add_resize_handler()
+ *
+ * Is a callback type used with the
+ * cogl_onscreen_add_resize_handler() allowing applications to be
+ * notified whenever an @onscreen framebuffer is resized.
+ *
+ * <note>Cogl automatically updates the viewport of an @onscreen
+ * framebuffer that is resized so this callback is also an indication
+ * that the viewport has been modified too</note>
+ *
+ * <note>A resize callback will only ever be called while dispatching
+ * Cogl events from the system mainloop; so for example during
+ * cogl_poll_dispatch(). This is so that callbacks shouldn't occur
+ * while an application might have arbitrary locks held for
+ * example.</note>
+ *
+ * Since: 2.0
+ */
+typedef void (*CoglOnscreenResizeCallback) (CoglOnscreen *onscreen,
+                                            int width,
+                                            int height,
+                                            void *user_data);
+
+/**
+ * cogl_onscreen_add_resize_handler:
+ * @onscreen: A #CoglOnscreen framebuffer
+ * @callback: A #CoglOnscreenResizeCallback to call when the @onscreen
+ *            changes size.
+ * @user_data: Private data to be passed to @callback.
+ *
+ * Registers a @callback with @onscreen that will be called whenever
+ * the @onscreen framebuffer changes size.
+ *
+ * The @callback can be removed using
+ * cogl_onscreen_remove_resize_handler() passing the same @callback
+ * and @user_data pair.
+ *
+ * <note>Since Cogl automatically updates the viewport of an @onscreen
+ * framebuffer that is resized, a resize callback can also be used to
+ * track when the viewport has been changed automatically by Cogl in
+ * case your application needs more specialized control over the
+ * viewport.</note>
+ *
+ * <note>A resize callback will only ever be called while dispatching
+ * Cogl events from the system mainloop; so for example during
+ * cogl_poll_dispatch(). This is so that callbacks shouldn't occur
+ * while an application might have arbitrary locks held for
+ * example.</note>
+ *
+ * Return value: a unique identifier that can be used to remove to remove
+ *               the callback later.
+ *
+ * Since: 2.0
+ */
+unsigned int
+cogl_onscreen_add_resize_handler (CoglOnscreen *onscreen,
+                                  CoglOnscreenResizeCallback callback,
+                                  void *user_data);
+
+/**
+ * cogl_onscreen_remove_resize_handler:
+ * @onscreen: A #CoglOnscreen framebuffer
+ * @id: An identifier returned from cogl_onscreen_add_resize_handler()
+ *
+ * Removes a resize @callback and @user_data pair that were previously
+ * associated with @onscreen via cogl_onscreen_add_resize_handler().
+ *
+ * Since: 2.0
+ */
+void
+cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen,
+                                     unsigned int id);
+
+/**
  * cogl_is_onscreen:
  * @object: A #CoglObject pointer
  *
diff --git a/cogl/cogl-sdl.c b/cogl/cogl-sdl.c
index 5de1683..c4adbf8 100644
--- a/cogl/cogl-sdl.c
+++ b/cogl/cogl-sdl.c
@@ -74,6 +74,8 @@ cogl_sdl_handle_event (CoglContext *context, SDL_Event *event)
 
   winsys = _cogl_context_get_winsys (context);
 
+  _cogl_renderer_handle_native_event (context->display->renderer, event);
+
   if (winsys->poll_dispatch)
     winsys->poll_dispatch (context, NULL, 0);
 }
diff --git a/cogl/winsys/cogl-winsys-egl-private.h b/cogl/winsys/cogl-winsys-egl-private.h
index c03bf15..96e7a7f 100644
--- a/cogl/winsys/cogl-winsys-egl-private.h
+++ b/cogl/winsys/cogl-winsys-egl-private.h
@@ -119,6 +119,8 @@ typedef struct _CoglDisplayEGL
   EGLSurface current_draw_surface;
   EGLContext current_context;
 
+  CoglBool pending_resize_notify;
+
   /* Platform specific display data */
   void *platform;
 } CoglDisplayEGL;
@@ -133,6 +135,8 @@ typedef struct _CoglOnscreenEGL
 {
   EGLSurface egl_surface;
 
+  CoglBool pending_resize_notify;
+
   /* Platform specific data */
   void *platform;
 } CoglOnscreenEGL;
diff --git a/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/winsys/cogl-winsys-egl-x11.c
index 94a9410..12604b0 100644
--- a/cogl/winsys/cogl-winsys-egl-x11.c
+++ b/cogl/winsys/cogl-winsys-egl-x11.c
@@ -89,6 +89,32 @@ find_onscreen_for_xid (CoglContext *context, uint32_t xid)
   return NULL;
 }
 
+static void
+notify_resize (CoglContext *context,
+               GLXDrawable drawable,
+               int width,
+               int height)
+{
+  CoglOnscreen *onscreen = find_onscreen_for_xid (context, drawable);
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglOnscreenEGL *egl_onscreen;
+
+  if (!onscreen)
+    return;
+
+  egl_onscreen = onscreen->winsys;
+
+  _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
+
+  /* We only want to notify that a resize happened when the
+     application calls cogl_context_dispatch so instead of immediately
+     notifying we'll set a flag to remember to notify later */
+  egl_display->pending_resize_notify = TRUE;
+  egl_onscreen->pending_resize_notify = TRUE;
+}
+
 static CoglFilterReturn
 event_filter_cb (XEvent *xevent, void *data)
 {
@@ -96,17 +122,10 @@ event_filter_cb (XEvent *xevent, void *data)
 
   if (xevent->type == ConfigureNotify)
     {
-      CoglOnscreen *onscreen =
-        find_onscreen_for_xid (context, xevent->xconfigure.window);
-
-      if (onscreen)
-        {
-          CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
-
-          _cogl_framebuffer_winsys_update_size (framebuffer,
-                                                xevent->xconfigure.width,
-                                                xevent->xconfigure.height);
-        }
+      notify_resize (context,
+                     xevent->xconfigure.window,
+                     xevent->xconfigure.width,
+                     xevent->xconfigure.height);
     }
 
   return COGL_FILTER_CONTINUE;
@@ -441,6 +460,45 @@ _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
     XUnmapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin);
 }
 
+static void
+_cogl_winsys_onscreen_set_resizeable (CoglOnscreen *onscreen,
+                                      CoglBool resizeable)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = framebuffer->context;
+  CoglXlibRenderer *xlib_renderer =
+    _cogl_xlib_renderer_get_data (context->display->renderer);
+  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenXlib *xlib_onscreen = egl_onscreen->platform;
+
+  XSizeHints *size_hints = XAllocSizeHints ();
+
+  if (resizeable)
+    {
+      /* TODO: Add cogl_onscreen_request_minimum_size () */
+      size_hints->min_width = 1;
+      size_hints->min_height = 1;
+
+      size_hints->max_width = INT_MAX;
+      size_hints->max_height = INT_MAX;
+    }
+  else
+    {
+      int width = cogl_framebuffer_get_width (framebuffer);
+      int height = cogl_framebuffer_get_height (framebuffer);
+
+      size_hints->min_width = width;
+      size_hints->min_height = height;
+
+      size_hints->max_width = width;
+      size_hints->max_height = height;
+    }
+
+  XSetWMNormalHints (xlib_renderer->xdpy, xlib_onscreen->xwin, size_hints);
+
+  XFree (size_hints);
+}
+
 static uint32_t
 _cogl_winsys_onscreen_x11_get_window_xid (CoglOnscreen *onscreen)
 {
@@ -570,10 +628,35 @@ _cogl_winsys_poll_get_info (CoglContext *context,
                             int *n_poll_fds,
                             int64_t *timeout)
 {
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+
   _cogl_xlib_renderer_poll_get_info (context->display->renderer,
                                      poll_fds,
                                      n_poll_fds,
                                      timeout);
+
+  if (egl_display->pending_resize_notify)
+    *timeout = 0;
+}
+
+static void
+flush_pending_notifications_cb (void *data,
+                                void *user_data)
+{
+  CoglFramebuffer *framebuffer = data;
+
+  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+    {
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+
+      if (egl_onscreen->pending_resize_notify)
+        {
+          _cogl_onscreen_notify_resize (onscreen);
+          egl_onscreen->pending_resize_notify = FALSE;
+        }
+    }
 }
 
 static void
@@ -581,9 +664,20 @@ _cogl_winsys_poll_dispatch (CoglContext *context,
                             const CoglPollFD *poll_fds,
                             int n_poll_fds)
 {
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+
   _cogl_xlib_renderer_poll_dispatch (context->display->renderer,
                                      poll_fds,
                                      n_poll_fds);
+
+  if (egl_display->pending_resize_notify)
+    {
+      g_list_foreach (context->framebuffers,
+                      flush_pending_notifications_cb,
+                      NULL);
+      egl_display->pending_resize_notify = FALSE;
+    }
 }
 
 #ifdef EGL_KHR_image_pixmap
@@ -726,6 +820,8 @@ _cogl_winsys_egl_xlib_get_vtable (void)
 
       vtable.onscreen_set_visibility =
         _cogl_winsys_onscreen_set_visibility;
+      vtable.onscreen_set_resizeable =
+        _cogl_winsys_onscreen_set_resizeable;
 
       vtable.onscreen_x11_get_window_xid =
         _cogl_winsys_onscreen_x11_get_window_xid;
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index 6650106..e6dea0b 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -80,6 +80,7 @@ typedef struct _CoglOnscreenGLX
   GLXDrawable glxwin;
   uint32_t last_swap_vsync_counter;
   CoglBool pending_swap_notify;
+  CoglBool pending_resize_notify;
 } CoglOnscreenGLX;
 
 typedef struct _CoglTexturePixmapGLX
@@ -178,6 +179,32 @@ notify_swap_buffers (CoglContext *context, GLXDrawable drawable)
   glx_onscreen->pending_swap_notify = TRUE;
 }
 
+static void
+notify_resize (CoglContext *context,
+               GLXDrawable drawable,
+               int width,
+               int height)
+{
+  CoglOnscreen *onscreen = find_onscreen_for_xid (context, drawable);
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglDisplay *display = context->display;
+  CoglGLXDisplay *glx_display = display->winsys;
+  CoglOnscreenGLX *glx_onscreen;
+
+  if (!onscreen)
+    return;
+
+  glx_onscreen = onscreen->winsys;
+
+  _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
+
+  /* We only want to notify that a resize happened when the
+     application calls cogl_context_dispatch so instead of immediately
+     notifying we'll set a flag to remember to notify later */
+  glx_display->pending_resize_notify = TRUE;
+  glx_onscreen->pending_resize_notify = TRUE;
+}
+
 static CoglFilterReturn
 glx_event_filter_cb (XEvent *xevent, void *data)
 {
@@ -188,17 +215,10 @@ glx_event_filter_cb (XEvent *xevent, void *data)
 
   if (xevent->type == ConfigureNotify)
     {
-      CoglOnscreen *onscreen =
-        find_onscreen_for_xid (context, xevent->xconfigure.window);
-
-      if (onscreen)
-        {
-          CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
-
-          _cogl_framebuffer_winsys_update_size (framebuffer,
-                                                xevent->xconfigure.width,
-                                                xevent->xconfigure.height);
-        }
+      notify_resize (context,
+                     xevent->xconfigure.window,
+                     xevent->xconfigure.width,
+                     xevent->xconfigure.height);
 
       /* we let ConfigureNotify pass through */
       return COGL_FILTER_CONTINUE;
@@ -1407,6 +1427,44 @@ _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
     XUnmapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin);
 }
 
+static void
+_cogl_winsys_onscreen_set_resizeable (CoglOnscreen *onscreen,
+                                      CoglBool resizeable)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = framebuffer->context;
+  CoglXlibRenderer *xlib_renderer =
+    _cogl_xlib_renderer_get_data (context->display->renderer);
+  CoglOnscreenXlib *xlib_onscreen = onscreen->winsys;
+
+  XSizeHints *size_hints = XAllocSizeHints ();
+
+  if (resizeable)
+    {
+      /* TODO: Add cogl_onscreen_request_minimum_size () */
+      size_hints->min_width = 1;
+      size_hints->min_height = 1;
+
+      size_hints->max_width = INT_MAX;
+      size_hints->max_height = INT_MAX;
+    }
+  else
+    {
+      int width = cogl_framebuffer_get_width (framebuffer);
+      int height = cogl_framebuffer_get_height (framebuffer);
+
+      size_hints->min_width = width;
+      size_hints->min_height = height;
+
+      size_hints->max_width = width;
+      size_hints->max_height = height;
+    }
+
+  XSetWMNormalHints (xlib_renderer->xdpy, xlib_onscreen->xwin, size_hints);
+
+  XFree (size_hints);
+}
+
 /* XXX: This is a particularly hacky _cogl_winsys interface... */
 static XVisualInfo *
 _cogl_winsys_xlib_get_visual_info (void)
@@ -1999,13 +2057,13 @@ _cogl_winsys_poll_get_info (CoglContext *context,
 
   /* If we've already got a pending swap notify then we'll dispatch
      immediately */
-  if (glx_display->pending_swap_notify)
+  if (glx_display->pending_swap_notify || glx_display->pending_resize_notify)
     *timeout = 0;
 }
 
 static void
-flush_pending_swap_notify_cb (void *data,
-                              void *user_data)
+flush_pending_notifications_cb (void *data,
+                                void *user_data)
 {
   CoglFramebuffer *framebuffer = data;
 
@@ -2019,6 +2077,12 @@ flush_pending_swap_notify_cb (void *data,
           _cogl_onscreen_notify_swap_buffers (onscreen);
           glx_onscreen->pending_swap_notify = FALSE;
         }
+
+      if (glx_onscreen->pending_resize_notify)
+        {
+          _cogl_onscreen_notify_resize (onscreen);
+          glx_onscreen->pending_resize_notify = FALSE;
+        }
     }
 }
 
@@ -2034,12 +2098,13 @@ _cogl_winsys_poll_dispatch (CoglContext *context,
                                      poll_fds,
                                      n_poll_fds);
 
-  if (glx_display->pending_swap_notify)
+  if (glx_display->pending_swap_notify || glx_display->pending_resize_notify)
     {
       g_list_foreach (context->framebuffers,
-                      flush_pending_swap_notify_cb,
+                      flush_pending_notifications_cb,
                       NULL);
       glx_display->pending_swap_notify = FALSE;
+      glx_display->pending_resize_notify = FALSE;
     }
 }
 
@@ -2068,6 +2133,8 @@ static CoglWinsysVtable _cogl_winsys_vtable =
     .onscreen_x11_get_window_xid =
       _cogl_winsys_onscreen_x11_get_window_xid,
     .onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility,
+    .onscreen_set_resizeable =
+      _cogl_winsys_onscreen_set_resizeable,
 
     .poll_get_info = _cogl_winsys_poll_get_info,
     .poll_dispatch = _cogl_winsys_poll_dispatch,
diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h
index cd9ca2e..e549c07 100644
--- a/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/winsys/cogl-winsys-private.h
@@ -119,6 +119,9 @@ typedef struct _CoglWinsysVtable
                            const int *rectangles,
                            int n_rectangles);
 
+  void
+  (*onscreen_set_resizeable) (CoglOnscreen *onscreen, CoglBool resizeable);
+
 #ifdef COGL_HAS_EGL_SUPPORT
   EGLDisplay
   (*context_egl_get_egl_display) (CoglContext *context);
diff --git a/cogl/winsys/cogl-winsys-sdl.c b/cogl/winsys/cogl-winsys-sdl.c
index 58c177a..ee01d50 100644
--- a/cogl/winsys/cogl-winsys-sdl.c
+++ b/cogl/winsys/cogl-winsys-sdl.c
@@ -46,8 +46,9 @@ typedef struct _CoglRendererSdl
 typedef struct _CoglDisplaySdl
 {
   SDL_Surface *surface;
-  CoglBool has_onscreen;
+  CoglOnscreen *onscreen;
   Uint32 video_mode_flags;
+  CoglBool pending_resize_notify;
 } CoglDisplaySdl;
 
 static CoglFuncPtr
@@ -190,6 +191,39 @@ error:
   return FALSE;
 }
 
+static CoglFilterReturn
+sdl_event_filter_cb (SDL_Event *event, void *data)
+{
+  if (event->type == SDL_VIDEORESIZE)
+    {
+      CoglContext *context = data;
+      CoglDisplay *display = context->display;
+      CoglDisplaySdl *sdl_display = display->winsys;
+      float width = event->resize.w;
+      float height = event->resize.h;
+      CoglFramebuffer *framebuffer;
+
+      if (!sdl_display->onscreen)
+        return COGL_FILTER_CONTINUE;
+
+      sdl_display->surface = SDL_SetVideoMode (width, height,
+                                               0, /* bitsperpixel */
+                                               sdl_display->video_mode_flags);
+
+      framebuffer = COGL_FRAMEBUFFER (sdl_display->onscreen);
+      _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
+
+      /* We only want to notify that a resize happened when the
+         application calls cogl_context_dispatch so instead of immediately
+         notifying we'll set a flag to remember to notify later */
+      sdl_display->pending_resize_notify = TRUE;
+
+      return COGL_FILTER_CONTINUE;
+    }
+
+  return COGL_FILTER_CONTINUE;
+}
+
 static CoglBool
 _cogl_winsys_context_init (CoglContext *context, GError **error)
 {
@@ -199,6 +233,10 @@ _cogl_winsys_context_init (CoglContext *context, GError **error)
     g_error ("cogl_sdl_renderer_set_event_type() or cogl_sdl_context_new() "
              "must be called during initialization");
 
+  _cogl_renderer_add_native_filter (renderer,
+                                    (CoglNativeFilterFunc)sdl_event_filter_cb,
+                                    context);
+
   return _cogl_context_update_features (context, error);
 }
 
@@ -219,7 +257,7 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
   CoglDisplay *display = context->display;
   CoglDisplaySdl *sdl_display = display->winsys;
 
-  sdl_display->has_onscreen = FALSE;
+  sdl_display->onscreen = NULL;
 }
 
 static CoglBool
@@ -232,7 +270,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
   CoglDisplaySdl *sdl_display = display->winsys;
   int width, height;
 
-  if (sdl_display->has_onscreen)
+  if (sdl_display->onscreen)
     {
       g_set_error (error, COGL_WINSYS_ERROR,
                    COGL_WINSYS_ERROR_CREATE_ONSCREEN,
@@ -265,7 +303,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
                                         sdl_display->surface->w,
                                         sdl_display->surface->h);
 
-  sdl_display->has_onscreen = TRUE;
+  sdl_display->onscreen = onscreen;
 
   return TRUE;
 }
@@ -289,6 +327,49 @@ _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
   /* SDL doesn't appear to provide a way to set this */
 }
 
+static void
+_cogl_winsys_onscreen_set_resizeable (CoglOnscreen *onscreen,
+                                      CoglBool resizeable)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = framebuffer->context;
+  CoglDisplay *display = context->display;
+  CoglDisplaySdl *sdl_display = display->winsys;
+  int width, height;
+
+  width = cogl_framebuffer_get_width (framebuffer);
+  height = cogl_framebuffer_get_height (framebuffer);
+
+  if (resizeable)
+    sdl_display->video_mode_flags |= SDL_RESIZABLE;
+  else
+    sdl_display->video_mode_flags &= ~SDL_RESIZABLE;
+
+  sdl_display->surface = SDL_SetVideoMode (width, height,
+                                           0, /* bitsperpixel */
+                                           sdl_display->video_mode_flags);
+}
+
+static void
+_cogl_winsys_poll_dispatch (CoglContext *context,
+                            const CoglPollFD *poll_fds,
+                            int n_poll_fds)
+{
+  CoglDisplay *display = context->display;
+  CoglDisplaySdl *sdl_display = display->winsys;
+
+  if (sdl_display->pending_resize_notify)
+    {
+      CoglOnscreen *onscreen = sdl_display->onscreen;
+
+      g_return_if_fail (onscreen != NULL);
+
+      _cogl_onscreen_notify_resize (onscreen);
+
+      sdl_display->pending_resize_notify = FALSE;
+    }
+}
+
 const CoglWinsysVtable *
 _cogl_winsys_sdl_get_vtable (void)
 {
@@ -320,6 +401,9 @@ _cogl_winsys_sdl_get_vtable (void)
       vtable.onscreen_update_swap_throttled =
         _cogl_winsys_onscreen_update_swap_throttled;
       vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility;
+      vtable.onscreen_set_resizeable = _cogl_winsys_onscreen_set_resizeable;
+
+      vtable.poll_dispatch = _cogl_winsys_poll_dispatch;
 
       vtable_inited = TRUE;
     }
diff --git a/examples/cogl-hello.c b/examples/cogl-hello.c
index a0b002d..27f562c 100644
--- a/examples/cogl-hello.c
+++ b/examples/cogl-hello.c
@@ -59,6 +59,8 @@ main (int argc, char **argv)
     cogl_onscreen_show (onscreen);
     data.fb = COGL_FRAMEBUFFER (onscreen);
 
+    cogl_onscreen_set_resizeable (onscreen, TRUE);
+
     data.triangle = cogl_primitive_new_p2c4 (data.ctx,
                                              COGL_VERTICES_MODE_TRIANGLES,
                                              3, triangle_vertices);
diff --git a/examples/cogl-x11-foreign.c b/examples/cogl-x11-foreign.c
index 70049f2..7254857 100644
--- a/examples/cogl-x11-foreign.c
+++ b/examples/cogl-x11-foreign.c
@@ -31,6 +31,16 @@ update_cogl_x11_event_mask (CoglOnscreen *onscreen,
                            &attrs);
 }
 
+static void
+resize_handler (CoglOnscreen *onscreen,
+                int width,
+                int height,
+                void *user_data)
+{
+  CoglFramebuffer *fb = user_data;
+  cogl_framebuffer_set_viewport (fb, width / 4, height / 4, width / 2, height / 2);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -149,6 +159,9 @@ main (int argc, char **argv)
 
   fb = COGL_FRAMEBUFFER (onscreen);
 
+  cogl_onscreen_set_resizeable (onscreen, TRUE);
+  cogl_onscreen_add_resize_handler (onscreen, resize_handler, onscreen);
+
   triangle = cogl_primitive_new_p2c4 (ctx, COGL_VERTICES_MODE_TRIANGLES,
                                       3, triangle_vertices);
   pipeline = cogl_pipeline_new (ctx);
@@ -170,13 +183,18 @@ main (int argc, char **argv)
             }
           cogl_xlib_renderer_handle_event (renderer, &event);
         }
-      cogl_framebuffer_clear4f (fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
-      cogl_framebuffer_draw_primitive (fb, pipeline, triangle);
-      cogl_onscreen_swap_buffers (onscreen);
 
+      /* After forwarding native events directly to Cogl you should
+       * then allow Cogl to dispatch any corresponding event
+       * callbacks, such as resize notification callbacks...
+       */
       cogl_poll_get_info (ctx, &poll_fds, &n_poll_fds, &timeout);
       g_poll ((GPollFD *) poll_fds, n_poll_fds, 0);
       cogl_poll_dispatch (ctx, poll_fds, n_poll_fds);
+
+      cogl_framebuffer_clear4f (fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+      cogl_framebuffer_draw_primitive (fb, pipeline, triangle);
+      cogl_onscreen_swap_buffers (onscreen);
     }
 
   return 0;
-- 
1.7.7.6



More information about the Cogl mailing list