1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_DRIVER_DIRECTFB
24 
25 #include "SDL_DirectFB_video.h"
26 #include "SDL_DirectFB_window.h"
27 
28 #include "../../events/SDL_windowevents_c.h"
29 
30 #define COLOR_EXPAND(col) col.r, col.g, col.b, col.a
31 
32 static DFB_Theme theme_std = {
33     4, 4, 8, 8,
34     {255, 200, 200, 200},
35     24,
36     {255, 0, 0, 255},
37     16,
38     {255, 255, 255, 255},
39     "/usr/share/fonts/truetype/freefont/FreeSans.ttf",
40     {255, 255, 0, 0},
41     {255, 255, 255, 0},
42 };
43 
44 static DFB_Theme theme_none = {
45     0, 0, 0, 0,
46     {0, 0, 0, 0},
47     0,
48     {0, 0, 0, 0},
49     0,
50     {0, 0, 0, 0},
51     NULL
52 };
53 
54 static void
DrawTriangle(IDirectFBSurface * s,int down,int x,int y,int w)55 DrawTriangle(IDirectFBSurface * s, int down, int x, int y, int w)
56 {
57     int x1, x2, x3;
58     int y1, y2, y3;
59 
60     if (down) {
61         x1 = x + w / 2;
62         x2 = x;
63         x3 = x + w;
64         y1 = y + w;
65         y2 = y;
66         y3 = y;
67     } else {
68         x1 = x + w / 2;
69         x2 = x;
70         x3 = x + w;
71         y1 = y;
72         y2 = y + w;
73         y3 = y + w;
74     }
75     s->FillTriangle(s, x1, y1, x2, y2, x3, y3);
76 }
77 
78 static void
LoadFont(_THIS,SDL_Window * window)79 LoadFont(_THIS, SDL_Window * window)
80 {
81     SDL_DFB_DEVICEDATA(_this);
82     SDL_DFB_WINDOWDATA(window);
83 
84     if (windata->font != NULL) {
85         SDL_DFB_RELEASE(windata->font);
86         windata->font = NULL;
87         SDL_DFB_CHECK(windata->window_surface->SetFont(windata->window_surface, windata->font));
88     }
89 
90     if (windata->theme.font != NULL)
91     {
92         DFBFontDescription fdesc;
93 
94         SDL_zero(fdesc);
95         fdesc.flags = DFDESC_HEIGHT;
96         fdesc.height = windata->theme.font_size;
97         SDL_DFB_CHECK(devdata->
98                       dfb->CreateFont(devdata->dfb, windata->theme.font,
99                                       &fdesc, &windata->font));
100         SDL_DFB_CHECK(windata->window_surface->SetFont(windata->window_surface, windata->font));
101     }
102 }
103 
104 static void
DrawCraption(_THIS,IDirectFBSurface * s,int x,int y,char * text)105 DrawCraption(_THIS, IDirectFBSurface * s, int x, int y, char *text)
106 {
107     DFBSurfaceTextFlags flags;
108 
109     flags = DSTF_CENTER | DSTF_TOP;
110 
111     s->DrawString(s, text, -1, x, y, flags);
112 }
113 
114 void
DirectFB_WM_RedrawLayout(_THIS,SDL_Window * window)115 DirectFB_WM_RedrawLayout(_THIS, SDL_Window * window)
116 {
117     SDL_DFB_WINDOWDATA(window);
118     IDirectFBSurface *s = windata->window_surface;
119     DFB_Theme *t = &windata->theme;
120     int i;
121     int d = (t->caption_size - t->font_size) / 2;
122     int x, y, w;
123 
124 
125     if (!windata->is_managed || (window->flags & SDL_WINDOW_FULLSCREEN))
126         return;
127 
128     SDL_DFB_CHECK(s->SetSrcBlendFunction(s, DSBF_ONE));
129     SDL_DFB_CHECK(s->SetDstBlendFunction(s, DSBF_ZERO));
130     SDL_DFB_CHECK(s->SetDrawingFlags(s, DSDRAW_NOFX));
131     SDL_DFB_CHECK(s->SetBlittingFlags(s, DSBLIT_NOFX));
132 
133     LoadFont(_this, window);
134     /* s->SetDrawingFlags(s, DSDRAW_BLEND); */
135     s->SetColor(s, COLOR_EXPAND(t->frame_color));
136     /* top */
137     for (i = 0; i < t->top_size; i++)
138         s->DrawLine(s, 0, i, windata->size.w, i);
139     /* bottom */
140     for (i = windata->size.h - t->bottom_size; i < windata->size.h; i++)
141         s->DrawLine(s, 0, i, windata->size.w, i);
142     /* left */
143     for (i = 0; i < t->left_size; i++)
144         s->DrawLine(s, i, 0, i, windata->size.h);
145     /* right */
146     for (i = windata->size.w - t->right_size; i < windata->size.w; i++)
147         s->DrawLine(s, i, 0, i, windata->size.h);
148     /* Caption */
149     s->SetColor(s, COLOR_EXPAND(t->caption_color));
150     s->FillRectangle(s, t->left_size, t->top_size, windata->client.w,
151                      t->caption_size);
152     /* Close Button */
153     w = t->caption_size;
154     x = windata->size.w - t->right_size - w + d;
155     y = t->top_size + d;
156     s->SetColor(s, COLOR_EXPAND(t->close_color));
157     DrawTriangle(s, 1, x, y, w - 2 * d);
158     /* Max Button */
159     s->SetColor(s, COLOR_EXPAND(t->max_color));
160     DrawTriangle(s, window->flags & SDL_WINDOW_MAXIMIZED ? 1 : 0, x - w,
161                y, w - 2 * d);
162 
163     /* Caption */
164     if (*window->title) {
165         s->SetColor(s, COLOR_EXPAND(t->font_color));
166         DrawCraption(_this, s, (x - w) / 2, t->top_size + d, window->title);
167     }
168     /* Icon */
169     if (windata->icon) {
170         DFBRectangle dr;
171 
172         dr.x = t->left_size + d;
173         dr.y = t->top_size + d;
174         dr.w = w - 2 * d;
175         dr.h = w - 2 * d;
176         s->SetBlittingFlags(s, DSBLIT_BLEND_ALPHACHANNEL);
177 
178         s->StretchBlit(s, windata->icon, NULL, &dr);
179     }
180     windata->wm_needs_redraw = 0;
181 }
182 
183 DFBResult
DirectFB_WM_GetClientSize(_THIS,SDL_Window * window,int * cw,int * ch)184 DirectFB_WM_GetClientSize(_THIS, SDL_Window * window, int *cw, int *ch)
185 {
186     SDL_DFB_WINDOWDATA(window);
187     IDirectFBWindow *dfbwin = windata->dfbwin;
188 
189     SDL_DFB_CHECK(dfbwin->GetSize(dfbwin, cw, ch));
190     dfbwin->GetSize(dfbwin, cw, ch);
191     *cw -= windata->theme.left_size + windata->theme.right_size;
192     *ch -=
193         windata->theme.top_size + windata->theme.caption_size +
194         windata->theme.bottom_size;
195     return DFB_OK;
196 }
197 
198 void
DirectFB_WM_AdjustWindowLayout(SDL_Window * window,int flags,int w,int h)199 DirectFB_WM_AdjustWindowLayout(SDL_Window * window, int flags, int w, int h)
200 {
201     SDL_DFB_WINDOWDATA(window);
202 
203     if (!windata->is_managed)
204         windata->theme = theme_none;
205     else if (flags & SDL_WINDOW_BORDERLESS)
206         /* desc.caps |= DWCAPS_NODECORATION;) */
207         windata->theme = theme_none;
208     else if (flags & SDL_WINDOW_FULLSCREEN) {
209         windata->theme = theme_none;
210     } else if (flags & SDL_WINDOW_MAXIMIZED) {
211         windata->theme = theme_std;
212         windata->theme.left_size = 0;
213         windata->theme.right_size = 0;
214         windata->theme.top_size = 0;
215         windata->theme.bottom_size = 0;
216     } else {
217         windata->theme = theme_std;
218     }
219 
220     windata->client.x = windata->theme.left_size;
221     windata->client.y = windata->theme.top_size + windata->theme.caption_size;
222     windata->client.w = w;
223     windata->client.h = h;
224     windata->size.w =
225         w + windata->theme.left_size + windata->theme.right_size;
226     windata->size.h =
227         h + windata->theme.top_size +
228         windata->theme.caption_size + windata->theme.bottom_size;
229 }
230 
231 
232 enum
233 {
234     WM_POS_NONE = 0x00,
235     WM_POS_CAPTION = 0x01,
236     WM_POS_CLOSE = 0x02,
237     WM_POS_MAX = 0x04,
238     WM_POS_LEFT = 0x08,
239     WM_POS_RIGHT = 0x10,
240     WM_POS_TOP = 0x20,
241     WM_POS_BOTTOM = 0x40,
242 };
243 
244 static int
WMIsClient(DFB_WindowData * p,int x,int y)245 WMIsClient(DFB_WindowData * p, int x, int y)
246 {
247     x -= p->client.x;
248     y -= p->client.y;
249     if (x < 0 || y < 0)
250         return 0;
251     if (x >= p->client.w || y >= p->client.h)
252         return 0;
253     return 1;
254 }
255 
256 static int
WMPos(DFB_WindowData * p,int x,int y)257 WMPos(DFB_WindowData * p, int x, int y)
258 {
259     int pos = WM_POS_NONE;
260 
261     if (!WMIsClient(p, x, y)) {
262         if (y < p->theme.top_size) {
263             pos |= WM_POS_TOP;
264         } else if (y < p->client.y) {
265             if (x <
266                 p->size.w - p->theme.right_size - 2 * p->theme.caption_size) {
267                 pos |= WM_POS_CAPTION;
268             } else if (x <
269                        p->size.w - p->theme.right_size -
270                        p->theme.caption_size) {
271                 pos |= WM_POS_MAX;
272             } else {
273                 pos |= WM_POS_CLOSE;
274             }
275         } else if (y >= p->size.h - p->theme.bottom_size) {
276             pos |= WM_POS_BOTTOM;
277         }
278         if (x < p->theme.left_size) {
279             pos |= WM_POS_LEFT;
280         } else if (x >= p->size.w - p->theme.right_size) {
281             pos |= WM_POS_RIGHT;
282         }
283     }
284     return pos;
285 }
286 
287 int
DirectFB_WM_ProcessEvent(_THIS,SDL_Window * window,DFBWindowEvent * evt)288 DirectFB_WM_ProcessEvent(_THIS, SDL_Window * window, DFBWindowEvent * evt)
289 {
290     SDL_DFB_DEVICEDATA(_this);
291     SDL_DFB_WINDOWDATA(window);
292     DFB_WindowData *gwindata = ((devdata->grabbed_window) ? (DFB_WindowData *) ((devdata->grabbed_window)->driverdata) : NULL);
293     IDirectFBWindow *dfbwin = windata->dfbwin;
294     DFBWindowOptions wopts;
295 
296     if (!windata->is_managed)
297         return 0;
298 
299     SDL_DFB_CHECK(dfbwin->GetOptions(dfbwin, &wopts));
300 
301     switch (evt->type) {
302     case DWET_BUTTONDOWN:
303         if (evt->buttons & DIBM_LEFT) {
304             int pos = WMPos(windata, evt->x, evt->y);
305             switch (pos) {
306             case WM_POS_NONE:
307                 return 0;
308             case WM_POS_CLOSE:
309                 windata->wm_grab = WM_POS_NONE;
310                 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_CLOSE, 0,
311                                     0);
312                 return 1;
313             case WM_POS_MAX:
314                 windata->wm_grab = WM_POS_NONE;
315                 if (window->flags & SDL_WINDOW_MAXIMIZED) {
316                     SDL_RestoreWindow(window);
317                 } else {
318                     SDL_MaximizeWindow(window);
319                 }
320                 return 1;
321             case WM_POS_CAPTION:
322                 if (!(wopts & DWOP_KEEP_STACKING)) {
323                     DirectFB_RaiseWindow(_this, window);
324                 }
325                 if (window->flags & SDL_WINDOW_MAXIMIZED)
326                     return 1;
327                 /* fall through */
328             default:
329                 windata->wm_grab = pos;
330                 if (gwindata != NULL)
331                     SDL_DFB_CHECK(gwindata->dfbwin->UngrabPointer(gwindata->dfbwin));
332                 SDL_DFB_CHECK(dfbwin->GrabPointer(dfbwin));
333                 windata->wm_lastx = evt->cx;
334                 windata->wm_lasty = evt->cy;
335             }
336         }
337         return 1;
338     case DWET_BUTTONUP:
339         if (!windata->wm_grab)
340             return 0;
341         if (!(evt->buttons & DIBM_LEFT)) {
342             if (windata->wm_grab & (WM_POS_RIGHT | WM_POS_BOTTOM)) {
343                 int dx = evt->cx - windata->wm_lastx;
344                 int dy = evt->cy - windata->wm_lasty;
345 
346                 if (!(wopts & DWOP_KEEP_SIZE)) {
347                     int cw, ch;
348                     if ((windata->wm_grab & (WM_POS_BOTTOM | WM_POS_RIGHT)) == WM_POS_BOTTOM)
349                         dx = 0;
350                     else if ((windata->wm_grab & (WM_POS_BOTTOM | WM_POS_RIGHT)) == WM_POS_RIGHT)
351                         dy = 0;
352                     SDL_DFB_CHECK(dfbwin->GetSize(dfbwin, &cw, &ch));
353 
354                     /* necessary to trigger an event - ugly */
355                     SDL_DFB_CHECK(dfbwin->DisableEvents(dfbwin, DWET_ALL));
356                     SDL_DFB_CHECK(dfbwin->Resize(dfbwin, cw + dx + 1, ch + dy));
357                     SDL_DFB_CHECK(dfbwin->EnableEvents(dfbwin, DWET_ALL));
358 
359                     SDL_DFB_CHECK(dfbwin->Resize(dfbwin, cw + dx, ch + dy));
360                 }
361             }
362             SDL_DFB_CHECK(dfbwin->UngrabPointer(dfbwin));
363             if (gwindata != NULL)
364                 SDL_DFB_CHECK(gwindata->dfbwin->GrabPointer(gwindata->dfbwin));
365             windata->wm_grab = WM_POS_NONE;
366             return 1;
367         }
368         break;
369     case DWET_MOTION:
370         if (!windata->wm_grab)
371             return 0;
372         if (evt->buttons & DIBM_LEFT) {
373             int dx = evt->cx - windata->wm_lastx;
374             int dy = evt->cy - windata->wm_lasty;
375 
376             if (windata->wm_grab & WM_POS_CAPTION) {
377                 if (!(wopts & DWOP_KEEP_POSITION))
378                     SDL_DFB_CHECK(dfbwin->Move(dfbwin, dx, dy));
379             }
380             if (windata->wm_grab & (WM_POS_RIGHT | WM_POS_BOTTOM)) {
381                 if (!(wopts & DWOP_KEEP_SIZE)) {
382                     int cw, ch;
383 
384                     /* Make sure all events are disabled for this operation ! */
385                     SDL_DFB_CHECK(dfbwin->DisableEvents(dfbwin, DWET_ALL));
386 
387                     if ((windata->wm_grab & (WM_POS_BOTTOM | WM_POS_RIGHT)) == WM_POS_BOTTOM)
388                         dx = 0;
389                     else if ((windata->wm_grab & (WM_POS_BOTTOM | WM_POS_RIGHT)) == WM_POS_RIGHT)
390                         dy = 0;
391 
392                     SDL_DFB_CHECK(dfbwin->GetSize(dfbwin, &cw, &ch));
393                     SDL_DFB_CHECK(dfbwin->Resize(dfbwin, cw + dx, ch + dy));
394 
395                     SDL_DFB_CHECK(dfbwin->EnableEvents(dfbwin, DWET_ALL));
396                 }
397             }
398             windata->wm_lastx = evt->cx;
399             windata->wm_lasty = evt->cy;
400             return 1;
401         }
402         break;
403     case DWET_KEYDOWN:
404         break;
405     case DWET_KEYUP:
406         break;
407     default:
408         ;
409     }
410     return 0;
411 }
412 
413 #endif /* SDL_VIDEO_DRIVER_DIRECTFB */
414