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