From 3bdaf4c611182ee47089fb91597ba6f60f1c766e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 5 Jan 2016 02:46:10 -0500 Subject: [PATCH] Added SDL_SetWindowOpacity() and SDL_GetWindowOpacity(). This is currently implemented for X11, Cocoa, Windows, and DirectFB. This patch is based on work in Unreal Engine 4's fork of SDL, compliments of Epic Games. --- include/SDL_video.h | 28 +++++++++++++++++ src/dynapi/SDL_dynapi_overrides.h | 2 ++ src/dynapi/SDL_dynapi_procs.h | 2 ++ src/test/SDL_test_common.c | 18 +++++++++++ src/video/SDL_sysvideo.h | 3 ++ src/video/SDL_video.c | 38 ++++++++++++++++++++++++ src/video/cocoa/SDL_cocoavideo.m | 1 + src/video/cocoa/SDL_cocoawindow.h | 1 + src/video/cocoa/SDL_cocoawindow.m | 8 +++++ src/video/directfb/SDL_DirectFB_video.c | 1 + src/video/directfb/SDL_DirectFB_window.c | 13 ++++++++ src/video/directfb/SDL_DirectFB_window.h | 1 + src/video/windows/SDL_windowsvideo.c | 1 + src/video/windows/SDL_windowswindow.c | 33 ++++++++++++++++++++ src/video/windows/SDL_windowswindow.h | 1 + src/video/x11/SDL_x11video.c | 2 ++ src/video/x11/SDL_x11video.h | 1 + src/video/x11/SDL_x11window.c | 19 ++++++++++++ src/video/x11/SDL_x11window.h | 1 + 19 files changed, 174 insertions(+) diff --git a/include/SDL_video.h b/include/SDL_video.h index b439ef9f64e46..f4a0269e0318b 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -845,6 +845,34 @@ extern DECLSPEC int SDLCALL SDL_SetWindowBrightness(SDL_Window * window, float b */ extern DECLSPEC float SDLCALL SDL_GetWindowBrightness(SDL_Window * window); +/** + * \brief Set the opacity for a window + * + * \param window The window which will be made transparent or opaque + * \param opacity Opacity (0.0f - transparent, 1.0f - opaque) This will be + * clamped internally between 0.0f and 1.0f. + * + * \return 0 on success, or -1 if setting the opacity isn't supported. + * + * \sa SDL_GetWindowOpacity() + */ +extern DECLSPEC int SDLCALL SDL_SetWindowOpacity(SDL_Window * window, float opacity); + +/** + * \brief Get the opacity of a window. + * + * If transparency isn't supported on this platform, opacity will be reported + * as 1.0f without error. + * + * \param window The window in question. + * \param opacity Opacity (0.0f - transparent, 1.0f - opaque) + * + * \return 0 on success, or -1 on error (invalid window, etc). + * + * \sa SDL_SetWindowOpacity() + */ +extern DECLSPEC int SDLCALL SDL_GetWindowOpacity(SDL_Window * window, float * out_opacity); + /** * \brief Set the gamma ramp for a window. * diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index a4dcf7825a9f9..1c0a34d578d5e 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -599,3 +599,5 @@ #define SDL_JoystickFromInstanceID SDL_JoystickFromInstanceID_REAL #define SDL_GetDisplayUsableBounds SDL_GetDisplayUsableBounds_REAL #define SDL_GetWindowBordersSize SDL_GetWindowBordersSize_REAL +#define SDL_SetWindowOpacity SDL_SetWindowOpacity_REAL +#define SDL_GetWindowOpacity SDL_GetWindowOpacity_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index a0191c4803876..563b2a40f3592 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -633,3 +633,5 @@ SDL_DYNAPI_PROC(SDL_GameController*,SDL_GameControllerFromInstanceID,(SDL_Joysti SDL_DYNAPI_PROC(SDL_Joystick*,SDL_JoystickFromInstanceID,(SDL_JoystickID a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetDisplayUsableBounds,(int a, SDL_Rect *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetWindowBordersSize,(SDL_Window *a, int *b, int *c, int *d, int *e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(int,SDL_SetWindowOpacity,(SDL_Window *a, float b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetWindowOpacity,(SDL_Window *a, float *b),(a,b),return) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index fee249cb9bd56..8607cf9296373 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1368,6 +1368,24 @@ SDLTest_CommonEvent(SDLTest_CommonState * state, SDL_Event * event, int *done) } } break; + case SDLK_o: + if (withControl) { + /* Ctrl-O (or Ctrl-Shift-O) changes window opacity. */ + SDL_Window *window = SDL_GetWindowFromID(event->key.windowID); + if (window) { + float opacity; + if (SDL_GetWindowOpacity(window, &opacity) == 0) { + if (withShift) { + opacity += 0.20f; + } else { + opacity -= 0.20f; + } + SDL_SetWindowOpacity(window, opacity); + } + } + } + break; + case SDLK_c: if (withControl) { /* Ctrl-C copy awesome text! */ diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 25436ca747a48..b8320f3a818e6 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -86,6 +86,8 @@ struct SDL_Window SDL_DisplayMode fullscreen_mode; + float opacity; + float brightness; Uint16 *gamma; Uint16 *saved_gamma; /* (just offset into gamma) */ @@ -207,6 +209,7 @@ struct SDL_VideoDevice void (*SetWindowMinimumSize) (_THIS, SDL_Window * window); void (*SetWindowMaximumSize) (_THIS, SDL_Window * window); int (*GetWindowBordersSize) (_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right); + int (*SetWindowOpacity) (_THIS, SDL_Window * window, float opacity); void (*ShowWindow) (_THIS, SDL_Window * window); void (*HideWindow) (_THIS, SDL_Window * window); void (*RaiseWindow) (_THIS, SDL_Window * window); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 826e9fdc26c41..d764e22a1cfb0 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1415,6 +1415,7 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) } window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN); window->last_fullscreen_flags = window->flags; + window->opacity = 1.0f; window->brightness = 1.0f; window->next = _this->windows; window->is_destroying = SDL_FALSE; @@ -1475,6 +1476,7 @@ SDL_CreateWindowFrom(const void *data) window->flags = SDL_WINDOW_FOREIGN; window->last_fullscreen_flags = window->flags; window->is_destroying = SDL_FALSE; + window->opacity = 1.0f; window->brightness = 1.0f; window->next = _this->windows; if (_this->windows) { @@ -2190,6 +2192,42 @@ SDL_GetWindowBrightness(SDL_Window * window) return window->brightness; } +int +SDL_SetWindowOpacity(SDL_Window * window, float opacity) +{ + int retval; + CHECK_WINDOW_MAGIC(window, -1); + + if (!_this->SetWindowOpacity) { + return SDL_Unsupported(); + } + + if (opacity < 0.0f) { + opacity = 0.0f; + } else if (opacity > 1.0f) { + opacity = 1.0f; + } + + retval = _this->SetWindowOpacity(_this, window, opacity); + if (retval == 0) { + window->opacity = opacity; + } + + return retval; +} + +int +SDL_GetWindowOpacity(SDL_Window * window, float * out_opacity) +{ + CHECK_WINDOW_MAGIC(window, -1); + + if (out_opacity) { + *out_opacity = window->opacity; + } + + return 0; +} + int SDL_SetWindowGammaRamp(SDL_Window * window, const Uint16 * red, const Uint16 * green, diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m index dbee221b59836..dd3cf6b4c8248 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/src/video/cocoa/SDL_cocoavideo.m @@ -87,6 +87,7 @@ device->SetWindowSize = Cocoa_SetWindowSize; device->SetWindowMinimumSize = Cocoa_SetWindowMinimumSize; device->SetWindowMaximumSize = Cocoa_SetWindowMaximumSize; + device->SetWindowOpacity = Cocoa_SetWindowOpacity; device->ShowWindow = Cocoa_ShowWindow; device->HideWindow = Cocoa_HideWindow; device->RaiseWindow = Cocoa_RaiseWindow; diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h index 1037badfc5f50..4e2f7ea2b408a 100644 --- a/src/video/cocoa/SDL_cocoawindow.h +++ b/src/video/cocoa/SDL_cocoawindow.h @@ -125,6 +125,7 @@ extern void Cocoa_SetWindowPosition(_THIS, SDL_Window * window); extern void Cocoa_SetWindowSize(_THIS, SDL_Window * window); extern void Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window); extern void Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window); +extern int Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity); extern void Cocoa_ShowWindow(_THIS, SDL_Window * window); extern void Cocoa_HideWindow(_THIS, SDL_Window * window); extern void Cocoa_RaiseWindow(_THIS, SDL_Window * window); diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index a74d97f8812c7..16b617b326185 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -1782,6 +1782,14 @@ take effect properly (e.g. setting the window size, etc.) return 0; /* just succeed, the real work is done elsewhere. */ } +int +Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + [data->nswindow setAlphaValue:opacity]; + return 0; +} + #endif /* SDL_VIDEO_DRIVER_COCOA */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/directfb/SDL_DirectFB_video.c b/src/video/directfb/SDL_DirectFB_video.c index 5579759eb0a0c..d339dd78e2432 100644 --- a/src/video/directfb/SDL_DirectFB_video.c +++ b/src/video/directfb/SDL_DirectFB_video.c @@ -121,6 +121,7 @@ DirectFB_CreateDevice(int devindex) device->SetWindowIcon = DirectFB_SetWindowIcon; device->SetWindowPosition = DirectFB_SetWindowPosition; device->SetWindowSize = DirectFB_SetWindowSize; + device->SetWindowOpacity = DirectFB_SetWindowOpacity; device->ShowWindow = DirectFB_ShowWindow; device->HideWindow = DirectFB_HideWindow; device->RaiseWindow = DirectFB_RaiseWindow; diff --git a/src/video/directfb/SDL_DirectFB_window.c b/src/video/directfb/SDL_DirectFB_window.c index 3b8b45d442916..40bbe6aee2a31 100644 --- a/src/video/directfb/SDL_DirectFB_window.c +++ b/src/video/directfb/SDL_DirectFB_window.c @@ -529,4 +529,17 @@ DirectFB_AdjustWindowSurface(SDL_Window * window) return; } +int +DirectFB_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) +{ + const Uint8 alpha = (Uint8) ((unsigned int) (opacity * 255.0f)); + SDL_DFB_WINDOWDATA(window); + SDL_DFB_CHECKERR(windata->dfbwin->SetOpacity(windata->dfbwin, alpha)); + windata->opacity = alpha; + return 0; + +error: + return -1; +} + #endif /* SDL_VIDEO_DRIVER_DIRECTFB */ diff --git a/src/video/directfb/SDL_DirectFB_window.h b/src/video/directfb/SDL_DirectFB_window.h index 658cc8749aa91..4b997081227a8 100644 --- a/src/video/directfb/SDL_DirectFB_window.h +++ b/src/video/directfb/SDL_DirectFB_window.h @@ -75,6 +75,7 @@ extern SDL_bool DirectFB_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info); extern void DirectFB_AdjustWindowSurface(SDL_Window * window); +extern int DirectFB_SetWindowOpacity(_THIS, SDL_Window * window, float opacity); #endif /* _SDL_directfb_window_h */ diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index dc444bca4e50a..41100fbdafd22 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -137,6 +137,7 @@ WIN_CreateDevice(int devindex) device->SetWindowIcon = WIN_SetWindowIcon; device->SetWindowPosition = WIN_SetWindowPosition; device->SetWindowSize = WIN_SetWindowSize; + device->SetWindowOpacity = WIN_SetWindowOpacity; device->ShowWindow = WIN_ShowWindow; device->HideWindow = WIN_HideWindow; device->RaiseWindow = WIN_RaiseWindow; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 26683a0ed04ff..b8f24db8a116b 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -826,6 +826,39 @@ WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled) return 0; /* just succeed, the real work is done elsewhere. */ } +int +WIN_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) +{ + const SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + const HWND hwnd = data->hwnd; + const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE); + + SDL_assert(style != 0); + + if (opacity == 1.0f) { + /* want it fully opaque, just mark it unlayered if necessary. */ + if (style & WS_EX_LAYERED) { + if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_LAYERED) == 0) { + return WIN_SetError("SetWindowLong()"); + } + } + } else { + const BYTE alpha = (BYTE) ((int) (opacity * 255.0f)); + /* want it transparent, mark it layered if necessary. */ + if ((style & WS_EX_LAYERED) == 0) { + if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED) == 0) { + return WIN_SetError("SetWindowLong()"); + } + } + + if (SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA) == 0) { + return WIN_SetError("SetLayeredWindowAttributes()"); + } + } + + return 0; +} + #endif /* SDL_VIDEO_DRIVER_WINDOWS */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index f91aa0ed2b478..e7b13c747b786 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -56,6 +56,7 @@ extern void WIN_SetWindowTitle(_THIS, SDL_Window * window); extern void WIN_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon); extern void WIN_SetWindowPosition(_THIS, SDL_Window * window); extern void WIN_SetWindowSize(_THIS, SDL_Window * window); +extern int WIN_SetWindowOpacity(_THIS, SDL_Window * window, float opacity); extern void WIN_ShowWindow(_THIS, SDL_Window * window); extern void WIN_HideWindow(_THIS, SDL_Window * window); extern void WIN_RaiseWindow(_THIS, SDL_Window * window); diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index e085cf7a835b7..b3ff2912956f6 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -233,6 +233,7 @@ X11_CreateDevice(int devindex) device->SetWindowMinimumSize = X11_SetWindowMinimumSize; device->SetWindowMaximumSize = X11_SetWindowMaximumSize; device->GetWindowBordersSize = X11_GetWindowBordersSize; + device->SetWindowOpacity = X11_SetWindowOpacity; device->ShowWindow = X11_ShowWindow; device->HideWindow = X11_HideWindow; device->RaiseWindow = X11_RaiseWindow; @@ -407,6 +408,7 @@ X11_VideoInit(_THIS) GET_ATOM(_NET_WM_ICON_NAME); GET_ATOM(_NET_WM_ICON); GET_ATOM(_NET_WM_PING); + GET_ATOM(_NET_WM_WINDOW_OPACITY); GET_ATOM(_NET_WM_USER_TIME); GET_ATOM(_NET_ACTIVE_WINDOW); GET_ATOM(UTF8_STRING); diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h index de991ca9212c3..9bd37f457c2b7 100644 --- a/src/video/x11/SDL_x11video.h +++ b/src/video/x11/SDL_x11video.h @@ -104,6 +104,7 @@ typedef struct SDL_VideoData Atom _NET_WM_ICON_NAME; Atom _NET_WM_ICON; Atom _NET_WM_PING; + Atom _NET_WM_WINDOW_OPACITY; Atom _NET_WM_USER_TIME; Atom _NET_ACTIVE_WINDOW; Atom UTF8_STRING; diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index 9c1fc6421be31..cc1198c4e5f24 100644 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -923,6 +923,25 @@ X11_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *b return result; } +int +X11_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + Display *display = data->videodata->display; + Atom _NET_WM_WINDOW_OPACITY = data->videodata->_NET_WM_WINDOW_OPACITY; + + if (opacity == 1.0f) { + X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY); + } else { + const Uint32 FullyOpaque = 0xFFFFFFFF; + const long alpha = (long) ((double)opacity * (double)FullyOpaque); + X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&alpha, 1); + } + + return 0; +} + void X11_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) { diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h index 24e443896be5a..cc636e035bbe0 100644 --- a/src/video/x11/SDL_x11window.h +++ b/src/video/x11/SDL_x11window.h @@ -80,6 +80,7 @@ extern void X11_SetWindowPosition(_THIS, SDL_Window * window); extern void X11_SetWindowMinimumSize(_THIS, SDL_Window * window); extern void X11_SetWindowMaximumSize(_THIS, SDL_Window * window); extern int X11_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right); +extern int X11_SetWindowOpacity(_THIS, SDL_Window * window, float opacity); extern void X11_SetWindowSize(_THIS, SDL_Window * window); extern void X11_ShowWindow(_THIS, SDL_Window * window); extern void X11_HideWindow(_THIS, SDL_Window * window);