From df515741161f7390570d3b110a272d1b6664c050 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 20 Jun 2012 16:22:36 +0100 Subject: [PATCH] onscreen: Adds support for resizable windows This adds api to be able to request that the window system allows a given onscreen framebuffer to be resizable, 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}_resizable() 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. Reviewed-by: Neil Roberts (cherry picked from commit a1a8cc00bfa2cecaf1007aec5f3dd95dc07b1786) --- 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 f5d4e05f0..1ffcd3211 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 08774dbae..06bdd4dd4 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 resizable; + 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 242754c4b..ad8232cb7 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; @@ -346,6 +354,22 @@ _cogl_onscreen_notify_swap_buffers (CoglOnscreen *onscreen) entry->callback (COGL_FRAMEBUFFER (onscreen), entry->user_data); } +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_resizable (CoglOnscreen *onscreen, + CoglBool resizable) +{ + CoglFramebuffer *framebuffer; + const CoglWinsysVtable *winsys; + + if (onscreen->resizable == resizable) + return; + + onscreen->resizable = resizable; + + framebuffer = COGL_FRAMEBUFFER (onscreen); + if (framebuffer->allocated) + { + winsys = _cogl_framebuffer_get_winsys (COGL_FRAMEBUFFER (onscreen)); + + if (winsys->onscreen_set_resizable) + winsys->onscreen_set_resizable (onscreen, resizable); + } +} + +CoglBool +cogl_onscreen_get_resizable (CoglOnscreen *onscreen) +{ + return onscreen->resizable; +} + +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 a3f1d73a6..c4c4dfdc1 100644 --- a/cogl/cogl-onscreen.h +++ b/cogl/cogl-onscreen.h @@ -380,6 +380,144 @@ void cogl_onscreen_remove_swap_buffers_callback (CoglOnscreen *onscreen, unsigned int id); +/** + * cogl_onscreen_set_resizable: + * @onscreen: A #CoglOnscreen framebuffer + * + * Lets you request Cogl to mark an @onscreen framebuffer as + * resizable or not. + * + * By default, if possible, a @onscreen will be created by Cogl + * as non resizable, but it is not guaranteed that this is always + * possible for all window systems. + * + * 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. + * + * 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. + * + * Since: 2.0 + */ +void +cogl_onscreen_set_resizable (CoglOnscreen *onscreen, + CoglBool resizable); + +/** + * cogl_onscreen_get_resizable: + * @onscreen: A #CoglOnscreen framebuffer + * + * Lets you query whether @onscreen has been marked as resizable via + * the cogl_onscreen_set_resizable() api. + * + * By default, if possible, a @onscreen will be created by Cogl + * as non resizable, but it is not guaranteed that this is always + * possible for all window systems. + * + * If cogl_onscreen_set_resizable(@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 resizable. + * + * Return value: Returns whether @onscreen has been marked as + * resizable or not. + * Since: 2.0 + */ +CoglBool +cogl_onscreen_get_resizable (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. + * + * 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 + * + * 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. + * + * 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. + * + * 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. + * + * 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. + * + * 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 5de168386..c4adbf8e8 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 27274bb50..265a423a7 100644 --- a/cogl/winsys/cogl-winsys-egl-private.h +++ b/cogl/winsys/cogl-winsys-egl-private.h @@ -117,6 +117,8 @@ typedef struct _CoglDisplayEGL EGLSurface current_draw_surface; EGLContext current_context; + CoglBool pending_resize_notify; + /* Platform specific display data */ void *platform; } CoglDisplayEGL; @@ -131,6 +133,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 94a941083..4e264d5f0 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_resizable (CoglOnscreen *onscreen, + CoglBool resizable) +{ + 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 (resizable) + { + /* 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_resizable = + _cogl_winsys_onscreen_set_resizable; 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 c733ec406..8fc9b7104 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 @@ -183,6 +184,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) { @@ -193,17 +220,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; @@ -1412,6 +1432,44 @@ _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen, XUnmapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); } +static void +_cogl_winsys_onscreen_set_resizable (CoglOnscreen *onscreen, + CoglBool resizable) +{ + 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 (resizable) + { + /* 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) @@ -2004,13 +2062,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; @@ -2024,6 +2082,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; + } } } @@ -2039,12 +2103,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; } } @@ -2073,6 +2138,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_resizable = + _cogl_winsys_onscreen_set_resizable, .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 a39cbd61e..dd09a0270 100644 --- a/cogl/winsys/cogl-winsys-private.h +++ b/cogl/winsys/cogl-winsys-private.h @@ -120,6 +120,9 @@ typedef struct _CoglWinsysVtable const int *rectangles, int n_rectangles); + void + (*onscreen_set_resizable) (CoglOnscreen *onscreen, CoglBool resizable); + #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 39843fb69..3e87966a1 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 @@ -200,6 +201,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) { @@ -209,6 +243,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); } @@ -229,7 +267,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 @@ -242,7 +280,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, @@ -275,7 +313,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; } @@ -299,6 +337,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_resizable (CoglOnscreen *onscreen, + CoglBool resizable) +{ + 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 (resizable) + 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) { @@ -330,6 +411,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_resizable = _cogl_winsys_onscreen_set_resizable; + + vtable.poll_dispatch = _cogl_winsys_poll_dispatch; vtable_inited = TRUE; } diff --git a/examples/cogl-hello.c b/examples/cogl-hello.c index a0b002d10..cf0e8f6db 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_resizable (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 70049f2f5..9397f4d4f 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_resizable (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;