Commit 3bdaf4c611182ee47089fb91597ba6f60f1c766e

Ryan C. Gordon 2016-01-05T02:46:10

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
diff --git a/include/SDL_video.h b/include/SDL_video.h
index b439ef9..f4a0269 100644
--- a/include/SDL_video.h
+++ b/include/SDL_video.h
@@ -846,6 +846,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.
  *
  *  \param window The window for which the gamma ramp should be set.
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index a4dcf78..1c0a34d 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 a0191c4..563b2a4 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 fee249c..8607cf9 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 25436ca..b8320f3 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 826e9fd..d764e22 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) {
@@ -2191,6 +2193,42 @@ SDL_GetWindowBrightness(SDL_Window * window)
 }
 
 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,
                                             const Uint16 * blue)
diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m
index dbee221..dd3cf6b 100644
--- a/src/video/cocoa/SDL_cocoavideo.m
+++ b/src/video/cocoa/SDL_cocoavideo.m
@@ -87,6 +87,7 @@ Cocoa_CreateDevice(int devindex)
     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 1037bad..4e2f7ea 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 a74d97f..16b617b 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -1782,6 +1782,14 @@ Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
     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 5579759..d339dd7 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 3b8b45d..40bbe6a 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 658cc87..4b99708 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 dc444bc..41100fb 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 26683a0..b8f24db 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 f91aa0e..e7b13c7 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 e085cf7..b3ff291 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 de991ca..9bd37f4 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 9c1fc64..cc1198c 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 24e4438..cc636e0 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);