FreeRDP
SDL3/dialogs/sdl_widget.cpp
1 
20 #include <cassert>
21 #include <cstdio>
22 #include <cstdlib>
23 
24 #include <SDL3/SDL.h>
25 #include <SDL3_ttf/SDL_ttf.h>
26 
27 #include "sdl_widget.hpp"
28 #include "../sdl_utils.hpp"
29 
30 #include "res/sdl3_resource_manager.hpp"
31 
32 #include <freerdp/log.h>
33 
34 #if defined(WITH_SDL_IMAGE_DIALOGS)
35 #include <SDL3_image/SDL_image.h>
36 #endif
37 
38 #define TAG CLIENT_TAG("SDL.widget")
39 
40 static const SDL_Color backgroundcolor = { 0x38, 0x36, 0x35, 0xff };
41 
42 static const Uint32 hpadding = 10;
43 
44 SdlWidget::SdlWidget(SDL_Renderer* renderer, const SDL_FRect& rect, bool input)
45  : _rect(rect), _input(input)
46 {
47  assert(renderer);
48 
50  "OpenSans-VariableFont_wdth,wght.ttf");
51  if (!ops)
52  widget_log_error(-1, "SDLResourceManager::get");
53  else
54  {
55  _font = TTF_OpenFontIO(ops, 1, 64);
56  if (!_font)
57  widget_log_error(-1, "TTF_OpenFontRW");
58  }
59 }
60 
61 #if defined(WITH_SDL_IMAGE_DIALOGS)
62 SdlWidget::SdlWidget(SDL_Renderer* renderer, const SDL_FRect& rect, SDL_IOStream* ops) : _rect(rect)
63 {
64  if (ops)
65  {
66  _image = IMG_LoadTexture_IO(renderer, ops, 1);
67  if (!_image)
68  widget_log_error(-1, "IMG_LoadTexture_IO");
69  }
70 }
71 #endif
72 
73 SdlWidget::SdlWidget(SdlWidget&& other) noexcept
74  : _font(other._font), _image(other._image), _rect(other._rect), _input(other._input),
75  _wrap(other._wrap), _text_width(other._text_width)
76 {
77  other._font = nullptr;
78  other._image = nullptr;
79 }
80 
81 SDL_Texture* SdlWidget::render_text(SDL_Renderer* renderer, const std::string& text,
82  SDL_Color fgcolor, SDL_FRect& src, SDL_FRect& dst)
83 {
84  auto surface = TTF_RenderText_Blended(_font, text.c_str(), 0, fgcolor);
85  if (!surface)
86  {
87  widget_log_error(-1, "TTF_RenderText_Blended");
88  return nullptr;
89  }
90 
91  auto texture = SDL_CreateTextureFromSurface(renderer, surface);
92  SDL_DestroySurface(surface);
93  if (!texture)
94  {
95  widget_log_error(-1, "SDL_CreateTextureFromSurface");
96  return nullptr;
97  }
98 
99  std::unique_ptr<TTF_TextEngine, decltype(&TTF_DestroySurfaceTextEngine)> engine(
100  TTF_CreateRendererTextEngine(renderer), TTF_DestroySurfaceTextEngine);
101  if (!engine)
102  {
103  widget_log_error(-1, "TTF_CreateRendererTextEngine");
104  return nullptr;
105  }
106 
107  std::unique_ptr<TTF_Text, decltype(&TTF_DestroyText)> txt(
108  TTF_CreateText(engine.get(), _font, text.c_str(), text.size()), TTF_DestroyText);
109 
110  if (!txt)
111  {
112  widget_log_error(-1, "TTF_CreateText");
113  return nullptr;
114  }
115  int w = 0;
116  int h = 0;
117  if (!TTF_GetTextSize(txt.get(), &w, &h))
118  {
119  widget_log_error(-1, "TTF_GetTextSize");
120  return nullptr;
121  }
122 
123  src.w = static_cast<float>(w);
124  src.h = static_cast<float>(h);
125  /* Do some magic:
126  * - Add padding before and after text
127  * - if text is too long only show the last elements
128  * - if text is too short only update used space
129  */
130  dst = _rect;
131  dst.x += hpadding;
132  dst.w -= 2 * hpadding;
133  const float scale = dst.h / src.h;
134  const float sws = (src.w) * scale;
135  const float dws = (dst.w) / scale;
136  if (dst.w > sws)
137  dst.w = sws;
138  if (src.w > dws)
139  {
140  src.x = src.w - dws;
141  src.w = dws;
142  }
143  return texture;
144 }
145 
146 static float scale(float dw, float dh)
147 {
148  const auto scale = dh / dw;
149  const auto dr = dh * scale;
150  return dr;
151 }
152 
153 SDL_Texture* SdlWidget::render_text_wrapped(SDL_Renderer* renderer, const std::string& text,
154  SDL_Color fgcolor, SDL_FRect& src, SDL_FRect& dst)
155 {
156  assert(_text_width < INT32_MAX);
157 
158  auto surface = TTF_RenderText_Blended_Wrapped(_font, text.c_str(), 0, fgcolor,
159  static_cast<int>(_text_width));
160  if (!surface)
161  {
162  widget_log_error(-1, "TTF_RenderText_Blended");
163  return nullptr;
164  }
165 
166  src.w = static_cast<float>(surface->w);
167  src.h = static_cast<float>(surface->h);
168 
169  auto texture = SDL_CreateTextureFromSurface(renderer, surface);
170  SDL_DestroySurface(surface);
171  if (!texture)
172  {
173  widget_log_error(-1, "SDL_CreateTextureFromSurface");
174  return nullptr;
175  }
176 
177  /* Do some magic:
178  * - Add padding before and after text
179  * - if text is too long only show the last elements
180  * - if text is too short only update used space
181  */
182  dst = _rect;
183  dst.x += hpadding;
184  dst.w -= 2 * hpadding;
185  auto dh = scale(src.w, src.h);
186  if (dh < dst.h)
187  dst.h = dh;
188 
189  return texture;
190 }
191 
192 SdlWidget::~SdlWidget()
193 {
194  TTF_CloseFont(_font);
195  if (_image)
196  SDL_DestroyTexture(_image);
197 }
198 
199 bool SdlWidget::error_ex(Uint32 res, const char* what, const char* file, size_t line,
200  const char* fkt)
201 {
202  static wLog* log = nullptr;
203  if (!log)
204  log = WLog_Get(TAG);
205  return sdl_log_error_ex(res, log, what, file, line, fkt);
206 }
207 
208 static bool draw_rect(SDL_Renderer* renderer, const SDL_FRect* rect, SDL_Color color)
209 {
210  const int drc = SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
211  if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
212  return false;
213 
214  const int rc = SDL_RenderFillRect(renderer, rect);
215  return !widget_log_error(rc, "SDL_RenderFillRect");
216 }
217 
218 bool SdlWidget::fill(SDL_Renderer* renderer, SDL_Color color)
219 {
220  std::vector<SDL_Color> colors = { color };
221  return fill(renderer, colors);
222 }
223 
224 bool SdlWidget::fill(SDL_Renderer* renderer, const std::vector<SDL_Color>& colors)
225 {
226  assert(renderer);
227  SDL_BlendMode mode = SDL_BLENDMODE_INVALID;
228  SDL_GetRenderDrawBlendMode(renderer, &mode);
229  SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
230  for (auto color : colors)
231  {
232  draw_rect(renderer, &_rect, color);
233  SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD);
234  }
235  SDL_SetRenderDrawBlendMode(renderer, mode);
236  return true;
237 }
238 
239 bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor,
240  SDL_Color bgcolor)
241 {
242  assert(renderer);
243 
244  if (!fill(renderer, bgcolor))
245  return false;
246  return update_text(renderer, text, fgcolor);
247 }
248 
249 bool SdlWidget::wrap() const
250 {
251  return _wrap;
252 }
253 
254 bool SdlWidget::set_wrap(bool wrap, size_t width)
255 {
256  _wrap = wrap;
257  _text_width = width;
258  return _wrap;
259 }
260 
261 const SDL_FRect& SdlWidget::rect() const
262 {
263  return _rect;
264 }
265 
266 bool SdlWidget::update_text(SDL_Renderer* renderer, const std::string& text, SDL_Color fgcolor)
267 {
268 
269  if (text.empty())
270  return true;
271 
272  SDL_FRect src{};
273  SDL_FRect dst{};
274 
275  SDL_Texture* texture = nullptr;
276  if (_image)
277  {
278  texture = _image;
279  dst = _rect;
280  auto propId = SDL_GetTextureProperties(_image);
281  auto w = SDL_GetNumberProperty(propId, SDL_PROP_TEXTURE_WIDTH_NUMBER, -1);
282  auto h = SDL_GetNumberProperty(propId, SDL_PROP_TEXTURE_HEIGHT_NUMBER, -1);
283  if (w < 0 || h < 0)
284  widget_log_error(-1, "SDL_GetTextureProperties");
285  src.w = static_cast<float>(w);
286  src.h = static_cast<float>(h);
287  }
288  else if (_wrap)
289  texture = render_text_wrapped(renderer, text, fgcolor, src, dst);
290  else
291  texture = render_text(renderer, text, fgcolor, src, dst);
292  if (!texture)
293  return false;
294 
295  const int rc = SDL_RenderTexture(renderer, texture, &src, &dst);
296  if (!_image)
297  SDL_DestroyTexture(texture);
298  if (rc < 0)
299  return !widget_log_error(rc, "SDL_RenderCopy");
300  return true;
301 }
302 
303 bool clear_window(SDL_Renderer* renderer)
304 {
305  assert(renderer);
306 
307  const int drc = SDL_SetRenderDrawColor(renderer, backgroundcolor.r, backgroundcolor.g,
308  backgroundcolor.b, backgroundcolor.a);
309  if (widget_log_error(drc, "SDL_SetRenderDrawColor"))
310  return false;
311 
312  const int rcls = SDL_RenderClear(renderer);
313  return !widget_log_error(rcls, "SDL_RenderClear");
314 }
static SDL_IOStream * get(const std::string &type, const std::string &id)
static std::string typeFonts()