Make Litehtml image loading non-blocking using threads
[claws.git] / src / plugins / litehtml_viewer / container_linux.cpp
1 /*
2  * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
3  * Copyright(C) 2019 the Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write tothe Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #include "claws-features.h"
21 #endif
22
23 #include "container_linux.h"
24
25 #include <cairo-ft.h>
26
27 #define _USE_MATH_DEFINES
28 #include <math.h>
29
30 #ifndef M_PI
31 #       define M_PI    3.14159265358979323846
32 #endif
33
34 container_linux::container_linux(void)
35 {
36         m_temp_surface  = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2);
37         m_temp_cr               = cairo_create(m_temp_surface);
38         g_rec_mutex_init(&m_images_lock);
39 }
40
41 container_linux::~container_linux(void)
42 {
43         clear_images();
44         cairo_surface_destroy(m_temp_surface);
45         cairo_destroy(m_temp_cr);
46         g_rec_mutex_clear(&m_images_lock);
47 }
48
49 int container_linux::pt_to_px( int pt )
50 {
51         GdkScreen* screen = gdk_screen_get_default();
52         double dpi = gdk_screen_get_resolution(screen);
53
54         return (int) ((double) pt * dpi / 72.0);
55 }
56
57 void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker )
58 {
59         if(!marker.image.empty())
60         {
61                 /*litehtml::tstring url;
62                 make_url(marker.image.c_str(), marker.baseurl, url);
63
64                 lock_images_cache();
65                 images_map::iterator img_i = m_images.find(url.c_str());
66                 if(img_i != m_images.end())
67                 {
68                         if(img_i->second)
69                         {
70                                 draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
71                         }
72                 }
73                 unlock_images_cache();*/
74         } else
75         {
76                 switch(marker.marker_type)
77                 {
78                 case litehtml::list_style_type_circle:
79                         {
80                                 draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 0.5);
81                         }
82                         break;
83                 case litehtml::list_style_type_disc:
84                         {
85                                 fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color);
86                         }
87                         break;
88                 case litehtml::list_style_type_square:
89                         if(hdc)
90                         {
91                                 cairo_t* cr = (cairo_t*) hdc;
92                                 cairo_save(cr);
93
94                                 cairo_new_path(cr);
95                                 cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
96
97                                 set_color(cr, marker.color);
98                                 cairo_fill(cr);
99                                 cairo_restore(cr);
100                         }
101                         break;
102                 default:
103                         /*do nothing*/
104                         break;
105                 }
106         }
107 }
108
109 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
110 {
111         cairo_t* cr = (cairo_t*) hdc;
112         cairo_save(cr);
113         apply_clip(cr);
114
115         rounded_rectangle(cr, bg.border_box, bg.border_radius);
116         cairo_clip(cr);
117
118         cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
119         cairo_clip(cr);
120
121         if(bg.color.alpha)
122         {
123                 set_color(cr, bg.color);
124                 cairo_paint(cr);
125         }
126
127         litehtml::tstring url;
128         make_url(bg.image.c_str(), bg.baseurl.c_str(), url);
129
130         lock_images_cache();
131         bool found = false;
132         const image *img_i = NULL;
133
134         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
135                 const image *i = &(*ii);
136                 if (i->first == url) {
137                         img_i = i;
138                         found = true;
139                         break;
140                 }
141         }
142
143         if(img_i != NULL && img_i->second)
144         {
145                 GdkPixbuf *bgbmp = img_i->second;
146
147                 GdkPixbuf *new_img;
148                 if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
149                 {
150                         new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
151                         bgbmp = new_img;
152                 }
153
154                 cairo_surface_t* img = surface_from_pixbuf(bgbmp);
155                 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
156                 cairo_matrix_t flib_m;
157                 cairo_matrix_init_identity(&flib_m);
158                 cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
159                 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
160                 cairo_pattern_set_matrix (pattern, &flib_m);
161
162                 switch(bg.repeat)
163                 {
164                 case litehtml::background_repeat_no_repeat:
165                         draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
166                         break;
167
168                 case litehtml::background_repeat_repeat_x:
169                         cairo_set_source(cr, pattern);
170                         cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
171                         cairo_fill(cr);
172                         break;
173
174                 case litehtml::background_repeat_repeat_y:
175                         cairo_set_source(cr, pattern);
176                         cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
177                         cairo_fill(cr);
178                         break;
179
180                 case litehtml::background_repeat_repeat:
181                         cairo_set_source(cr, pattern);
182                         cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
183                         cairo_fill(cr);
184                         break;
185                 }
186
187                 cairo_pattern_destroy(pattern);
188                 cairo_surface_destroy(img);
189
190         }
191
192         unlock_images_cache();
193         cairo_restore(cr);
194 }
195
196 void container_linux::make_url(const litehtml::tchar_t* url,    const litehtml::tchar_t* basepath, litehtml::tstring& out)
197 {
198         out = url;
199 }
200
201 void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
202 {
203         if(rx > 0 && ry > 0)
204         {
205
206                 cairo_save(cr);
207
208                 cairo_translate(cr, x, y);
209                 cairo_scale(cr, 1, ry / rx);
210                 cairo_translate(cr, -x, -y);
211
212                 if(neg)
213                 {
214                         cairo_arc_negative(cr, x, y, rx, a1, a2);
215                 } else
216                 {
217                         cairo_arc(cr, x, y, rx, a1, a2);
218                 }
219
220                 cairo_restore(cr);
221         } else
222         {
223                 cairo_move_to(cr, x, y);
224         }
225 }
226
227 void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
228 {
229         cairo_t* cr = (cairo_t*) hdc;
230         cairo_save(cr);
231         apply_clip(cr);
232
233         cairo_new_path(cr);
234
235         int bdr_top             = 0;
236         int bdr_bottom  = 0;
237         int bdr_left    = 0;
238         int bdr_right   = 0;
239
240         if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
241         {
242                 bdr_top = (int) borders.top.width;
243         }
244         if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
245         {
246                 bdr_bottom = (int) borders.bottom.width;
247         }
248         if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
249         {
250                 bdr_left = (int) borders.left.width;
251         }
252         if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
253         {
254                 bdr_right = (int) borders.right.width;
255         }
256
257         // draw right border
258         if(bdr_right)
259         {
260                 set_color(cr, borders.right.color);
261
262                 double r_top    = borders.radius.top_right_x;
263                 double r_bottom = borders.radius.bottom_right_x;
264
265                 if(r_top)
266                 {
267                         double end_angle        = 2 * M_PI;
268                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_top / (double) bdr_right + 1);
269
270                         add_path_arc(cr,
271                                 draw_pos.right() - r_top,
272                                 draw_pos.top() + r_top,
273                                 r_top - bdr_right,
274                                 r_top - bdr_right + (bdr_right - bdr_top),
275                                 end_angle,
276                                 start_angle, true);
277
278                         add_path_arc(cr,
279                                 draw_pos.right() - r_top,
280                                 draw_pos.top() + r_top,
281                                 r_top,
282                                 r_top,
283                                 start_angle,
284                                 end_angle, false);
285                 } else
286                 {
287                         cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
288                         cairo_line_to(cr, draw_pos.right(), draw_pos.top());
289                 }
290
291                 if(r_bottom)
292                 {
293                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom() - r_bottom);
294
295                         double start_angle      = 0;
296                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_right + 1);
297
298                         add_path_arc(cr,
299                                 draw_pos.right() - r_bottom,
300                                 draw_pos.bottom() - r_bottom,
301                                 r_bottom,
302                                 r_bottom,
303                                 start_angle,
304                                 end_angle, false);
305
306                         add_path_arc(cr,
307                                 draw_pos.right() - r_bottom,
308                                 draw_pos.bottom() - r_bottom,
309                                 r_bottom - bdr_right,
310                                 r_bottom - bdr_right + (bdr_right - bdr_bottom),
311                                 end_angle,
312                                 start_angle, true);
313                 } else
314                 {
315                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
316                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
317                 }
318
319                 cairo_fill(cr);
320         }
321
322         // draw bottom border
323         if(bdr_bottom)
324         {
325                 set_color(cr, borders.bottom.color);
326
327                 double r_left   = borders.radius.bottom_left_x;
328                 double r_right  = borders.radius.bottom_right_x;
329
330                 if(r_left)
331                 {
332                         double start_angle      = M_PI / 2.0;
333                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_left / (double) bdr_bottom + 1);
334
335                         add_path_arc(cr,
336                                 draw_pos.left() + r_left,
337                                 draw_pos.bottom() - r_left,
338                                 r_left - bdr_bottom + (bdr_bottom - bdr_left),
339                                 r_left - bdr_bottom,
340                                 start_angle,
341                                 end_angle, false);
342
343                         add_path_arc(cr,
344                                 draw_pos.left() + r_left,
345                                 draw_pos.bottom() - r_left,
346                                 r_left,
347                                 r_left,
348                                 end_angle,
349                                 start_angle, true);
350                 } else
351                 {
352                         cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
353                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
354                 }
355
356                 if(r_right)
357                 {
358                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.bottom());
359
360                         double end_angle        = M_PI / 2.0;
361                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_right / (double) bdr_bottom + 1);
362
363                         add_path_arc(cr,
364                                 draw_pos.right() - r_right,
365                                 draw_pos.bottom() - r_right,
366                                 r_right,
367                                 r_right,
368                                 end_angle,
369                                 start_angle, true);
370
371                         add_path_arc(cr,
372                                 draw_pos.right() - r_right,
373                                 draw_pos.bottom() - r_right,
374                                 r_right - bdr_bottom + (bdr_bottom - bdr_right),
375                                 r_right - bdr_bottom,
376                                 start_angle,
377                                 end_angle, false);
378                 } else
379                 {
380                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
381                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
382                 }
383
384                 cairo_fill(cr);
385         }
386
387         // draw top border
388         if(bdr_top)
389         {
390                 set_color(cr, borders.top.color);
391
392                 double r_left   = borders.radius.top_left_x;
393                 double r_right  = borders.radius.top_right_x;
394
395                 if(r_left)
396                 {
397                         double end_angle        = M_PI * 3.0 / 2.0;
398                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_left / (double) bdr_top + 1);
399
400                         add_path_arc(cr,
401                                 draw_pos.left() + r_left,
402                                 draw_pos.top() + r_left,
403                                 r_left,
404                                 r_left,
405                                 end_angle,
406                                 start_angle, true);
407
408                         add_path_arc(cr,
409                                 draw_pos.left() + r_left,
410                                 draw_pos.top() + r_left,
411                                 r_left - bdr_top + (bdr_top - bdr_left),
412                                 r_left - bdr_top,
413                                 start_angle,
414                                 end_angle, false);
415                 } else
416                 {
417                         cairo_move_to(cr, draw_pos.left(), draw_pos.top());
418                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
419                 }
420
421                 if(r_right)
422                 {
423                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.top() + bdr_top);
424
425                         double start_angle      = M_PI * 3.0 / 2.0;
426                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_right / (double) bdr_top + 1);
427
428                         add_path_arc(cr,
429                                 draw_pos.right() - r_right,
430                                 draw_pos.top() + r_right,
431                                 r_right - bdr_top + (bdr_top - bdr_right),
432                                 r_right - bdr_top,
433                                 start_angle,
434                                 end_angle, false);
435
436                         add_path_arc(cr,
437                                 draw_pos.right() - r_right,
438                                 draw_pos.top() + r_right,
439                                 r_right,
440                                 r_right,
441                                 end_angle,
442                                 start_angle, true);
443                 } else
444                 {
445                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
446                         cairo_line_to(cr, draw_pos.right(),     draw_pos.top());
447                 }
448
449                 cairo_fill(cr);
450         }
451
452         // draw left border
453         if(bdr_left)
454         {
455                 set_color(cr, borders.left.color);
456
457                 double r_top    = borders.radius.top_left_x;
458                 double r_bottom = borders.radius.bottom_left_x;
459
460                 if(r_top)
461                 {
462                         double start_angle      = M_PI;
463                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_top / (double) bdr_left + 1);
464
465                         add_path_arc(cr,
466                                 draw_pos.left() + r_top,
467                                 draw_pos.top() + r_top,
468                                 r_top - bdr_left,
469                                 r_top - bdr_left + (bdr_left - bdr_top),
470                                 start_angle,
471                                 end_angle, false);
472
473                         add_path_arc(cr,
474                                 draw_pos.left() + r_top,
475                                 draw_pos.top() + r_top,
476                                 r_top,
477                                 r_top,
478                                 end_angle,
479                                 start_angle, true);
480                 } else
481                 {
482                         cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
483                         cairo_line_to(cr, draw_pos.left(), draw_pos.top());
484                 }
485
486                 if(r_bottom)
487                 {
488                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom() - r_bottom);
489
490                         double end_angle        = M_PI;
491                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_left + 1);
492
493                         add_path_arc(cr,
494                                 draw_pos.left() + r_bottom,
495                                 draw_pos.bottom() - r_bottom,
496                                 r_bottom,
497                                 r_bottom,
498                                 end_angle,
499                                 start_angle, true);
500
501                         add_path_arc(cr,
502                                 draw_pos.left() + r_bottom,
503                                 draw_pos.bottom() - r_bottom,
504                                 r_bottom - bdr_left,
505                                 r_bottom - bdr_left + (bdr_left - bdr_bottom),
506                                 start_angle,
507                                 end_angle, false);
508                 } else
509                 {
510                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom());
511                         cairo_line_to(cr, draw_pos.left() + bdr_left,   draw_pos.bottom() - bdr_bottom);
512                 }
513
514                 cairo_fill(cr);
515         }
516         cairo_restore(cr);
517 }
518
519 void container_linux::transform_text(litehtml::tstring& text, litehtml::text_transform tt)
520 {
521
522 }
523
524 void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y )
525 {
526         litehtml::position clip_pos = pos;
527         litehtml::position client_pos;
528         get_client_rect(client_pos);
529         if(!valid_x)
530         {
531                 clip_pos.x              = client_pos.x;
532                 clip_pos.width  = client_pos.width;
533         }
534         if(!valid_y)
535         {
536                 clip_pos.y              = client_pos.y;
537                 clip_pos.height = client_pos.height;
538         }
539         m_clips.emplace_back(clip_pos, bdr_radius);
540 }
541
542 void container_linux::del_clip()
543 {
544         if(!m_clips.empty())
545         {
546                 m_clips.pop_back();
547         }
548 }
549
550 void container_linux::apply_clip( cairo_t* cr )
551 {
552         for(const auto& clip_box : m_clips)
553         {
554                 rounded_rectangle(cr, clip_box.box, clip_box.radius);
555                 cairo_clip(cr);
556         }
557 }
558
559 void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
560 {
561         if(!cr) return;
562         cairo_save(cr);
563
564         apply_clip(cr);
565
566         cairo_new_path(cr);
567
568         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
569         cairo_scale (cr, width / 2.0, height / 2.0);
570         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
571
572         set_color(cr, color);
573         cairo_set_line_width(cr, line_width);
574         cairo_stroke(cr);
575
576         cairo_restore(cr);
577 }
578
579 void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
580 {
581         if(!cr) return;
582         cairo_save(cr);
583
584         apply_clip(cr);
585
586         cairo_new_path(cr);
587
588         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
589         cairo_scale (cr, width / 2.0, height / 2.0);
590         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
591
592         set_color(cr, color);
593         cairo_fill(cr);
594
595         cairo_restore(cr);
596 }
597
598 std::shared_ptr<litehtml::element>      container_linux::create_element(const litehtml::tchar_t *tag_name,
599                                                                                                                                           const litehtml::string_map &attributes,
600                                                                                                                                           const std::shared_ptr<litehtml::document> &doc)
601 {
602         return 0;
603 }
604
605 void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
606 {
607         cairo_new_path(cr);
608         if(radius.top_left_x)
609         {
610                 cairo_arc(cr, pos.left() + radius.top_left_x, pos.top() + radius.top_left_x, radius.top_left_x, M_PI, M_PI * 3.0 / 2.0);
611         } else
612         {
613                 cairo_move_to(cr, pos.left(), pos.top());
614         }
615
616         cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());
617
618         if(radius.top_right_x)
619         {
620                 cairo_arc(cr, pos.right() - radius.top_right_x, pos.top() + radius.top_right_x, radius.top_right_x, M_PI * 3.0 / 2.0, 2.0 * M_PI);
621         }
622
623         cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);
624
625         if(radius.bottom_right_x)
626         {
627                 cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0);
628         }
629
630         cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());
631
632         if(radius.bottom_left_x)
633         {
634                 cairo_arc(cr, pos.left() + radius.bottom_left_x, pos.bottom() - radius.bottom_left_x, radius.bottom_left_x, M_PI / 2.0, M_PI);
635         }
636 }
637
638 void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x,     int y, int cx, int cy)
639 {
640         cairo_save(cr);
641
642         {
643                 cairo_matrix_t flib_m;
644                 cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);
645
646                 if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
647                 {
648                         GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
649                         gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
650                         cairo_paint(cr);
651                 } else
652                 {
653                         gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
654                         cairo_paint(cr);
655                 }
656         }
657
658         cairo_restore(cr);
659 }
660
661 cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
662 {
663         cairo_surface_t* ret = NULL;
664
665         if(gdk_pixbuf_get_has_alpha(bmp))
666         {
667                 ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
668         } else
669         {
670                 ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
671         }
672
673 //      Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
674 //      Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
675 //      Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
676         cairo_t *ctx = cairo_create(ret);
677         cairo_paint(ctx);
678         cairo_destroy(ctx);
679
680         return ret;
681 }
682
683 void container_linux::get_media_features(litehtml::media_features& media) const
684 {
685         litehtml::position client;
686     get_client_rect(client);
687         media.type                      = litehtml::media_type_screen;
688         media.width                     = client.width;
689         media.height            = client.height;
690         media.device_width      = gdk_screen_width();
691         media.device_height     = gdk_screen_height();
692         media.color                     = 8;
693         media.monochrome        = 0;
694         media.color_index       = 256;
695         media.resolution        = 96;
696 }
697
698 void container_linux::get_language(litehtml::tstring& language, litehtml::tstring& culture) const
699 {
700         language = _t("en");
701         culture = _t("");
702 }
703
704 void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)
705 {
706
707 }