File indexing completed on 2024-04-21 15:38:30
0001 /** 0002 This file belong to the KMPlayer project, a movie player plugin for Konqueror 0003 Copyright (C) 2007 Koos Vriezen <koos.vriezen@gmail.com> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Lesser General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Lesser General Public License for more details. 0014 0015 You should have received a copy of the GNU Lesser General Public 0016 License along with this library; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0018 **/ 0019 0020 #include "config-kmplayer.h" 0021 0022 #include <stdlib.h> 0023 #include <math.h> 0024 0025 #include <qapplication.h> 0026 #include <qslider.h> 0027 #include <qcursor.h> 0028 #include <qmap.h> 0029 #include <QPalette> 0030 #include <QDesktopWidget> 0031 #include <QtX11Extras/QX11Info> 0032 #include <QPainter> 0033 #include <QMainWindow> 0034 #include <QWidgetAction> 0035 #include <QTextBlock> 0036 #include <QTextDocument> 0037 #include <QAbstractTextDocumentLayout> 0038 #include <QImage> 0039 #include <QAbstractNativeEventFilter> 0040 0041 #include <kactioncollection.h> 0042 #include <kstatusbar.h> 0043 #include <kshortcut.h> 0044 #include <klocalizedstring.h> 0045 #include <kdebug.h> 0046 0047 #include "kmplayerview.h" 0048 #include "kmplayercontrolpanel.h" 0049 #include "playlistview.h" 0050 #include "viewarea.h" 0051 #ifdef KMPLAYER_WITH_CAIRO 0052 # include <cairo-xcb.h> 0053 #endif 0054 #include "mediaobject.h" 0055 #include "kmplayer_smil.h" 0056 #include "kmplayer_rp.h" 0057 #include "mediaobject.h" 0058 0059 #include <xcb/xcb.h> 0060 0061 #include <kurl.h> 0062 0063 using namespace KMPlayer; 0064 0065 #if QT_VERSION >= 0x050600 0066 static qreal pixel_device_ratio; 0067 #endif 0068 //------------------------------------------------------------------------- 0069 0070 #ifdef KMPLAYER_WITH_CAIRO 0071 static void clearSurface (cairo_t *cr, const IRect &rect) { 0072 cairo_save (cr); 0073 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); 0074 cairo_rectangle (cr, rect.x (), rect.y (), rect.width (), rect.height ()); 0075 cairo_fill (cr); 0076 cairo_restore (cr); 0077 } 0078 0079 void ImageData::copyImage (Surface *s, const SSize &sz, cairo_surface_t *similar, CalculatedSizer *zoom) { 0080 cairo_surface_t *src_sf; 0081 bool clear = false; 0082 int w = sz.width; 0083 int h = sz.height; 0084 0085 if (surface) { 0086 src_sf = surface; 0087 } else { 0088 if (image->depth () < 24) { 0089 QImage qi = image->convertToFormat (QImage::Format_RGB32); 0090 *image = qi; 0091 } 0092 src_sf = cairo_image_surface_create_for_data ( 0093 image->bits (), 0094 has_alpha ? CAIRO_FORMAT_ARGB32:CAIRO_FORMAT_RGB24, 0095 width, height, image->bytesPerLine ()); 0096 if (flags & ImagePixmap && !(flags & ImageAnimated)) { 0097 surface = cairo_surface_create_similar (similar, 0098 has_alpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR, 0099 width, height); 0100 cairo_pattern_t *pat = cairo_pattern_create_for_surface (src_sf); 0101 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 0102 cairo_t *cr = cairo_create (surface); 0103 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 0104 cairo_set_source (cr, pat); 0105 cairo_paint (cr); 0106 cairo_destroy (cr); 0107 cairo_pattern_destroy (pat); 0108 cairo_surface_destroy (src_sf); 0109 src_sf = surface; 0110 delete image; 0111 image = NULL; 0112 } 0113 } 0114 0115 cairo_pattern_t *img_pat = cairo_pattern_create_for_surface (src_sf); 0116 cairo_pattern_set_extend (img_pat, CAIRO_EXTEND_NONE); 0117 if (zoom) { 0118 cairo_matrix_t mat; 0119 Single zx, zy, zw, zh; 0120 zoom->calcSizes (NULL, NULL, width, height, zx, zy, zw, zh); 0121 cairo_matrix_init_translate (&mat, zx, zy); 0122 cairo_matrix_scale (&mat, 1.0 * zw/w, 1.0 * zh/h); 0123 cairo_pattern_set_matrix (img_pat, &mat); 0124 } else if (w != width && h != height) { 0125 cairo_matrix_t mat; 0126 cairo_matrix_init_scale (&mat, 1.0 * width/w, 1.0 * height/h); 0127 cairo_pattern_set_matrix (img_pat, &mat); 0128 } 0129 if (!s->surface) 0130 s->surface = cairo_surface_create_similar (similar, 0131 has_alpha ? 0132 CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR, w, h); 0133 else 0134 clear = true; 0135 cairo_t *cr = cairo_create (s->surface); 0136 if (clear) 0137 clearSurface (cr, IRect (0, 0, w, h)); 0138 cairo_set_source (cr, img_pat); 0139 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 0140 cairo_paint (cr); 0141 cairo_destroy (cr); 0142 0143 cairo_pattern_destroy (img_pat); 0144 if (!surface) 0145 cairo_surface_destroy (src_sf); 0146 } 0147 #endif 0148 0149 //------------------------------------------------------------------------- 0150 0151 #define REGION_SCROLLBAR_WIDTH 20 0152 0153 #ifdef KMPLAYER_WITH_CAIRO 0154 0155 # define CAIRO_SET_SOURCE_RGB(cr,c) \ 0156 cairo_set_source_rgb ((cr), \ 0157 1.0 * (((c) >> 16) & 0xff) / 255, \ 0158 1.0 * (((c) >> 8) & 0xff) / 255, \ 0159 1.0 * (((c)) & 0xff) / 255) 0160 0161 # define CAIRO_SET_SOURCE_ARGB(cr,c) \ 0162 cairo_set_source_rgba ((cr), \ 0163 1.0 * (((c) >> 16) & 0xff) / 255, \ 0164 1.0 * (((c) >> 8) & 0xff) / 255, \ 0165 1.0 * (((c)) & 0xff) / 255, \ 0166 1.0 * (((c) >> 24) & 0xff) / 255) 0167 0168 struct KMPLAYER_NO_EXPORT PaintContext 0169 { 0170 PaintContext (const Matrix& m, const IRect& c) 0171 : matrix (m) 0172 , clip (c) 0173 , fit (fit_default) 0174 , bg_repeat (SMIL::RegionBase::BgRepeat) 0175 , bg_image (NULL) 0176 {} 0177 Matrix matrix; 0178 IRect clip; 0179 Fit fit; 0180 SMIL::RegionBase::BackgroundRepeat bg_repeat; 0181 ImageData *bg_image; 0182 }; 0183 0184 class KMPLAYER_NO_EXPORT CairoPaintVisitor : public Visitor, public PaintContext { 0185 cairo_surface_t * cairo_surface; 0186 // stack vars need for transitions 0187 TransitionModule *cur_transition; 0188 cairo_pattern_t * cur_pat; 0189 cairo_matrix_t cur_mat; 0190 float opacity; 0191 bool toplevel; 0192 0193 void traverseRegion (Node *reg, Surface *s); 0194 void updateExternal (SMIL::MediaType *av, SurfacePtr s); 0195 void paint (TransitionModule *trans, MediaOpacity mopacity, Surface *s, 0196 const IPoint &p, const IRect &); 0197 void video (Mrl *mt, Surface *s); 0198 public: 0199 cairo_t * cr; 0200 CairoPaintVisitor (cairo_surface_t * cs, Matrix m, 0201 const IRect & rect, QColor c=QColor(), bool toplevel=false); 0202 ~CairoPaintVisitor (); 0203 using Visitor::visit; 0204 void visit (Node *); 0205 void visit (SMIL::Smil *); 0206 void visit (SMIL::Layout *); 0207 void visit (SMIL::RegionBase *); 0208 void visit (SMIL::Transition *); 0209 void visit (SMIL::TextMediaType *); 0210 void visit (SMIL::Brush *); 0211 void visit (SMIL::SmilText *); 0212 void visit (SMIL::RefMediaType *); 0213 void visit (RP::Imfl *); 0214 void visit (RP::Fill *); 0215 void visit (RP::Fadein *); 0216 void visit (RP::Fadeout *); 0217 void visit (RP::Crossfade *); 0218 void visit (RP::Wipe *); 0219 void visit (RP::ViewChange *); 0220 }; 0221 0222 KDE_NO_CDTOR_EXPORT 0223 CairoPaintVisitor::CairoPaintVisitor (cairo_surface_t * cs, Matrix m, 0224 const IRect & rect, QColor c, bool top) 0225 : PaintContext (m, rect), cairo_surface (cs), toplevel (top) 0226 { 0227 cr = cairo_create (cs); 0228 if (toplevel) { 0229 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 0230 cairo_set_tolerance (cr, 0.5 ); 0231 //cairo_push_group (cr); 0232 cairo_set_source_rgb (cr, 0233 1.0 * c.red () / 255, 1.0 * c.green () / 255, 1.0 * c.blue () / 255); 0234 cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height()); 0235 cairo_fill (cr); 0236 } else { 0237 clearSurface (cr, rect); 0238 } 0239 } 0240 0241 KDE_NO_CDTOR_EXPORT CairoPaintVisitor::~CairoPaintVisitor () { 0242 /*if (toplevel) { 0243 cairo_pattern_t * pat = cairo_pop_group (cr); 0244 //cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 0245 cairo_set_source (cr, pat); 0246 cairo_rectangle (cr, clip.x, clip.y, clip.w, clip.h); 0247 cairo_fill (cr); 0248 cairo_pattern_destroy (pat); 0249 }*/ 0250 cairo_destroy (cr); 0251 } 0252 0253 KDE_NO_EXPORT void CairoPaintVisitor::visit (Node * n) { 0254 kWarning() << "Paint called on " << n->nodeName(); 0255 } 0256 0257 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Smil *s) { 0258 if (s->active () && s->layout_node) 0259 s->layout_node->accept (this); 0260 } 0261 0262 KDE_NO_EXPORT void CairoPaintVisitor::traverseRegion (Node *node, Surface *s) { 0263 ConnectionList *nl = nodeMessageReceivers (node, MsgSurfaceAttach); 0264 if (nl) { 0265 for (Connection *c = nl->first(); c; c = nl->next ()) 0266 if (c->connecter) 0267 c->connecter->accept (this); 0268 } 0269 /*for (SurfacePtr c = s->lastChild (); c; c = c->previousSibling ()) { 0270 if (c->node && c->node->id != SMIL::id_node_region && 0271 c->node && c->node->id != SMIL::id_node_root_layout) 0272 c->node->accept (this); 0273 else 0274 break; 0275 }*/ 0276 // finally visit region children 0277 for (SurfacePtr c = s->firstChild (); c; c = c->nextSibling ()) { 0278 if (c->node && c->node->id == SMIL::id_node_region) 0279 c->node->accept (this); 0280 else 0281 break; 0282 } 0283 s->dirty = false; 0284 } 0285 0286 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Layout *layout) { 0287 if (layout->root_layout) 0288 layout->root_layout->accept (this); 0289 } 0290 0291 0292 static void cairoDrawRect (cairo_t *cr, unsigned int color, 0293 int x, int y, int w, int h) { 0294 CAIRO_SET_SOURCE_ARGB (cr, color); 0295 cairo_rectangle (cr, x, y, w, h); 0296 cairo_fill (cr); 0297 } 0298 0299 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::RegionBase *reg) { 0300 Surface *s = (Surface *) reg->role (RoleDisplay); 0301 if (s) { 0302 SRect rect = s->bounds; 0303 0304 IRect scr = matrix.toScreen (rect); 0305 if (clip.intersect (scr).isEmpty ()) 0306 return; 0307 PaintContext ctx_save = *(PaintContext *) this; 0308 matrix = Matrix (rect.x(), rect.y(), s->xscale, s->yscale); 0309 matrix.transform (ctx_save.matrix); 0310 clip = clip.intersect (scr); 0311 if (SMIL::RegionBase::BgInherit != reg->bg_repeat) 0312 bg_repeat = reg->bg_repeat; 0313 cairo_save (cr); 0314 0315 Surface *cs = s->firstChild (); 0316 if (!s->virtual_size.isEmpty ()) 0317 matrix.translate (-s->x_scroll, -s->y_scroll); 0318 0319 if (fit_default != reg->fit) 0320 fit = reg->fit; 0321 0322 ImageMedia *im = reg->media_info 0323 ? (ImageMedia *) reg->media_info->media 0324 : NULL; 0325 0326 ImageData *bg_img = im && !im->isEmpty() ? im->cached_img.ptr () : NULL; 0327 if (reg->background_image == "inherit") 0328 bg_img = bg_image; 0329 else 0330 bg_image = bg_img; 0331 unsigned int bg_alpha = s->background_color & 0xff000000; 0332 if ((SMIL::RegionBase::ShowAlways == reg->show_background || 0333 reg->m_AttachedMediaTypes.first ()) && 0334 (bg_alpha || bg_img)) { 0335 cairo_save (cr); 0336 if (bg_alpha) { 0337 cairo_rectangle (cr, 0338 clip.x (), clip.y (), clip.width (), clip.height ()); 0339 if (bg_alpha < 0xff000000) { 0340 CAIRO_SET_SOURCE_ARGB (cr, s->background_color); 0341 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 0342 cairo_fill (cr); 0343 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 0344 } else { 0345 CAIRO_SET_SOURCE_RGB (cr, s->background_color); 0346 cairo_fill (cr); 0347 } 0348 } 0349 if (bg_img) { 0350 Single w = bg_img->width; 0351 Single h = bg_img->height; 0352 matrix.getWH (w, h); 0353 if (!s->surface) 0354 bg_img->copyImage (s, SSize (w, h), cairo_surface); 0355 if (bg_img->has_alpha) 0356 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 0357 cairo_pattern_t *pat = cairo_pattern_create_for_surface (s->surface); 0358 cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT); 0359 cairo_matrix_t mat; 0360 cairo_matrix_init_translate (&mat, -scr.x (), -scr.y ()); 0361 cairo_pattern_set_matrix (pat, &mat); 0362 cairo_set_source (cr, pat); 0363 int cw = clip.width (); 0364 int ch = clip.height (); 0365 switch (bg_repeat) { 0366 case SMIL::RegionBase::BgRepeatX: 0367 if (h < ch) 0368 ch = h; 0369 break; 0370 case SMIL::RegionBase::BgRepeatY: 0371 if (w < cw) 0372 cw = w; 0373 break; 0374 case SMIL::RegionBase::BgNoRepeat: 0375 if (w < cw) 0376 cw = w; 0377 if (h < ch) 0378 ch = h; 0379 break; 0380 default: 0381 break; 0382 } 0383 cairo_rectangle (cr, clip.x (), clip.y (), cw, ch); 0384 cairo_fill (cr); 0385 cairo_pattern_destroy (pat); 0386 if (bg_img->has_alpha) 0387 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 0388 } 0389 cairo_restore (cr); 0390 } 0391 traverseRegion (reg, s); 0392 cs = s->firstChild (); 0393 if (cs && (s->scroll || cs->scroll) && cs == s->lastChild ()) { 0394 SRect r = cs->bounds; 0395 if (r.width () > rect.width () || r.height () > rect.height ()) { 0396 if (s->virtual_size.isEmpty ()) 0397 s->x_scroll = s->y_scroll = 0; 0398 s->virtual_size = r.size; 0399 matrix.getWH (s->virtual_size.width, s->virtual_size.height); 0400 s->virtual_size.width += REGION_SCROLLBAR_WIDTH; 0401 s->virtual_size.height += REGION_SCROLLBAR_WIDTH; 0402 const int vy = s->virtual_size.height; 0403 const int vw = s->virtual_size.width; 0404 int sbw = REGION_SCROLLBAR_WIDTH; 0405 int sbx = scr.x () + scr.width () - sbw; 0406 int sby = scr.y (); 0407 int sbh = scr.height () - REGION_SCROLLBAR_WIDTH; 0408 IRect sb_clip = clip.intersect (IRect (sbx, sby, sbw, sbh)); 0409 if (!sb_clip.isEmpty ()) { 0410 int knob_h = sbh * scr.height () / vy; 0411 int knob_y = scr.y () + s->y_scroll * sbh / vy; 0412 IRect knob (sbx, knob_y, sbw, knob_h); 0413 cairo_save (cr); 0414 cairo_rectangle (cr, sb_clip.x (), sb_clip.y (), 0415 sb_clip.width (), sb_clip.height ()); 0416 cairo_clip (cr); 0417 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 0418 cairo_set_line_width (cr, 2); 0419 CAIRO_SET_SOURCE_ARGB (cr, 0x80A0A0A0); 0420 cairo_rectangle (cr, sbx + 1, sby + 1, sbw - 2, sbh - 2); 0421 cairo_stroke (cr); 0422 if (s->y_scroll) 0423 cairoDrawRect (cr, 0x80000000, 0424 sbx + 2, sby + 2, 0425 sbw - 4, knob.y() - sby - 2); 0426 cairoDrawRect (cr, 0x80808080, 0427 knob.x() + 2, knob.y(), 0428 knob.width() - 4, knob.height()); 0429 if (sby + sbh - knob.y() - knob.height() - 2 > 0) 0430 cairoDrawRect (cr, 0x80000000, 0431 sbx + 2, knob.y() + knob.height(), 0432 sbw - 4, sby + sbh - knob.y() -knob.height()-2); 0433 cairo_restore (cr); 0434 } 0435 sbh = REGION_SCROLLBAR_WIDTH; 0436 sbx = scr.x (); 0437 sby = scr.y () + scr.height () - sbh; 0438 sbw = scr.width () - REGION_SCROLLBAR_WIDTH; 0439 sb_clip = clip.intersect (IRect (sbx, sby, sbw, sbh)); 0440 if (!sb_clip.isEmpty ()) { 0441 int knob_w = sbw * scr.width () / vw; 0442 int knob_x = scr.x () + s->x_scroll * sbw / vw; 0443 IRect knob (knob_x, sby, knob_w, sbh); 0444 cairo_save (cr); 0445 cairo_rectangle (cr, sb_clip.x (), sb_clip.y (), 0446 sb_clip.width (), sb_clip.height ()); 0447 cairo_clip (cr); 0448 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 0449 cairo_set_line_width (cr, 2); 0450 CAIRO_SET_SOURCE_ARGB (cr, 0x80A0A0A0); 0451 cairo_rectangle (cr, sbx + 1, sby + 1, sbw - 2, sbh - 2); 0452 cairo_stroke (cr); 0453 if (s->x_scroll) 0454 cairoDrawRect (cr, 0x80000000, 0455 sbx + 2, sby + 2, 0456 knob.x() - sbx - 2, sbh - 4); 0457 cairoDrawRect (cr, 0x80808080, 0458 knob.x(), knob.y() + 2, 0459 knob.width(), knob.height() - 4); 0460 if (sbx + sbw - knob.x() - knob.width() - 2 > 0) 0461 cairoDrawRect (cr, 0x80000000, 0462 knob.x() + knob.width(), sby + 2, 0463 sbx + sbw - knob.x() - knob.width()-2, sbh - 4); 0464 cairo_restore (cr); 0465 } 0466 } 0467 } 0468 cairo_restore (cr); 0469 *(PaintContext *) this = ctx_save; 0470 s->dirty = false; 0471 } 0472 } 0473 0474 #define CAIRO_SET_PATTERN_COND(cr,pat,mat) \ 0475 if (pat) { \ 0476 cairo_pattern_set_extend (cur_pat, CAIRO_EXTEND_NONE); \ 0477 cairo_pattern_set_matrix (pat, &mat); \ 0478 cairo_pattern_set_filter (pat, CAIRO_FILTER_FAST); \ 0479 cairo_set_source (cr, pat); \ 0480 } 0481 0482 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Transition *trans) { 0483 float perc = trans->start_progress + (trans->end_progress - trans->start_progress)*cur_transition->trans_gain; 0484 if (cur_transition->trans_out_active) 0485 perc = 1.0 - perc; 0486 if (SMIL::Transition::Fade == trans->type) { 0487 CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat) 0488 cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height()); 0489 opacity = perc; 0490 } else if (SMIL::Transition::BarWipe == trans->type) { 0491 IRect rect; 0492 if (SMIL::Transition::SubTopToBottom == trans->sub_type) { 0493 if (SMIL::Transition::dir_reverse == trans->direction) { 0494 int dy = (int) ((1.0 - perc) * clip.height ()); 0495 rect = IRect (clip.x (), clip.y () + dy, 0496 clip.width (), clip.height () - dy); 0497 } else { 0498 rect = IRect (clip.x (), clip.y (), 0499 clip.width (), (int) (perc * clip.height ())); 0500 } 0501 } else { 0502 if (SMIL::Transition::dir_reverse == trans->direction) { 0503 int dx = (int) ((1.0 - perc) * clip.width ()); 0504 rect = IRect (clip.x () + dx, clip.y (), 0505 clip.width () - dx, clip.height ()); 0506 } else { 0507 rect = IRect (clip.x (), clip.y (), 0508 (int) (perc * clip.width ()), clip.height ()); 0509 } 0510 } 0511 cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height()); 0512 CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat) 0513 } else if (SMIL::Transition::PushWipe == trans->type) { 0514 int dx = 0, dy = 0; 0515 if (SMIL::Transition::SubFromTop == trans->sub_type) 0516 dy = -(int) ((1.0 - perc) * clip.height ()); 0517 else if (SMIL::Transition::SubFromRight == trans->sub_type) 0518 dx = (int) ((1.0 - perc) * clip.width ()); 0519 else if (SMIL::Transition::SubFromBottom == trans->sub_type) 0520 dy = (int) ((1.0 - perc) * clip.height ()); 0521 else //if (SMIL::Transition::SubFromLeft == trans->sub_type) 0522 dx = -(int) ((1.0 - perc) * clip.width ()); 0523 cairo_matrix_translate (&cur_mat, -dx, -dy); 0524 IRect rect = clip.intersect (IRect (clip.x () + dx, clip.y () + dy, 0525 clip.width (), clip.height ())); 0526 cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height()); 0527 CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat) 0528 } else if (SMIL::Transition::IrisWipe == trans->type) { 0529 CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat) 0530 if (SMIL::Transition::SubDiamond == trans->sub_type) { 0531 cairo_rectangle (cr, clip.x(), clip.y(),clip.width(),clip.height()); 0532 cairo_clip (cr); 0533 int dx = (int) (perc * clip.width ()); 0534 int dy = (int) (perc * clip.height ()); 0535 int mx = clip.x () + clip.width ()/2; 0536 int my = clip.y () + clip.height ()/2; 0537 cairo_new_path (cr); 0538 cairo_move_to (cr, mx, my - dy); 0539 cairo_line_to (cr, mx + dx, my); 0540 cairo_line_to (cr, mx, my + dy); 0541 cairo_line_to (cr, mx - dx, my); 0542 cairo_close_path (cr); 0543 } else { // SubRectangle 0544 int dx = (int) (0.5 * (1 - perc) * clip.width ()); 0545 int dy = (int) (0.5 * (1 - perc) * clip.height ()); 0546 cairo_rectangle (cr, clip.x () + dx, clip.y () + dy, 0547 clip.width () - 2 * dx, clip.height () -2 * dy); 0548 } 0549 } else if (SMIL::Transition::ClockWipe == trans->type) { 0550 cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height()); 0551 cairo_clip (cr); 0552 int mx = clip.x () + clip.width ()/2; 0553 int my = clip.y () + clip.height ()/2; 0554 cairo_new_path (cr); 0555 cairo_move_to (cr, mx, my); 0556 float hw = 1.0 * clip.width ()/2; 0557 float hh = 1.0 * clip.height ()/2; 0558 float radius = sqrtf (hw * hw + hh * hh); 0559 float phi; 0560 switch (trans->sub_type) { 0561 case SMIL::Transition::SubClockwiseThree: 0562 phi = 0; 0563 break; 0564 case SMIL::Transition::SubClockwiseSix: 0565 phi = M_PI / 2; 0566 break; 0567 case SMIL::Transition::SubClockwiseNine: 0568 phi = M_PI; 0569 break; 0570 default: // Twelve 0571 phi = -M_PI / 2; 0572 break; 0573 } 0574 if (SMIL::Transition::dir_reverse == trans->direction) 0575 cairo_arc_negative (cr, mx, my, radius, phi, phi - 2 * M_PI * perc); 0576 else 0577 cairo_arc (cr, mx, my, radius, phi, phi + 2 * M_PI * perc); 0578 cairo_close_path (cr); 0579 CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat) 0580 } else if (SMIL::Transition::BowTieWipe == trans->type) { 0581 cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height()); 0582 cairo_clip (cr); 0583 int mx = clip.x () + clip.width ()/2; 0584 int my = clip.y () + clip.height ()/2; 0585 cairo_new_path (cr); 0586 cairo_move_to (cr, mx, my); 0587 float hw = 1.0 * clip.width ()/2; 0588 float hh = 1.0 * clip.height ()/2; 0589 float radius = sqrtf (hw * hw + hh * hh); 0590 float phi; 0591 switch (trans->sub_type) { 0592 case SMIL::Transition::SubHorizontal: 0593 phi = 0; 0594 break; 0595 default: // Vertical 0596 phi = -M_PI / 2; 0597 break; 0598 } 0599 float dphi = 0.5 * M_PI * perc; 0600 cairo_arc (cr, mx, my, radius, phi - dphi, phi + dphi); 0601 cairo_close_path (cr); 0602 cairo_new_sub_path (cr); 0603 cairo_move_to (cr, mx, my); 0604 if (SMIL::Transition::SubHorizontal == trans->sub_type) 0605 cairo_arc (cr, mx, my, radius, M_PI + phi - dphi, M_PI + phi +dphi); 0606 else 0607 cairo_arc (cr, mx, my, radius, -phi - dphi, -phi + dphi); 0608 cairo_close_path (cr); 0609 CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat) 0610 } else if (SMIL::Transition::EllipseWipe == trans->type) { 0611 cairo_rectangle (cr, clip.x(), clip.y(), clip.width(), clip.height()); 0612 cairo_clip (cr); 0613 int mx = clip.x () + clip.width ()/2; 0614 int my = clip.y () + clip.height ()/2; 0615 float hw = (double) clip.width ()/2; 0616 float hh = (double) clip.height ()/2; 0617 float radius = sqrtf (hw * hw + hh * hh); 0618 cairo_save (cr); 0619 cairo_new_path (cr); 0620 cairo_translate (cr, (int) mx, (int) my); 0621 cairo_move_to (cr, - Single (radius), 0); 0622 if (SMIL::Transition::SubHorizontal == trans->sub_type) 0623 cairo_scale (cr, 1.0, 0.6); 0624 else if (SMIL::Transition::SubVertical == trans->sub_type) 0625 cairo_scale (cr, 0.6, 1.0); 0626 cairo_arc (cr, 0, 0, perc * radius, 0, 2 * M_PI); 0627 cairo_close_path (cr); 0628 cairo_restore (cr); 0629 CAIRO_SET_PATTERN_COND(cr, cur_pat, cur_mat) 0630 } 0631 } 0632 0633 KDE_NO_EXPORT void CairoPaintVisitor::video (Mrl *m, Surface *s) { 0634 if (m->media_info && 0635 m->media_info->media && 0636 (MediaManager::Audio == m->media_info->type || 0637 MediaManager::AudioVideo == m->media_info->type)) { 0638 AudioVideoMedia *avm = static_cast<AudioVideoMedia *> (m->media_info->media); 0639 if (avm->viewer ()) { 0640 if (s && 0641 avm->process && 0642 avm->process->state () > IProcess::Ready && 0643 strcmp (m->nodeName (), "audio")) { 0644 s->xscale = s->yscale = 1; // either scale width/height or use bounds 0645 avm->viewer ()->setGeometry (s->toScreen (s->bounds.size)); 0646 } else { 0647 avm->viewer ()->setGeometry (IRect (-60, -60, 50, 50)); 0648 } 0649 } 0650 } 0651 } 0652 0653 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::RefMediaType *ref) { 0654 Surface *s = ref->surface (); 0655 if (s && ref->external_tree) { 0656 updateExternal (ref, s); 0657 return; 0658 } 0659 if (!ref->media_info) 0660 return; 0661 if (fit_default != fit 0662 && fit_default == ref->fit 0663 && fit != ref->effective_fit) { 0664 ref->effective_fit = fit; 0665 s->resize (ref->calculateBounds(), false); 0666 } 0667 if (ref->media_info->media && 0668 ref->media_info->media->type () == MediaManager::Image) { 0669 if (!s) 0670 return; 0671 0672 IRect scr = matrix.toScreen (s->bounds); 0673 IRect clip_rect = clip.intersect (scr); 0674 if (clip_rect.isEmpty ()) 0675 return; 0676 0677 ImageMedia *im = static_cast <ImageMedia *> (ref->media_info->media); 0678 ImageData *id = im ? im->cached_img.ptr () : NULL; 0679 if (id && id->flags == ImageData::ImageScalable) 0680 im->render (scr.size); 0681 if (!id || im->isEmpty () || ref->size.isEmpty ()) { 0682 s->remove(); 0683 return; 0684 } 0685 if (!s->surface || s->dirty) 0686 id->copyImage (s, SSize (scr.width (), scr.height ()), cairo_surface, ref->pan_zoom); 0687 paint (&ref->transition, ref->media_opacity, s, scr.point, clip_rect); 0688 s->dirty = false; 0689 } else { 0690 video (ref, s); 0691 } 0692 } 0693 0694 KDE_NO_EXPORT void CairoPaintVisitor::paint (TransitionModule *trans, 0695 MediaOpacity mopacity, Surface *s, 0696 const IPoint &point, const IRect &rect) { 0697 cairo_save (cr); 0698 opacity = 1.0; 0699 cairo_matrix_init_translate (&cur_mat, -point.x, -point.y); 0700 cur_pat = cairo_pattern_create_for_surface (s->surface); 0701 if (trans->active_trans) { 0702 IRect clip_save = clip; 0703 clip = rect; 0704 cur_transition = trans; 0705 trans->active_trans->accept (this); 0706 clip = clip_save; 0707 } else { 0708 cairo_pattern_set_extend (cur_pat, CAIRO_EXTEND_NONE); 0709 cairo_pattern_set_matrix (cur_pat, &cur_mat); 0710 cairo_pattern_set_filter (cur_pat, CAIRO_FILTER_FAST); 0711 cairo_set_source (cr, cur_pat); 0712 cairo_rectangle (cr, rect.x(), rect.y(), rect.width(), rect.height()); 0713 } 0714 opacity *= mopacity.opacity / 100.0; 0715 bool over = opacity < 0.99 || 0716 CAIRO_CONTENT_COLOR != cairo_surface_get_content (s->surface); 0717 cairo_operator_t op; 0718 if (over) { 0719 op = cairo_get_operator (cr); 0720 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 0721 } 0722 if (opacity < 0.99) { 0723 cairo_clip (cr); 0724 cairo_paint_with_alpha (cr, opacity); 0725 } else { 0726 cairo_fill (cr); 0727 } 0728 if (over) 0729 cairo_set_operator (cr, op); 0730 cairo_pattern_destroy (cur_pat); 0731 cairo_restore (cr); 0732 } 0733 0734 static Mrl *findActiveMrl (Node *n, bool *rp_or_smil) { 0735 Mrl *mrl = n->mrl (); 0736 if (mrl) { 0737 *rp_or_smil = (mrl->id >= SMIL::id_node_first && 0738 mrl->id < SMIL::id_node_last) || 0739 (mrl->id >= RP::id_node_first && 0740 mrl->id < RP::id_node_last); 0741 if (*rp_or_smil || 0742 (mrl->media_info && 0743 (MediaManager::Audio == mrl->media_info->type || 0744 MediaManager::AudioVideo == mrl->media_info->type))) 0745 return mrl; 0746 } 0747 for (Node *c = n->firstChild (); c; c = c->nextSibling ()) 0748 if (c->active ()) { 0749 Mrl *m = findActiveMrl (c, rp_or_smil); 0750 if (m) 0751 return m; 0752 } 0753 return NULL; 0754 } 0755 0756 KDE_NO_EXPORT 0757 void CairoPaintVisitor::updateExternal (SMIL::MediaType *av, SurfacePtr s) { 0758 bool rp_or_smil = false; 0759 Mrl *ext_mrl = findActiveMrl (av->external_tree.ptr (), &rp_or_smil); 0760 if (!ext_mrl) 0761 return; 0762 if (!rp_or_smil) { 0763 video (ext_mrl, s.ptr ()); 0764 return; 0765 } 0766 IRect scr = matrix.toScreen (s->bounds); 0767 IRect clip_rect = clip.intersect (scr); 0768 if (clip_rect.isEmpty ()) 0769 return; 0770 if (!s->surface || s->dirty) { 0771 Matrix m = matrix; 0772 m.translate (-scr.x (), -scr.y ()); 0773 m.scale (s->xscale, s->yscale); 0774 IRect r (clip_rect.x() - scr.x () - 1, clip_rect.y() - scr.y () - 1, 0775 clip_rect.width() + 3, clip_rect.height() + 3); 0776 if (!s->surface) { 0777 s->surface = cairo_surface_create_similar (cairo_surface, 0778 CAIRO_CONTENT_COLOR_ALPHA, scr.width (), scr.height ()); 0779 r = IRect (0, 0, scr.size); 0780 } 0781 CairoPaintVisitor visitor (s->surface, m, r); 0782 ext_mrl->accept (&visitor); 0783 s->dirty = false; 0784 } 0785 paint (&av->transition, av->media_opacity, s.ptr (), scr.point, clip_rect); 0786 } 0787 0788 static void setAlignment (QTextDocument &td, unsigned char align) { 0789 QTextOption opt = td.defaultTextOption(); 0790 if (SmilTextProperties::AlignLeft == align) 0791 opt.setAlignment (Qt::AlignLeft); 0792 else if (SmilTextProperties::AlignCenter == align) 0793 opt.setAlignment (Qt::AlignCenter); 0794 else if (SmilTextProperties::AlignRight == align) 0795 opt.setAlignment (Qt::AlignRight); 0796 td.setDefaultTextOption (opt); 0797 } 0798 0799 static void calculateTextDimensions (const QFont& font, 0800 const QString& text, Single w, Single h, Single maxh, 0801 int *pxw, int *pxh, bool markup_text, 0802 unsigned char align = SmilTextProperties::AlignLeft) { 0803 QTextDocument td; 0804 td.setDefaultFont( font ); 0805 td.setDocumentMargin (0); 0806 QImage img (QSize ((int)w, (int)h), QImage::Format_RGB32); 0807 td.setPageSize (QSize ((int)w, (int)maxh)); 0808 td.documentLayout()->setPaintDevice (&img); 0809 if (markup_text) 0810 td.setHtml( text ); 0811 else 0812 td.setPlainText( text ); 0813 setAlignment (td, align); 0814 QRectF r = td.documentLayout()->blockBoundingRect (td.lastBlock()); 0815 *pxw = (int)td.idealWidth (); 0816 *pxh = (int)(r.y() + r.height()); 0817 #if QT_VERSION >= 0x050600 0818 *pxw = qMin( (int)(*pxw + pixel_device_ratio), (int)w); 0819 #endif 0820 } 0821 0822 static cairo_t *createContext (cairo_surface_t *similar, Surface *s, int w, int h) { 0823 unsigned int bg_alpha = s->background_color & 0xff000000; 0824 bool clear = s->surface; 0825 if (!s->surface) 0826 s->surface = cairo_surface_create_similar (similar, 0827 bg_alpha < 0xff000000 0828 ? CAIRO_CONTENT_COLOR_ALPHA 0829 : CAIRO_CONTENT_COLOR, 0830 w, h); 0831 cairo_t *cr = cairo_create (s->surface); 0832 if (clear) 0833 clearSurface (cr, IRect (0, 0, w, h)); 0834 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 0835 0836 if (bg_alpha) { 0837 if (bg_alpha < 0xff000000) 0838 CAIRO_SET_SOURCE_ARGB (cr, s->background_color); 0839 else 0840 CAIRO_SET_SOURCE_RGB (cr, s->background_color); 0841 cairo_paint (cr); 0842 } 0843 return cr; 0844 } 0845 0846 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::TextMediaType * txt) { 0847 if (!txt->media_info || !txt->media_info->media) 0848 return; 0849 TextMedia *tm = static_cast <TextMedia *> (txt->media_info->media); 0850 Surface *s = txt->surface (); 0851 if (!s) 0852 return; 0853 if (!s->surface) { 0854 txt->size = SSize (); 0855 s->bounds = txt->calculateBounds (); 0856 } 0857 IRect scr = matrix.toScreen (s->bounds); 0858 if (!s->surface || s->dirty) { 0859 0860 int w = scr.width (); 0861 int pxw, pxh; 0862 Single ft_size = w * txt->font_size / (double)s->bounds.width (); 0863 bool clear = s->surface; 0864 0865 QFont font (txt->font_name); 0866 font.setPixelSize(ft_size); 0867 if (clear) { 0868 pxw = scr.width (); 0869 pxh = scr.height (); 0870 } else { 0871 calculateTextDimensions (font, tm->text, 0872 w, 2 * ft_size, scr.height (), &pxw, &pxh, false); 0873 } 0874 QTextDocument td; 0875 td.setDocumentMargin (0); 0876 td.setDefaultFont (font); 0877 bool have_alpha = (s->background_color & 0xff000000) < 0xff000000; 0878 QImage img (QSize (pxw, pxh), have_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32); 0879 img.fill (s->background_color); 0880 td.setPageSize (QSize (pxw, pxh + (int)ft_size)); 0881 td.documentLayout()->setPaintDevice (&img); 0882 setAlignment (td, 1 + (int)txt->halign); 0883 td.setPlainText (tm->text); 0884 QPainter painter; 0885 painter.begin (&img); 0886 QAbstractTextDocumentLayout::PaintContext ctx; 0887 ctx.clip = QRect (0, 0, pxw, pxh); 0888 ctx.palette.setColor (QPalette::Text, QColor (QRgb (txt->font_color))); 0889 td.documentLayout()->draw (&painter, ctx); 0890 painter.end(); 0891 0892 cairo_t *cr_txt = createContext (cairo_surface, s, pxw, pxh); 0893 cairo_surface_t *src_sf = cairo_image_surface_create_for_data ( 0894 img.bits (), 0895 have_alpha ? CAIRO_FORMAT_ARGB32:CAIRO_FORMAT_RGB24, 0896 img.width(), img.height(), img.bytesPerLine ()); 0897 cairo_pattern_t *pat = cairo_pattern_create_for_surface (src_sf); 0898 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 0899 cairo_set_operator (cr_txt, CAIRO_OPERATOR_SOURCE); 0900 cairo_set_source (cr_txt, pat); 0901 cairo_paint (cr_txt); 0902 cairo_pattern_destroy (pat); 0903 cairo_surface_destroy (src_sf); 0904 cairo_destroy (cr_txt); 0905 0906 // update bounds rect 0907 SRect rect = matrix.toUser (IRect (scr.point, ISize (pxw, pxh))); 0908 txt->size = rect.size; 0909 s->bounds = txt->calculateBounds (); 0910 0911 // update coord. for painting below 0912 scr = matrix.toScreen (s->bounds); 0913 } 0914 IRect clip_rect = clip.intersect (scr); 0915 if (!clip_rect.isEmpty ()) 0916 paint (&txt->transition, txt->media_opacity, s, scr.point, clip_rect); 0917 s->dirty = false; 0918 } 0919 0920 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::Brush * brush) { 0921 Surface *s = brush->surface (); 0922 if (s) { 0923 opacity = 1.0; 0924 IRect clip_rect = clip.intersect (matrix.toScreen (s->bounds)); 0925 if (clip_rect.isEmpty ()) 0926 return; 0927 cairo_save (cr); 0928 if (brush->transition.active_trans) { 0929 cur_transition = &brush->transition; 0930 cur_pat = NULL; 0931 brush->transition.active_trans->accept (this); 0932 } else { 0933 cairo_rectangle (cr, clip_rect.x (), clip_rect.y (), 0934 clip_rect.width (), clip_rect.height ()); 0935 } 0936 unsigned int color = brush->color.color; 0937 if (!color) { 0938 color = brush->background_color.color; 0939 opacity *= brush->background_color.opacity / 100.0; 0940 } else { 0941 opacity *= brush->color.opacity / 100.0; 0942 } 0943 opacity *= brush->media_opacity.opacity / 100.0; 0944 if (opacity < 0.99) { 0945 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 0946 cairo_set_source_rgba (cr, 0947 1.0 * ((color >> 16) & 0xff) / 255, 0948 1.0 * ((color >> 8) & 0xff) / 255, 0949 1.0 * (color & 0xff) / 255, 0950 opacity); 0951 } else { 0952 CAIRO_SET_SOURCE_RGB (cr, color); 0953 } 0954 cairo_fill (cr); 0955 if (opacity < 0.99) 0956 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 0957 s->dirty = false; 0958 cairo_restore (cr); 0959 } 0960 } 0961 0962 struct SmilTextBlock { 0963 SmilTextBlock (const QFont& f, const QString &t, 0964 IRect r, unsigned char a) 0965 : font (f), rich_text (t), rect (r), align (a), next (NULL) {} 0966 0967 QFont font; 0968 QString rich_text; 0969 IRect rect; 0970 unsigned char align; 0971 0972 SmilTextBlock *next; 0973 }; 0974 0975 struct KMPLAYER_NO_EXPORT SmilTextInfo { 0976 SmilTextInfo (const SmilTextProperties &p) : props (p) {} 0977 0978 void span (float scale); 0979 0980 SmilTextProperties props; 0981 QString span_text; 0982 }; 0983 0984 class KMPLAYER_NO_EXPORT SmilTextVisitor : public Visitor { 0985 public: 0986 SmilTextVisitor (int w, float s, const SmilTextProperties &p) 0987 : first (NULL), last (NULL), width (w), voffset (0), 0988 scale (s), max_font_size (0), info (p) { 0989 info.span (scale); 0990 } 0991 using Visitor::visit; 0992 void visit (TextNode *); 0993 void visit (SMIL::TextFlow *); 0994 void visit (SMIL::TemporalMoment *); 0995 0996 void addRichText (const QString &txt); 0997 void push (); 0998 0999 SmilTextBlock *first; 1000 SmilTextBlock *last; 1001 1002 int width; 1003 int voffset; 1004 float scale; 1005 float max_font_size; 1006 SmilTextInfo info; 1007 QString rich_text; 1008 }; 1009 1010 void SmilTextInfo::span (float scale) { 1011 QString s = "<span style=\""; 1012 if (props.font_size.size () > -1) 1013 s += "font-size:" + QString::number ((int)(scale * props.font_size.size ())) + "px;"; 1014 s += "font-family:" + props.font_family + ";"; 1015 if (props.font_color > -1) 1016 s += QString().sprintf ("color:#%06x;", props.font_color); 1017 if (props.background_color > -1) 1018 s += QString().sprintf ("background-color:#%06x;", props.background_color); 1019 if (SmilTextProperties::StyleInherit != props.font_style) { 1020 s += "font-style:"; 1021 switch (props.font_style) { 1022 case SmilTextProperties::StyleOblique: 1023 s += "oblique;"; 1024 break; 1025 case SmilTextProperties::StyleItalic: 1026 s += "italic;"; 1027 break; 1028 default: 1029 s += "normal;"; 1030 break; 1031 } 1032 } 1033 if (SmilTextProperties::WeightInherit != props.font_weight) { 1034 s += "font-weight:"; 1035 switch (props.font_weight) { 1036 case SmilTextProperties::WeightBold: 1037 s += "bold;"; 1038 break; 1039 default: 1040 s += "normal;"; 1041 break; 1042 } 1043 } 1044 s += "\">"; 1045 span_text = s; 1046 } 1047 1048 void SmilTextVisitor::addRichText (const QString &txt) { 1049 if (!info.span_text.isEmpty ()) 1050 rich_text += info.span_text; 1051 rich_text += txt; 1052 if (!info.span_text.isEmpty ()) 1053 rich_text += "</span>"; 1054 } 1055 1056 void SmilTextVisitor::push () { 1057 if (!rich_text.isEmpty ()) { 1058 int pxw, pxh; 1059 float fs = info.props.font_size.size (); 1060 if (fs < 0) 1061 fs = TextMedia::defaultFontSize (); 1062 float maxfs = max_font_size; 1063 if (maxfs < 1.0) 1064 maxfs = fs; 1065 fs *= scale; 1066 maxfs *= scale; 1067 1068 QFont font ("Sans"); 1069 font.setPixelSize((int)fs); 1070 calculateTextDimensions (font, rich_text.toUtf8 ().constData (), 1071 width, 2 * maxfs, 1024, &pxw, &pxh, true, info.props.text_align); 1072 int x = 0; 1073 if (SmilTextProperties::AlignCenter == info.props.text_align) 1074 x = (width - pxw) / 2; 1075 else if (SmilTextProperties::AlignRight == info.props.text_align) 1076 x = width - pxw; 1077 SmilTextBlock *block = new SmilTextBlock (font, rich_text, 1078 IRect (x, voffset, pxw, pxh), info.props.text_align); 1079 voffset += pxh; 1080 max_font_size = 0; 1081 rich_text.clear(); 1082 if (!first) { 1083 first = last = block; 1084 } else { 1085 last->next = block; 1086 last = block; 1087 } 1088 } 1089 } 1090 1091 void SmilTextVisitor::visit (TextNode *text) { 1092 QString buffer; 1093 QTextStream out (&buffer, QIODevice::WriteOnly); 1094 out << XMLStringlet (text->nodeValue ()); 1095 addRichText (buffer); 1096 if (text->nextSibling ()) 1097 text->nextSibling ()->accept (this); 1098 } 1099 1100 void SmilTextVisitor::visit (SMIL::TextFlow *flow) { 1101 bool new_block = SMIL::id_node_p == flow->id || 1102 SMIL::id_node_br == flow->id || 1103 SMIL::id_node_div == flow->id; 1104 if ((new_block && !rich_text.isEmpty ()) || flow->firstChild ()) { 1105 float fs = info.props.font_size.size (); 1106 if (fs < 0) 1107 fs = TextMedia::defaultFontSize (); 1108 int par_extra = SMIL::id_node_p == flow->id 1109 ? (int)(scale * fs) : 0; 1110 voffset += par_extra; 1111 1112 SmilTextInfo saved_info = info; 1113 if (new_block) 1114 push (); 1115 1116 info.props.mask (flow->props); 1117 if ((float)info.props.font_size.size () > max_font_size) 1118 max_font_size = info.props.font_size.size (); 1119 info.span (scale); 1120 1121 if (flow->firstChild ()) 1122 flow->firstChild ()->accept (this); 1123 1124 if (rich_text.isEmpty ()) 1125 par_extra = 0; 1126 if (new_block && flow->firstChild ()) 1127 push (); 1128 voffset += par_extra; 1129 1130 info = saved_info; 1131 } 1132 if (flow->nextSibling ()) 1133 flow->nextSibling ()->accept (this); 1134 } 1135 1136 void SmilTextVisitor::visit (SMIL::TemporalMoment *tm) { 1137 if (tm->state >= Node::state_began 1138 && tm->nextSibling ()) 1139 tm->nextSibling ()->accept (this); 1140 } 1141 1142 KDE_NO_EXPORT void CairoPaintVisitor::visit (SMIL::SmilText *txt) { 1143 Surface *s = txt->surface (); 1144 if (!s) 1145 return; 1146 1147 SRect rect = s->bounds; 1148 IRect scr = matrix.toScreen (rect); 1149 1150 if (!s->surface) { 1151 1152 int w = scr.width (); 1153 float scale = 1.0 * w / (double)s->bounds.width (); 1154 SmilTextVisitor info (w, scale, txt->props); 1155 1156 Node *first = txt->firstChild (); 1157 for (Node *n = first; n; n = n->nextSibling ()) 1158 if (SMIL::id_node_clear == n->id) { 1159 if (n->state >= Node::state_began) 1160 first = n->nextSibling (); 1161 else 1162 break; 1163 } 1164 if (first) 1165 first->accept (&info); 1166 1167 info.push (); 1168 if (info.first) { 1169 cairo_t *cr_txt = createContext (cairo_surface, s, (int) w, info.voffset); 1170 1171 CAIRO_SET_SOURCE_RGB (cr_txt, 0); 1172 SmilTextBlock *b = info.first; 1173 int hoff = 0; 1174 int voff = 0; 1175 while (b) { 1176 cairo_translate (cr_txt, b->rect.x() - hoff, b->rect.y() - voff); 1177 QTextDocument td; 1178 td.setDocumentMargin (0); 1179 td.setDefaultFont (b->font); 1180 bool have_alpha = (s->background_color & 0xff000000) < 0xff000000; 1181 QImage img (QSize (b->rect.width(), b->rect.height()), have_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32); 1182 img.fill (s->background_color); 1183 td.setPageSize (QSize (b->rect.width(), b->rect.height() + 10)); 1184 setAlignment (td, b->align); 1185 td.documentLayout()->setPaintDevice (&img); 1186 td.setHtml (b->rich_text); 1187 QPainter painter; 1188 painter.begin (&img); 1189 QAbstractTextDocumentLayout::PaintContext ctx; 1190 ctx.clip = QRect (QPoint (0, 0), img.size ()); 1191 td.documentLayout()->draw (&painter, ctx); 1192 painter.end(); 1193 1194 cairo_surface_t *src_sf = cairo_image_surface_create_for_data ( 1195 img.bits (), 1196 have_alpha ? CAIRO_FORMAT_ARGB32:CAIRO_FORMAT_RGB24, 1197 img.width(), img.height(), img.bytesPerLine ()); 1198 cairo_pattern_t *pat = cairo_pattern_create_for_surface (src_sf); 1199 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 1200 cairo_set_operator (cr_txt, CAIRO_OPERATOR_SOURCE); 1201 cairo_set_source (cr_txt, pat); 1202 cairo_rectangle (cr_txt, 0, 0, b->rect.width(), b->rect.height()); 1203 cairo_fill (cr_txt); 1204 cairo_pattern_destroy (pat); 1205 cairo_surface_destroy (src_sf); 1206 1207 hoff = b->rect.x (); 1208 voff = b->rect.y (); 1209 SmilTextBlock *tmp = b; 1210 b = b->next; 1211 delete tmp; 1212 } 1213 cairo_destroy (cr_txt); 1214 1215 // update bounds rect 1216 s->bounds = matrix.toUser (IRect (scr.point, ISize (w, info.voffset))); 1217 txt->size = s->bounds.size; 1218 txt->updateBounds (false); 1219 1220 // update coord. for painting below 1221 scr = matrix.toScreen (s->bounds); 1222 } 1223 } 1224 IRect clip_rect = clip.intersect (scr); 1225 if (s->surface && !clip_rect.isEmpty ()) 1226 paint (&txt->transition, txt->media_opacity, s, scr.point, clip_rect); 1227 s->dirty = false; 1228 } 1229 1230 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Imfl * imfl) { 1231 if (imfl->surface ()) { 1232 cairo_save (cr); 1233 Matrix m = matrix; 1234 IRect scr = matrix.toScreen (SRect (0, 0, imfl->rp_surface->bounds.size)); 1235 int w = scr.width (); 1236 int h = scr.height (); 1237 cairo_rectangle (cr, scr.x (), scr.y (), w, h); 1238 //cairo_clip (cr); 1239 cairo_translate (cr, scr.x (), scr.y ()); 1240 cairo_scale (cr, 1.0*w/(double)imfl->size.width, 1.0*h/(double)imfl->size.height); 1241 if (imfl->needs_scene_img) 1242 cairo_push_group (cr); 1243 for (NodePtr n = imfl->firstChild (); n; n = n->nextSibling ()) 1244 if (n->state >= Node::state_began && 1245 n->state < Node::state_deactivated) { 1246 RP::TimingsBase * tb = convertNode<RP::TimingsBase>(n); 1247 switch (n->id) { 1248 case RP::id_node_viewchange: 1249 if (!(int)tb->srcw) 1250 tb->srcw = imfl->size.width; 1251 if (!(int)tb->srch) 1252 tb->srch = imfl->size.height; 1253 // fall through 1254 case RP::id_node_crossfade: 1255 case RP::id_node_fadein: 1256 case RP::id_node_fadeout: 1257 case RP::id_node_fill: 1258 case RP::id_node_wipe: 1259 if (!(int)tb->w) 1260 tb->w = imfl->size.width; 1261 if (!(int)tb->h) 1262 tb->h = imfl->size.height; 1263 n->accept (this); 1264 break; 1265 } 1266 } 1267 if (imfl->needs_scene_img) { 1268 cairo_pattern_t * pat = cairo_pop_group (cr); 1269 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 1270 cairo_set_source (cr, pat); 1271 cairo_paint (cr); 1272 cairo_pattern_destroy (pat); 1273 } 1274 cairo_restore (cr); 1275 matrix = m; 1276 } 1277 } 1278 1279 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fill * fi) { 1280 CAIRO_SET_SOURCE_RGB (cr, fi->color); 1281 if ((int)fi->w && (int)fi->h) { 1282 cairo_rectangle (cr, fi->x, fi->y, fi->w, fi->h); 1283 cairo_fill (cr); 1284 } 1285 } 1286 1287 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fadein * fi) { 1288 if (fi->target && fi->target->id == RP::id_node_image) { 1289 RP::Image *img = convertNode <RP::Image> (fi->target); 1290 ImageMedia *im = img && img->media_info 1291 ? static_cast <ImageMedia*> (img->media_info->media) : NULL; 1292 if (im && img->surface ()) { 1293 Single sx = fi->srcx, sy = fi->srcy, sw = fi->srcw, sh = fi->srch; 1294 if (!(int)sw) 1295 sw = img->size.width; 1296 if (!(int)sh) 1297 sh = img->size.height; 1298 if ((int)fi->w && (int)fi->h && (int)sw && (int)sh) { 1299 if (!img->img_surface->surface) 1300 im->cached_img->copyImage (img->img_surface, 1301 img->size, cairo_surface); 1302 cairo_matrix_t matrix; 1303 cairo_matrix_init_identity (&matrix); 1304 float scalex = 1.0 * sw / fi->w; 1305 float scaley = 1.0 * sh / fi->h; 1306 cairo_matrix_scale (&matrix, scalex, scaley); 1307 cairo_matrix_translate (&matrix, 1308 1.0*sx/scalex - (double)fi->x, 1309 1.0*sy/scaley - (double)fi->y); 1310 cairo_save (cr); 1311 cairo_rectangle (cr, fi->x, fi->y, fi->w, fi->h); 1312 cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface); 1313 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 1314 cairo_pattern_set_matrix (pat, &matrix); 1315 cairo_set_source (cr, pat); 1316 cairo_clip (cr); 1317 cairo_paint_with_alpha (cr, 1.0 * fi->progress / 100); 1318 cairo_restore (cr); 1319 cairo_pattern_destroy (pat); 1320 } 1321 } 1322 } 1323 } 1324 1325 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Fadeout * fo) { 1326 if (fo->progress > 0) { 1327 CAIRO_SET_SOURCE_RGB (cr, fo->to_color); 1328 if ((int)fo->w && (int)fo->h) { 1329 cairo_save (cr); 1330 cairo_rectangle (cr, fo->x, fo->y, fo->w, fo->h); 1331 cairo_clip (cr); 1332 cairo_paint_with_alpha (cr, 1.0 * fo->progress / 100); 1333 cairo_restore (cr); 1334 } 1335 } 1336 } 1337 1338 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Crossfade * cf) { 1339 if (cf->target && cf->target->id == RP::id_node_image) { 1340 RP::Image *img = convertNode <RP::Image> (cf->target); 1341 ImageMedia *im = img && img->media_info 1342 ? static_cast <ImageMedia*> (img->media_info->media) : NULL; 1343 if (im && img->surface ()) { 1344 Single sx = cf->srcx, sy = cf->srcy, sw = cf->srcw, sh = cf->srch; 1345 if (!(int)sw) 1346 sw = img->size.width; 1347 if (!(int)sh) 1348 sh = img->size.height; 1349 if ((int)cf->w && (int)cf->h && (int)sw && (int)sh) { 1350 if (!img->img_surface->surface) 1351 im->cached_img->copyImage (img->img_surface, 1352 img->size, cairo_surface); 1353 cairo_save (cr); 1354 cairo_matrix_t matrix; 1355 cairo_matrix_init_identity (&matrix); 1356 float scalex = 1.0 * sw / cf->w; 1357 float scaley = 1.0 * sh / cf->h; 1358 cairo_matrix_scale (&matrix, scalex, scaley); 1359 cairo_matrix_translate (&matrix, 1360 1.0*sx/scalex - (double)cf->x, 1361 1.0*sy/scaley - (double)cf->y); 1362 cairo_rectangle (cr, cf->x, cf->y, cf->w, cf->h); 1363 cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface); 1364 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 1365 cairo_pattern_set_matrix (pat, &matrix); 1366 cairo_set_source (cr, pat); 1367 cairo_clip (cr); 1368 cairo_paint_with_alpha (cr, 1.0 * cf->progress / 100); 1369 cairo_restore (cr); 1370 cairo_pattern_destroy (pat); 1371 } 1372 } 1373 } 1374 } 1375 1376 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::Wipe * wipe) { 1377 if (wipe->target && wipe->target->id == RP::id_node_image) { 1378 RP::Image *img = convertNode <RP::Image> (wipe->target); 1379 ImageMedia *im = img && img->media_info 1380 ? static_cast <ImageMedia*> (img->media_info->media) : NULL; 1381 if (im && img->surface ()) { 1382 Single x = wipe->x, y = wipe->y; 1383 Single tx = x, ty = y; 1384 Single w = wipe->w, h = wipe->h; 1385 Single sx = wipe->srcx, sy = wipe->srcy, sw = wipe->srcw, sh = wipe->srch; 1386 if (!(int)sw) 1387 sw = img->size.width; 1388 if (!(int)sh) 1389 sh = img->size.height; 1390 if (wipe->direction == RP::Wipe::dir_right) { 1391 Single dx = w * 1.0 * wipe->progress / 100; 1392 tx = x -w + dx; 1393 w = dx; 1394 } else if (wipe->direction == RP::Wipe::dir_left) { 1395 Single dx = w * 1.0 * wipe->progress / 100; 1396 tx = x + w - dx; 1397 x = tx; 1398 w = dx; 1399 } else if (wipe->direction == RP::Wipe::dir_down) { 1400 Single dy = h * 1.0 * wipe->progress / 100; 1401 ty = y - h + dy; 1402 h = dy; 1403 } else if (wipe->direction == RP::Wipe::dir_up) { 1404 Single dy = h * 1.0 * wipe->progress / 100; 1405 ty = y + h - dy; 1406 y = ty; 1407 h = dy; 1408 } 1409 1410 if ((int)w && (int)h) { 1411 if (!img->img_surface->surface) 1412 im->cached_img->copyImage (img->img_surface, 1413 img->size, cairo_surface); 1414 cairo_matrix_t matrix; 1415 cairo_matrix_init_identity (&matrix); 1416 float scalex = 1.0 * sw / wipe->w; 1417 float scaley = 1.0 * sh / wipe->h; 1418 cairo_matrix_scale (&matrix, scalex, scaley); 1419 cairo_matrix_translate (&matrix, 1420 1.0*sx/scalex - (double)tx, 1421 1.0*sy/scaley - (double)ty); 1422 cairo_pattern_t *pat = cairo_pattern_create_for_surface (img->img_surface->surface); 1423 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 1424 cairo_pattern_set_matrix (pat, &matrix); 1425 cairo_set_source (cr, pat); 1426 cairo_rectangle (cr, x, y, w, h); 1427 cairo_fill (cr); 1428 cairo_pattern_destroy (pat); 1429 } 1430 } 1431 } 1432 } 1433 1434 KDE_NO_EXPORT void CairoPaintVisitor::visit (RP::ViewChange * vc) { 1435 if (vc->unfinished () || vc->progress < 100) { 1436 cairo_pattern_t * pat = cairo_pop_group (cr); // from imfl 1437 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 1438 cairo_push_group (cr); 1439 cairo_save (cr); 1440 cairo_set_source (cr, pat); 1441 cairo_paint (cr); 1442 if ((int)vc->w && (int)vc->h && (int)vc->srcw && (int)vc->srch) { 1443 cairo_matrix_t matrix; 1444 cairo_matrix_init_identity (&matrix); 1445 float scalex = 1.0 * vc->srcw / vc->w; 1446 float scaley = 1.0 * vc->srch / vc->h; 1447 cairo_matrix_scale (&matrix, scalex, scaley); 1448 cairo_matrix_translate (&matrix, 1449 1.0*vc->srcx/scalex - (double)vc->x, 1450 1.0*vc->srcy/scaley - (double)vc->y); 1451 cairo_pattern_set_matrix (pat, &matrix); 1452 cairo_set_source (cr, pat); 1453 cairo_rectangle (cr, vc->x, vc->y, vc->w, vc->h); 1454 cairo_fill (cr); 1455 } 1456 cairo_pattern_destroy (pat); 1457 cairo_restore (cr); 1458 } 1459 } 1460 1461 #endif 1462 1463 //----------------------------------------------------------------------------- 1464 1465 namespace KMPlayer { 1466 1467 class KMPLAYER_NO_EXPORT MouseVisitor : public Visitor { 1468 ViewArea *view_area; 1469 Matrix matrix; 1470 NodePtrW source; 1471 const MessageType event; 1472 int x, y; 1473 bool handled; 1474 bool bubble_up; 1475 1476 bool deliverAndForward (Node *n, Surface *s, bool inside, bool deliver); 1477 void surfaceEvent (Node *mt, Surface *s); 1478 public: 1479 MouseVisitor (ViewArea *v, MessageType evt, Matrix m, int x, int y); 1480 KDE_NO_CDTOR_EXPORT ~MouseVisitor () {} 1481 using Visitor::visit; 1482 void visit (Node * n); 1483 void visit (Element *); 1484 void visit (SMIL::Smil *); 1485 void visit (SMIL::Layout *); 1486 void visit (SMIL::RegionBase *); 1487 void visit (SMIL::MediaType * n); 1488 void visit (SMIL::SmilText * n); 1489 void visit (SMIL::Anchor *); 1490 void visit (SMIL::Area *); 1491 QCursor cursor; 1492 }; 1493 1494 } // namespace 1495 1496 KDE_NO_CDTOR_EXPORT 1497 MouseVisitor::MouseVisitor (ViewArea *v, MessageType evt, Matrix m, int a, int b) 1498 : view_area (v), matrix (m), event (evt), x (a), y (b), 1499 handled (false), bubble_up (false) { 1500 } 1501 1502 KDE_NO_EXPORT void MouseVisitor::visit (Node * n) { 1503 kDebug () << "Mouse event ignored for " << n->nodeName (); 1504 } 1505 1506 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Smil *s) { 1507 if (s->active () && s->layout_node) 1508 s->layout_node->accept (this); 1509 } 1510 1511 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Layout * layout) { 1512 if (layout->root_layout) 1513 layout->root_layout->accept (this); 1514 } 1515 1516 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::RegionBase *region) { 1517 Surface *s = (Surface *) region->role (RoleDisplay); 1518 if (s) { 1519 SRect rect = s->bounds; 1520 IRect scr = matrix.toScreen (rect); 1521 int rx = scr.x(), ry = scr.y(), rw = scr.width(), rh = scr.height(); 1522 handled = false; 1523 bool inside = x > rx && x < rx+rw && y > ry && y< ry+rh; 1524 if (!inside && (event == MsgEventClicked || !s->has_mouse)) 1525 return; 1526 1527 if (event == MsgEventClicked && !s->virtual_size.isEmpty () && 1528 x > rx + rw - REGION_SCROLLBAR_WIDTH) { 1529 const int sbh = rh - REGION_SCROLLBAR_WIDTH; 1530 const int vy = s->virtual_size.height; 1531 const int knob_h = sbh * rh / vy; 1532 int knob_y = y - ry - 0.5 * knob_h; 1533 if (knob_y < 0) 1534 knob_y = 0; 1535 else if (knob_y + knob_h > sbh) 1536 knob_y = sbh - knob_h; 1537 s->y_scroll = vy * knob_y / sbh; 1538 view_area->scheduleRepaint (scr); 1539 return; 1540 } 1541 if (event == MsgEventClicked && !s->virtual_size.isEmpty () && 1542 y > ry + rh - REGION_SCROLLBAR_WIDTH) { 1543 const int sbw = rw - REGION_SCROLLBAR_WIDTH; 1544 const int vw = s->virtual_size.width; 1545 const int knob_w = sbw * rw / vw; 1546 int knob_x = x - rx - 0.5 * knob_w; 1547 if (knob_x < 0) 1548 knob_x = 0; 1549 else if (knob_x + knob_w > sbw) 1550 knob_x = sbw - knob_w; 1551 s->x_scroll = vw * knob_x / sbw; 1552 view_area->scheduleRepaint (scr); 1553 return; 1554 } 1555 1556 NodePtrW src = source; 1557 source = region; 1558 Matrix m = matrix; 1559 matrix = Matrix (rect.x(), rect.y(), 1.0, 1.0); 1560 matrix.transform (m); 1561 if (!s->virtual_size.isEmpty ()) 1562 matrix.translate (-s->x_scroll, -s->y_scroll); 1563 bubble_up = false; 1564 1565 bool child_handled = false; 1566 if (inside || s->has_mouse) 1567 for (SurfacePtr c = s->firstChild (); c; c = c->nextSibling ()) { 1568 if (c->node && c->node->id == SMIL::id_node_region) { 1569 c->node->accept (this); 1570 child_handled |= handled; 1571 if (!source || !source->active ()) 1572 break; 1573 } else { 1574 break; 1575 } 1576 } 1577 child_handled &= !bubble_up; 1578 bubble_up = false; 1579 if (source && source->active ()) 1580 deliverAndForward (region, s, inside, !child_handled); 1581 1582 handled = inside; 1583 matrix = m; 1584 source = src; 1585 } 1586 } 1587 1588 static void followLink (SMIL::LinkingBase * link) { 1589 kDebug() << "link to " << link->href << " clicked"; 1590 if (link->href.startsWith ("#")) { 1591 SMIL::Smil * s = SMIL::Smil::findSmilNode (link); 1592 if (s) 1593 s->jump (link->href.mid (1)); 1594 else 1595 kError() << "In document jumps smil not found" << endl; 1596 } else { 1597 PlayListNotify *notify = link->document ()->notify_listener; 1598 if (notify && !link->target.isEmpty ()) { 1599 notify->openUrl(KUrl(link->href), link->target, QString()); 1600 } else { 1601 NodePtr n = link; 1602 for (NodePtr p = link->parentNode (); p; p = p->parentNode ()) { 1603 if (n->mrl () && n->mrl ()->opener == p) { 1604 p->setState (Node::state_deferred); 1605 p->mrl ()->setParam (Ids::attr_src, link->href, 0L); 1606 p->activate (); 1607 break; 1608 } 1609 n = p; 1610 } 1611 } 1612 } 1613 } 1614 1615 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Anchor * anchor) { 1616 if (event == MsgEventPointerMoved) 1617 cursor.setShape (Qt::PointingHandCursor); 1618 else if (event == MsgEventClicked) 1619 followLink (anchor); 1620 } 1621 1622 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::Area * area) { 1623 NodePtr n = area->parentNode (); 1624 Surface *s = (Surface *) n->role (RoleDisplay); 1625 if (s) { 1626 SRect rect = s->bounds; 1627 IRect scr = matrix.toScreen (rect); 1628 int w = scr.width (), h = scr.height (); 1629 if (area->nr_coords > 1) { 1630 Single left = area->coords[0].size (rect.width ()); 1631 Single top = area->coords[1].size (rect.height ()); 1632 matrix.getXY (left, top); 1633 if (x < left || x > left + w || y < top || y > top + h) 1634 return; 1635 if (area->nr_coords > 3) { 1636 Single right = area->coords[2].size (rect.width ()); 1637 Single bottom = area->coords[3].size (rect.height ()); 1638 matrix.getXY (right, bottom); 1639 if (x > right || y > bottom) 1640 return; 1641 } 1642 } 1643 if (event == MsgEventPointerMoved) 1644 cursor.setShape (Qt::PointingHandCursor); 1645 else { 1646 ConnectionList *nl = nodeMessageReceivers (area, event); 1647 if (nl) 1648 for (Connection *c = nl->first(); c; c = nl->next ()) { 1649 if (c->connecter) 1650 c->connecter->accept (this); 1651 if (!source || !source->active ()) 1652 return; 1653 } 1654 if (event == MsgEventClicked && !area->href.isEmpty ()) 1655 followLink (area); 1656 } 1657 } 1658 } 1659 1660 KDE_NO_EXPORT void MouseVisitor::visit (Element *elm) { 1661 Runtime *rt = (Runtime *) elm->role (RoleTiming); 1662 if (rt) { 1663 Posting mouse_event (source, event); 1664 rt->message (event, &mouse_event); 1665 } 1666 } 1667 1668 bool MouseVisitor::deliverAndForward (Node *node, Surface *s, bool inside, bool deliver) { 1669 bool forward = deliver; 1670 MessageType user_event = event; 1671 if (event == MsgEventPointerMoved) { 1672 forward = true; // always pass move events 1673 if (inside && !s->has_mouse) { 1674 deliver = true; 1675 user_event = MsgEventPointerInBounds; 1676 } else if (!inside && s->has_mouse) { 1677 deliver = true; 1678 user_event = MsgEventPointerOutBounds; 1679 } else if (!inside) { 1680 return false; 1681 } else { 1682 deliver = false; 1683 } 1684 } 1685 s->has_mouse = inside; 1686 1687 if (event != MsgEventPointerMoved && !inside) 1688 return false; 1689 1690 NodePtrW node_save = node; 1691 1692 if (forward) { 1693 ConnectionList *nl = nodeMessageReceivers (node, MsgSurfaceAttach); 1694 if (nl) { 1695 NodePtr node_save = source; 1696 source = node; 1697 1698 for (Connection *c = nl->first(); c; c = nl->next ()) { 1699 if (c->connecter) 1700 c->connecter->accept (this); 1701 if (!source || !source->active ()) 1702 break; 1703 } 1704 source = node_save; 1705 } 1706 } 1707 if (!node_save || !node->active ()) 1708 return false; 1709 if (deliver) { 1710 Posting mouse_event (node, user_event); 1711 node->deliver (user_event, &mouse_event); 1712 } 1713 if (!node_save || !node->active ()) 1714 return false; 1715 return true; 1716 } 1717 1718 void MouseVisitor::surfaceEvent (Node *node, Surface *s) { 1719 if (!s) 1720 return; 1721 if (s->node && s->node.ptr () != node) { 1722 s->node->accept (this); 1723 return; 1724 } 1725 SRect rect = s->bounds; 1726 IRect scr = matrix.toScreen (rect); 1727 int rx = scr.x(), ry = scr.y(), rw = scr.width(), rh = scr.height(); 1728 const bool inside = x > rx && x < rx+rw && y > ry && y< ry+rh; 1729 const bool had_mouse = s->has_mouse; 1730 if (deliverAndForward (node, s, inside, true) && 1731 (inside || had_mouse) && 1732 s->firstChild () && s->firstChild ()->node) { 1733 Matrix m = matrix; 1734 matrix = Matrix (rect.x(), rect.y(), s->xscale, s->yscale); 1735 matrix.transform (m); 1736 s->firstChild ()->node->accept (this); 1737 matrix = m; 1738 } 1739 } 1740 1741 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::MediaType *mt) { 1742 if (mt->sensitivity == SMIL::MediaType::sens_transparent) 1743 bubble_up = true; 1744 else 1745 surfaceEvent (mt, mt->surface ()); 1746 } 1747 1748 KDE_NO_EXPORT void MouseVisitor::visit (SMIL::SmilText *st) { 1749 surfaceEvent (st, st->surface ()); 1750 } 1751 1752 //----------------------------------------------------------------------------- 1753 1754 namespace KMPlayer { 1755 class KMPLAYER_NO_EXPORT ViewerAreaPrivate { 1756 public: 1757 ViewerAreaPrivate (ViewArea *v) 1758 : m_view_area (v), backing_store (0), gc(0), 1759 screen(NULL), visual(NULL), width(0), height(0) 1760 {} 1761 ~ViewerAreaPrivate() { 1762 destroyBackingStore (); 1763 if (gc) { 1764 xcb_connection_t* connection = QX11Info::connection(); 1765 xcb_free_gc(connection, gc); 1766 } 1767 } 1768 void clearSurface (Surface *s) { 1769 #ifdef KMPLAYER_WITH_CAIRO 1770 if (s->surface) { 1771 cairo_surface_destroy (s->surface); 1772 s->surface = 0L; 1773 } 1774 destroyBackingStore (); 1775 #endif 1776 } 1777 void resizeSurface (Surface *s) { 1778 #ifdef KMPLAYER_WITH_CAIRO 1779 #if QT_VERSION >= 0x050600 1780 int w = (int)(m_view_area->width() * m_view_area->devicePixelRatioF()); 1781 int h = (int)(m_view_area->height() * m_view_area->devicePixelRatioF()); 1782 #else 1783 int w = m_view_area->width (); 1784 int h = m_view_area->height (); 1785 #endif 1786 if ((w != width || h != height) && s->surface) { 1787 clearSurface (s); 1788 width = w; 1789 height = h; 1790 } 1791 #endif 1792 } 1793 #ifdef KMPLAYER_WITH_CAIRO 1794 cairo_surface_t *createSurface (int w, int h) { 1795 xcb_connection_t* connection = QX11Info::connection(); 1796 destroyBackingStore (); 1797 xcb_screen_t* scr = screen_of_display(connection, QX11Info::appScreen()); 1798 backing_store = xcb_generate_id(connection); 1799 xcb_void_cookie_t cookie = xcb_create_pixmap_checked(connection, scr->root_depth, backing_store, m_view_area->winId(), w, h); 1800 xcb_generic_error_t* error = xcb_request_check(connection, cookie); 1801 if (error) { 1802 qDebug("failed to create pixmap"); 1803 return NULL; 1804 } 1805 return cairo_xcb_surface_create(connection, backing_store, visual_of_screen(connection, scr), w, h); 1806 } 1807 void swapBuffer (const IRect &sr, int dx, int dy) { 1808 xcb_connection_t* connection = QX11Info::connection(); 1809 if (!gc) { 1810 gc = xcb_generate_id(connection); 1811 uint32_t values[] = { XCB_GX_COPY, XCB_FILL_STYLE_SOLID, 1812 XCB_SUBWINDOW_MODE_CLIP_BY_CHILDREN, 0 }; 1813 xcb_create_gc(connection, gc, backing_store, 1814 XCB_GC_FUNCTION | XCB_GC_FILL_STYLE | 1815 XCB_GC_SUBWINDOW_MODE | XCB_GC_GRAPHICS_EXPOSURES, values); 1816 } 1817 xcb_copy_area(connection, backing_store, m_view_area->winId(), 1818 gc, sr.x(), sr.y(), dx, dy, sr.width (), sr.height ()); 1819 xcb_flush(connection); 1820 } 1821 #endif 1822 void destroyBackingStore () { 1823 #ifdef KMPLAYER_WITH_CAIRO 1824 if (backing_store) { 1825 xcb_connection_t* connection = QX11Info::connection(); 1826 xcb_free_pixmap(connection, backing_store); 1827 } 1828 #endif 1829 backing_store = 0; 1830 } 1831 xcb_screen_t *screen_of_display(xcb_connection_t* c, int num) 1832 { 1833 if (!screen) { 1834 xcb_screen_iterator_t iter; 1835 1836 iter = xcb_setup_roots_iterator (xcb_get_setup (c)); 1837 for (; iter.rem; --num, xcb_screen_next (&iter)) 1838 if (num == 0) { 1839 screen = iter.data; 1840 break; 1841 } 1842 } 1843 return screen; 1844 } 1845 1846 xcb_visualtype_t* visual_of_screen(xcb_connection_t* c, xcb_screen_t* screen) 1847 { 1848 if (!visual) { 1849 xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator (screen); 1850 for (; depth_iter.rem; xcb_depth_next (&depth_iter)) { 1851 xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator (depth_iter.data); 1852 for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) { 1853 if (screen->root_visual == visual_iter.data->visual_id) { 1854 visual = visual_iter.data; 1855 break; 1856 } 1857 } 1858 } 1859 } 1860 return visual; 1861 } 1862 ViewArea *m_view_area; 1863 xcb_drawable_t backing_store; 1864 xcb_gcontext_t gc; 1865 xcb_screen_t* screen; 1866 xcb_visualtype_t* visual; 1867 int width; 1868 int height; 1869 }; 1870 1871 class KMPLAYER_NO_EXPORT RepaintUpdater { 1872 public: 1873 RepaintUpdater (Node *n, RepaintUpdater *nx) : node (n), next (nx) {} 1874 1875 NodePtrW node; 1876 RepaintUpdater *next; 1877 }; 1878 1879 } 1880 1881 KDE_NO_CDTOR_EXPORT ViewArea::ViewArea (QWidget *, View * view, bool paint_bg) 1882 // : QWidget (parent, "kde_kmplayer_viewarea", WResizeNoErase | WRepaintNoErase), 1883 : //QWidget (parent), 1884 d (new ViewerAreaPrivate (this)), 1885 m_view (view), 1886 m_collection (new KActionCollection (this)), 1887 surface (new Surface (this)), 1888 m_mouse_invisible_timer (0), 1889 m_repaint_timer (0), 1890 m_restore_fullscreen_timer (0), 1891 m_fullscreen (false), 1892 m_minimal (false), 1893 m_updaters_enabled (true), 1894 m_paint_background (paint_bg) { 1895 if (!paint_bg) 1896 setAttribute (Qt::WA_NoSystemBackground, true); 1897 QPalette palette; 1898 palette.setColor (backgroundRole(), QColor (0, 0, 0)); 1899 setPalette (palette); 1900 setAcceptDrops (true); 1901 //new KAction (i18n ("Fullscreen"), KShortcut (Qt::Key_F), this, SLOT (accelActivated ()), m_collection, "view_fullscreen_toggle"); 1902 setMouseTracking (true); 1903 setFocusPolicy (Qt::ClickFocus); 1904 QCoreApplication::instance()->installNativeEventFilter(this); 1905 } 1906 1907 KDE_NO_CDTOR_EXPORT ViewArea::~ViewArea () { 1908 delete d; 1909 } 1910 1911 KDE_NO_EXPORT void ViewArea::stopTimers () { 1912 if (m_mouse_invisible_timer) { 1913 killTimer (m_mouse_invisible_timer); 1914 m_mouse_invisible_timer = 0; 1915 } 1916 if (m_repaint_timer) { 1917 killTimer (m_repaint_timer); 1918 m_repaint_timer = 0; 1919 } 1920 } 1921 1922 KDE_NO_EXPORT void ViewArea::fullScreen () { 1923 stopTimers (); 1924 if (m_fullscreen) { 1925 setVisible(false); 1926 setWindowState(windowState() & ~Qt::WindowFullScreen); // reset 1927 if (!m_restore_fullscreen_timer) 1928 m_restore_fullscreen_timer = startTimer(25); 1929 for (int i = 0; i < m_collection->count (); ++i) 1930 m_collection->action (i)->setEnabled (false); 1931 m_view->controlPanel()->enableFullscreenButton(false); 1932 unsetCursor(); 1933 } else { 1934 m_topwindow_rect = topLevelWidget ()->geometry (); 1935 #if QT_VERSION >= 0x050200 1936 m_view->dockArea()->takeCentralWidget(); 1937 #else 1938 setParent (0L); 1939 #endif 1940 move(qApp->desktop()->screenGeometry(this).topLeft()); 1941 setVisible(true); 1942 setWindowState( windowState() ^ Qt::WindowFullScreen ); // set 1943 for (int i = 0; i < m_collection->count (); ++i) 1944 m_collection->action (i)->setEnabled (true); 1945 m_view->controlPanel()->enableFullscreenButton(true); 1946 m_mouse_invisible_timer = startTimer(MOUSE_INVISIBLE_DELAY); 1947 } 1948 m_fullscreen = !m_fullscreen; 1949 m_view->controlPanel()->fullscreenAction->setChecked (m_fullscreen); 1950 1951 d->clearSurface (surface.ptr ()); 1952 emit fullScreenChanged (); 1953 } 1954 1955 void ViewArea::minimalMode () { 1956 m_minimal = !m_minimal; 1957 stopTimers (); 1958 m_mouse_invisible_timer = m_repaint_timer = 0; 1959 if (m_minimal) { 1960 m_view->setViewOnly (); 1961 m_view->setControlPanelMode (KMPlayer::View::CP_AutoHide); 1962 m_view->setNoInfoMessages (true); 1963 m_view->controlPanel()->enableFullscreenButton(true); 1964 } else { 1965 m_view->setControlPanelMode (KMPlayer::View::CP_Show); 1966 m_view->setNoInfoMessages (false); 1967 m_view->controlPanel()->enableFullscreenButton(false); 1968 } 1969 m_topwindow_rect = topLevelWidget ()->geometry (); 1970 } 1971 1972 KDE_NO_EXPORT void ViewArea::accelActivated () { 1973 m_view->controlPanel()->fullscreenAction->trigger (); 1974 } 1975 1976 KDE_NO_EXPORT void ViewArea::keyPressEvent (QKeyEvent *e) { 1977 if (surface->node) { 1978 QString txt = e->text (); 1979 if (!txt.isEmpty ()) 1980 surface->node->document ()->message (MsgAccessKey, 1981 (void *)(long) txt[0].unicode ()); 1982 } 1983 } 1984 1985 KDE_NO_EXPORT void ViewArea::mousePressEvent (QMouseEvent * e) { 1986 #if QT_VERSION >= 0x050600 1987 int devicex = (int)(e->x() * devicePixelRatioF()); 1988 int devicey = (int)(e->y() * devicePixelRatioF()); 1989 #else 1990 int devicex = e->x(); 1991 int devicey = e->y(); 1992 #endif 1993 if (surface->node) { 1994 MouseVisitor visitor (this, MsgEventClicked, 1995 Matrix (surface->bounds.x (), surface->bounds.y (), 1996 surface->xscale, surface->yscale), 1997 devicex, devicey); 1998 surface->node->accept (&visitor); 1999 } 2000 } 2001 2002 KDE_NO_EXPORT void ViewArea::mouseDoubleClickEvent (QMouseEvent *) { 2003 m_view->fullScreen (); // screensaver stuff 2004 } 2005 2006 KDE_NO_EXPORT void ViewArea::mouseMoveEvent (QMouseEvent * e) { 2007 if (e->buttons () == Qt::NoButton) 2008 m_view->mouseMoved (e->x (), e->y ()); 2009 if (surface->node) { 2010 #if QT_VERSION >= 0x050600 2011 int devicex = (int)(e->x() * devicePixelRatioF()); 2012 int devicey = (int)(e->y() * devicePixelRatioF()); 2013 #else 2014 int devicex = e->x(); 2015 int devicey = e->y(); 2016 #endif 2017 MouseVisitor visitor (this, MsgEventPointerMoved, 2018 Matrix (surface->bounds.x (), surface->bounds.y (), 2019 surface->xscale, surface->yscale), 2020 devicex, devicey); 2021 surface->node->accept (&visitor); 2022 setCursor (visitor.cursor); 2023 } 2024 e->accept (); 2025 mouseMoved (); // for m_mouse_invisible_timer 2026 } 2027 2028 KDE_NO_EXPORT void ViewArea::syncVisual () { 2029 #if QT_VERSION >= 0x050600 2030 pixel_device_ratio = devicePixelRatioF(); 2031 int w = (int)(width() * devicePixelRatioF()); 2032 int h = (int)(height() * devicePixelRatioF()); 2033 #else 2034 int w = width(); 2035 int h = height(); 2036 #endif 2037 IRect rect = m_repaint_rect.intersect (IRect (0, 0, w, h)); 2038 #ifdef KMPLAYER_WITH_CAIRO 2039 if (surface->node) { 2040 int ex = rect.x (); 2041 if (ex > 0) 2042 ex--; 2043 int ey = rect.y (); 2044 if (ey > 0) 2045 ey--; 2046 int ew = rect.width () + 2; 2047 int eh = rect.height () + 2; 2048 IRect swap_rect; 2049 cairo_surface_t *merge = NULL; 2050 cairo_pattern_t *pat = NULL; 2051 cairo_t *cr = NULL; 2052 if (!surface->surface) { 2053 surface->surface = d->createSurface(w, h); 2054 swap_rect = IRect (ex, ey, ew, eh); 2055 CairoPaintVisitor visitor (surface->surface, 2056 Matrix (surface->bounds.x(), surface->bounds.y(), 2057 surface->xscale, surface->yscale), 2058 swap_rect, 2059 palette ().color (backgroundRole ()), true); 2060 surface->node->accept (&visitor); 2061 m_update_rect = IRect (); 2062 } else if (!rect.isEmpty ()) { 2063 merge = cairo_surface_create_similar (surface->surface, 2064 CAIRO_CONTENT_COLOR, ew, eh); 2065 { 2066 CairoPaintVisitor visitor (merge, 2067 Matrix (surface->bounds.x()-ex, surface->bounds.y()-ey, 2068 surface->xscale, surface->yscale), 2069 IRect (0, 0, ew, eh), 2070 palette ().color (backgroundRole ()), true); 2071 surface->node->accept (&visitor); 2072 } 2073 cr = cairo_create (surface->surface); 2074 pat = cairo_pattern_create_for_surface (merge); 2075 cairo_pattern_set_extend (pat, CAIRO_EXTEND_NONE); 2076 cairo_matrix_t mat; 2077 cairo_matrix_init_translate (&mat, (int) -ex, (int) -ey); 2078 cairo_pattern_set_matrix (pat, &mat); 2079 cairo_set_source (cr, pat); 2080 //cairo_set_operator (cr, CAIRO_OPERATOR_ADD); 2081 cairo_rectangle (cr, ex, ey, ew, eh); 2082 //cairo_fill (cr); 2083 cairo_clip (cr); 2084 cairo_paint_with_alpha (cr, .8); 2085 swap_rect = IRect (ex, ey, ew, eh).unite (m_update_rect); 2086 m_update_rect = IRect (ex, ey, ew, eh); 2087 } else { 2088 swap_rect = m_update_rect; 2089 m_update_rect = IRect (); 2090 } 2091 d->swapBuffer (swap_rect, swap_rect.x (), swap_rect.y ()); 2092 if (merge) { 2093 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 2094 cairo_rectangle (cr, ex, ey, ew, eh); 2095 cairo_fill (cr); 2096 cairo_destroy (cr); 2097 cairo_pattern_destroy (pat); 2098 cairo_surface_destroy (merge); 2099 } 2100 cairo_surface_flush (surface->surface); 2101 } else 2102 #endif 2103 { 2104 m_update_rect = IRect (); 2105 #if QT_VERSION >= 0x050600 2106 repaint(QRect(rect.x() / devicePixelRatioF(), 2107 rect.y() / devicePixelRatioF(), 2108 rect.width() / devicePixelRatioF(), 2109 rect.height() / devicePixelRatioF())); 2110 #else 2111 repaint (QRect(rect.x(), rect.y(), rect.width(), rect.height())); 2112 #endif 2113 } 2114 } 2115 2116 KDE_NO_EXPORT void ViewArea::paintEvent (QPaintEvent * pe) { 2117 #ifdef KMPLAYER_WITH_CAIRO 2118 if (surface->node) { 2119 #if QT_VERSION >= 0x050600 2120 int x = (int)(pe->rect().x() * devicePixelRatioF()); 2121 int y = (int)(pe->rect().y() * devicePixelRatioF()); 2122 int w = (int)(pe->rect().width() * devicePixelRatioF()); 2123 int h = (int)(pe->rect().height() * devicePixelRatioF()); 2124 #else 2125 int x = pe->rect().x(); 2126 int y = pe->rect().y(); 2127 int w = pe->rect().width(); 2128 int h = pe->rect().height(); 2129 #endif 2130 scheduleRepaint(IRect(x, y, w, h)); 2131 } else 2132 #endif 2133 if (m_fullscreen || m_paint_background) 2134 { 2135 QPainter p (this); 2136 p.fillRect (pe->rect (), QBrush (palette ().color (backgroundRole ()))); 2137 p.end (); 2138 } 2139 } 2140 2141 QPaintEngine *ViewArea::paintEngine () const { 2142 #ifdef KMPLAYER_WITH_CAIRO 2143 if (surface->node) 2144 return NULL; 2145 else 2146 #endif 2147 return QWidget::paintEngine (); 2148 } 2149 2150 KDE_NO_EXPORT void ViewArea::scale (int) { 2151 resizeEvent (0L); 2152 } 2153 2154 KDE_NO_EXPORT void ViewArea::updateSurfaceBounds () { 2155 #if QT_VERSION >= 0x050600 2156 int devicew = (int)(width() * devicePixelRatioF()); 2157 int deviceh = (int)(height() * devicePixelRatioF()); 2158 #else 2159 int devicew = width(), deviceh = height(); 2160 #endif 2161 Single x, y, w = devicew, h = deviceh; 2162 h -= m_view->statusBarHeight (); 2163 h -= m_view->controlPanel ()->isVisible () && !m_fullscreen 2164 ? (m_view->controlPanelMode () == View::CP_Only 2165 ? h 2166 : (Single) m_view->controlPanel()->maximumSize ().height ()) 2167 : Single (0); 2168 2169 int scale = m_view->controlPanel ()->scale_slider->sliderPosition (); 2170 if (scale != 100) { 2171 int nw = w * 1.0 * scale / 100; 2172 int nh = h * 1.0 * scale / 100; 2173 x += (w - nw) / 2; 2174 y += (h - nh) / 2; 2175 w = nw; 2176 h = nh; 2177 } 2178 if (surface->node) { 2179 d->resizeSurface (surface.ptr ()); 2180 surface->resize (SRect (x, y, w, h)); 2181 surface->node->message (MsgSurfaceBoundsUpdate, (void *) true); 2182 } 2183 scheduleRepaint (IRect (0, 0, devicew, deviceh)); 2184 } 2185 2186 KDE_NO_EXPORT void ViewArea::resizeEvent (QResizeEvent *) { 2187 if (!m_view->controlPanel ()) return; 2188 Single x, y, w = width (), h = height (); 2189 Single hsb = m_view->statusBarHeight (); 2190 int hcp = m_view->controlPanel ()->isVisible () 2191 ? (m_view->controlPanelMode () == View::CP_Only 2192 ? h-hsb 2193 : (Single) m_view->controlPanel()->maximumSize ().height ()) 2194 : Single (0); 2195 // move controlpanel over video when autohiding and playing 2196 bool auto_hide = m_view->controlPanelMode () == View::CP_AutoHide; 2197 h -= Single (auto_hide ? 0 : hcp) - hsb; 2198 // now scale the regions and check if video region is already sized 2199 updateSurfaceBounds (); 2200 2201 // finally resize controlpanel and video widget 2202 if (m_view->controlPanel ()->isVisible ()) 2203 m_view->controlPanel ()->setGeometry (0, h-(auto_hide ? hcp:0), w, hcp); 2204 if (m_view->statusBar ()->isVisible ()) 2205 m_view->statusBar ()->setGeometry (0, h-hsb, w, hsb); 2206 int scale = m_view->controlPanel ()->scale_slider->sliderPosition (); 2207 Single ws = w * scale / 100; 2208 Single hs = h * scale / 100; 2209 x += (w - ws) / 2; 2210 y += (h - hs) / 2; 2211 m_view->console ()->setGeometry (0, 0, w, h); 2212 m_view->picture ()->setGeometry (0, 0, w, h); 2213 if (!surface->node && video_widgets.size () == 1) { 2214 #if QT_VERSION >= 0x050600 2215 x *= devicePixelRatioF(); 2216 y *= devicePixelRatioF(); 2217 ws *= devicePixelRatioF(); 2218 hs *= devicePixelRatioF(); 2219 #endif 2220 video_widgets.first ()->setGeometry (IRect (x, y, ws, hs)); 2221 } 2222 } 2223 2224 KDE_NO_EXPORT Surface *ViewArea::getSurface (Mrl *mrl) { 2225 surface->clear (); 2226 surface->node = mrl; 2227 kDebug() << mrl; 2228 //m_view->viewer()->resetBackgroundColor (); 2229 if (mrl) { 2230 updateSurfaceBounds (); 2231 #ifdef KMPLAYER_WITH_CAIRO 2232 setAttribute (Qt::WA_OpaquePaintEvent, true); 2233 setAttribute (Qt::WA_PaintOnScreen, true); 2234 #endif 2235 return surface.ptr (); 2236 } else { 2237 #ifdef KMPLAYER_WITH_CAIRO 2238 setAttribute (Qt::WA_OpaquePaintEvent, false); 2239 setAttribute (Qt::WA_PaintOnScreen, false); 2240 d->clearSurface (surface.ptr ()); 2241 #endif 2242 } 2243 #if QT_VERSION >= 0x050600 2244 int devicew = (int)(width() * devicePixelRatioF()); 2245 int deviceh = (int)(height() * devicePixelRatioF()); 2246 scheduleRepaint (IRect (0, 0, devicew, deviceh)); 2247 #else 2248 scheduleRepaint (IRect (0, 0, width (), height ())); 2249 #endif 2250 return 0L; 2251 } 2252 2253 KDE_NO_EXPORT void ViewArea::showEvent (QShowEvent *) { 2254 resizeEvent (0L); 2255 } 2256 2257 KDE_NO_EXPORT void ViewArea::dropEvent (QDropEvent * de) { 2258 m_view->dropEvent (de); 2259 } 2260 2261 KDE_NO_EXPORT void ViewArea::dragEnterEvent (QDragEnterEvent* dee) { 2262 m_view->dragEnterEvent (dee); 2263 } 2264 2265 KDE_NO_EXPORT void ViewArea::contextMenuEvent (QContextMenuEvent * e) { 2266 m_view->controlPanel ()->popupMenu->exec (e->globalPos ()); 2267 } 2268 2269 KDE_NO_EXPORT void ViewArea::mouseMoved () { 2270 if (m_fullscreen) { 2271 if (m_mouse_invisible_timer) 2272 killTimer (m_mouse_invisible_timer); 2273 unsetCursor (); 2274 m_mouse_invisible_timer = startTimer (MOUSE_INVISIBLE_DELAY); 2275 } 2276 } 2277 2278 KDE_NO_EXPORT void ViewArea::scheduleRepaint (const IRect &rect) { 2279 if (m_repaint_timer) { 2280 m_repaint_rect = m_repaint_rect.unite (rect); 2281 } else { 2282 m_repaint_rect = rect; 2283 m_repaint_timer = startTimer (25); 2284 } 2285 } 2286 2287 KDE_NO_EXPORT ConnectionList *ViewArea::updaters () { 2288 if (!m_repaint_timer) 2289 m_repaint_timer = startTimer (25); 2290 return &m_updaters; 2291 } 2292 2293 KDE_NO_EXPORT 2294 void ViewArea::enableUpdaters (bool enable, unsigned int skip) { 2295 m_updaters_enabled = enable; 2296 Connection *connect = m_updaters.first (); 2297 if (enable && connect) { 2298 UpdateEvent event (connect->connecter->document (), skip); 2299 for (; connect; connect = m_updaters.next ()) 2300 if (connect->connecter) 2301 connect->connecter->message (MsgSurfaceUpdate, &event); 2302 if (!m_repaint_timer) 2303 m_repaint_timer = startTimer (25); 2304 } else if (!enable && m_repaint_timer && 2305 m_repaint_rect.isEmpty () && m_update_rect.isEmpty ()) { 2306 killTimer (m_repaint_timer); 2307 m_repaint_timer = 0; 2308 } 2309 } 2310 2311 KDE_NO_EXPORT void ViewArea::timerEvent (QTimerEvent * e) { 2312 if (e->timerId () == m_mouse_invisible_timer) { 2313 killTimer (m_mouse_invisible_timer); 2314 m_mouse_invisible_timer = 0; 2315 if (m_fullscreen) 2316 setCursor (QCursor (Qt::BlankCursor)); 2317 } else if (e->timerId () == m_repaint_timer) { 2318 Connection *connect = m_updaters.first (); 2319 int count = 0; 2320 if (m_updaters_enabled && connect) { 2321 UpdateEvent event (connect->connecter->document (), 0); 2322 for (; connect; count++, connect = m_updaters.next ()) 2323 if (connect->connecter) 2324 connect->connecter->message (MsgSurfaceUpdate, &event); 2325 } 2326 //repaint (m_repaint_rect, false); 2327 if (!m_repaint_rect.isEmpty () || !m_update_rect.isEmpty ()) { 2328 syncVisual (); 2329 m_repaint_rect = IRect (); 2330 } 2331 if (m_update_rect.isEmpty () && 2332 (!m_updaters_enabled || !m_updaters.first ())) { 2333 killTimer (m_repaint_timer); 2334 m_repaint_timer = 0; 2335 } 2336 } else if (e->timerId () == m_restore_fullscreen_timer) { 2337 xcb_connection_t* connection = QX11Info::connection(); 2338 xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(connection, winId()); 2339 xcb_get_window_attributes_reply_t* attrs = xcb_get_window_attributes_reply(connection, cookie, NULL); 2340 if (attrs->map_state == XCB_MAP_STATE_UNMAPPED) { 2341 m_view->dockArea ()->setCentralWidget (this); 2342 killTimer(m_restore_fullscreen_timer); 2343 m_restore_fullscreen_timer = 0; 2344 } 2345 free(attrs); 2346 } else { 2347 kError () << "unknown timer " << e->timerId () << " " << m_repaint_timer << endl; 2348 killTimer (e->timerId ()); 2349 } 2350 } 2351 2352 KDE_NO_EXPORT void ViewArea::closeEvent (QCloseEvent * e) { 2353 //kDebug () << "closeEvent"; 2354 if (m_fullscreen) { 2355 m_view->fullScreen(); 2356 if (!m_view->topLevelWidget ()->isVisible ()) 2357 m_view->topLevelWidget ()->setVisible (true); 2358 e->ignore (); 2359 } else 2360 QWidget::closeEvent (e); 2361 } 2362 2363 IViewer *ViewArea::createVideoWidget () { 2364 VideoOutput *viewer = new VideoOutput (this, m_view); 2365 video_widgets.push_back (viewer); 2366 viewer->setGeometry (IRect (-60, -60, 50, 50)); 2367 viewer->setVisible (true); 2368 m_view->controlPanel ()->raise (); 2369 return viewer; 2370 } 2371 2372 void ViewArea::destroyVideoWidget (IViewer *widget) { 2373 int i = video_widgets.indexOf(widget); 2374 if (i >= 0) { 2375 IViewer *viewer = widget; 2376 delete viewer; 2377 video_widgets.removeAt(i); 2378 } else { 2379 kWarning () << "destroyVideoWidget widget not found" << endl; 2380 } 2381 } 2382 2383 void ViewArea::setVideoWidgetVisible (bool show) { 2384 const VideoWidgetList::iterator e = video_widgets.end (); 2385 for (VideoWidgetList::iterator it = video_widgets.begin (); it != e; ++it) 2386 static_cast <VideoOutput *> (*it)->setVisible (show); 2387 } 2388 2389 static void setXSelectInput(WId wid, uint32_t mask) { 2390 xcb_connection_t* connection = QX11Info::connection(); 2391 const uint32_t values[] = { mask }; 2392 xcb_change_window_attributes(connection, wid, XCB_CW_EVENT_MASK, values); 2393 xcb_query_tree_cookie_t biscuit = xcb_query_tree(connection, wid); 2394 xcb_query_tree_reply_t *reply = xcb_query_tree_reply(connection, biscuit, NULL); 2395 if (reply) { 2396 xcb_window_t *chlds = xcb_query_tree_children(reply); 2397 for (int i = 0; i < xcb_query_tree_children_length(reply); i++) 2398 setXSelectInput(chlds[i], mask); 2399 free(reply); 2400 } else { 2401 qDebug("failed to get x children"); 2402 } 2403 } 2404 2405 bool ViewArea::nativeEventFilter(const QByteArray& eventType, void * message, long *result) { 2406 if (eventType != "xcb_generic_event_t") 2407 return false; 2408 2409 xcb_generic_event_t* event = (xcb_generic_event_t*)message; 2410 switch (event->response_type & ~0x80) { 2411 case XCB_UNMAP_NOTIFY: { 2412 xcb_unmap_notify_event_t* ev = (xcb_unmap_notify_event_t*)event; 2413 if (ev->event != ev->window) { 2414 const VideoWidgetList::iterator e = video_widgets.end (); 2415 for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i) { 2416 if (ev->event == (*i)->ownHandle()) { 2417 (*i)->embedded(0); 2418 break; 2419 } 2420 } 2421 } 2422 break; 2423 } 2424 case XCB_MAP_NOTIFY: { 2425 xcb_map_notify_event_t* ev = (xcb_map_notify_event_t*)event; 2426 if (!ev->override_redirect && ev->event != ev->window) { 2427 xcb_connection_t* connection = QX11Info::connection(); 2428 const VideoWidgetList::iterator e = video_widgets.end (); 2429 for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i) { 2430 if (ev->event == (*i)->ownHandle()) { 2431 (*i)->embedded(ev->window); 2432 return false; 2433 } 2434 xcb_window_t p = ev->event; 2435 xcb_window_t w = ev->window; 2436 xcb_window_t v = (*i)->clientHandle (); 2437 xcb_window_t va = winId (); 2438 xcb_window_t root = 0; 2439 while (p != v) { 2440 xcb_query_tree_cookie_t cookie = xcb_query_tree(connection, w); 2441 xcb_query_tree_reply_t *reply = xcb_query_tree_reply(connection, cookie, NULL); 2442 if (reply) { 2443 p = reply->parent; 2444 root = reply->root; 2445 free(reply); 2446 } else { 2447 qDebug("failed to get x parent"); 2448 break; 2449 } 2450 if (p == va || p == v || p == root) 2451 break; 2452 w = p; 2453 } 2454 if (p == v) { 2455 setXSelectInput (ev->window, 2456 static_cast <VideoOutput *>(*i)->inputMask ()); 2457 break; 2458 } 2459 } 2460 } 2461 break; 2462 } 2463 case XCB_MOTION_NOTIFY: { 2464 xcb_motion_notify_event_t* ev = (xcb_motion_notify_event_t*)event; 2465 if (m_view->controlPanelMode () == View::CP_AutoHide) { 2466 const VideoWidgetList::iterator e = video_widgets.end (); 2467 for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i) { 2468 QPoint p = mapToGlobal (QPoint (0, 0)); 2469 int x = ev->root_x - p.x (); 2470 int y = ev->root_y - p.y (); 2471 #if QT_VERSION >= 0x050600 2472 m_view->mouseMoved(x / devicePixelRatioF(), y / devicePixelRatioF()); 2473 int devicew = (int)(width() * devicePixelRatioF()); 2474 int deviceh = (int)(height() * devicePixelRatioF()); 2475 #else 2476 m_view->mouseMoved (x, y); 2477 int devicew = width(); 2478 int deviceh = height(); 2479 #endif 2480 if (x > 0 && x < devicew && y > 0 && y < deviceh) 2481 mouseMoved (); 2482 } 2483 } 2484 break; 2485 } 2486 case XCB_KEY_PRESS: { 2487 xcb_key_press_event_t* ev = (xcb_key_press_event_t*)event; 2488 const VideoWidgetList::iterator e = video_widgets.end (); 2489 for (VideoWidgetList::iterator i=video_widgets.begin(); i != e; ++i) 2490 if ((*i)->clientHandle () == ev->event && 2491 static_cast <VideoOutput *>(*i)->inputMask() & XCB_EVENT_MASK_KEY_PRESS) { 2492 if (ev->detail == 41 /*FIXME 'f'*/) 2493 m_view->fullScreen (); 2494 break; 2495 } 2496 break; 2497 } 2498 default: 2499 break; 2500 } 2501 return false; 2502 } 2503 2504 //---------------------------------------------------------------------- 2505 2506 KDE_NO_CDTOR_EXPORT VideoOutput::VideoOutput (QWidget *parent, View * view) 2507 : QX11EmbedContainer (parent), 2508 m_plain_window(0), m_client_window(0), resized_timer(0), 2509 m_bgcolor (0), m_aspect (0.0), 2510 m_view (view) 2511 { 2512 setAcceptDrops (true); 2513 connect (view->viewArea (), SIGNAL (fullScreenChanged ()), 2514 this, SLOT (fullScreenChanged ())); 2515 kDebug() << "VideoOutput::VideoOutput" << endl; 2516 setMonitoring (MonitorAll); 2517 setAttribute (Qt::WA_NoSystemBackground, true); 2518 2519 xcb_connection_t* connection = QX11Info::connection(); 2520 xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(connection, winId()); 2521 xcb_get_window_attributes_reply_t* attrs = xcb_get_window_attributes_reply(connection, cookie, NULL); 2522 if (!(attrs->your_event_mask & XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY)) 2523 setXSelectInput(winId(), attrs->your_event_mask | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY); 2524 free(attrs); 2525 //setProtocol (QXEmbed::XPLAIN); 2526 } 2527 2528 KDE_NO_CDTOR_EXPORT VideoOutput::~VideoOutput () { 2529 kDebug() << "VideoOutput::~VideoOutput" << endl; 2530 if (m_plain_window) { 2531 xcb_connection_t* connection = QX11Info::connection(); 2532 xcb_destroy_window(connection, m_plain_window); 2533 xcb_flush(connection); 2534 m_plain_window = 0; 2535 } 2536 } 2537 2538 void VideoOutput::useIndirectWidget (bool inderect) { 2539 kDebug () << "setIntermediateWindow " << !!m_plain_window << "->" << inderect; 2540 if (!clientWinId () || !!m_plain_window != inderect) { 2541 xcb_connection_t* connection = QX11Info::connection(); 2542 if (inderect) { 2543 if (!m_plain_window) { 2544 xcb_screen_t* scr = m_view->viewArea()->d->screen_of_display(connection, QX11Info::appScreen()); 2545 m_plain_window = xcb_generate_id(connection); 2546 uint32_t values[] = { scr->black_pixel, m_input_mask }; 2547 #if QT_VERSION >= 0x050600 2548 int devicew = (int)(width() * devicePixelRatioF()); 2549 int deviceh = (int)(height() * devicePixelRatioF()); 2550 #else 2551 int devicew = width(); 2552 int deviceh = height(); 2553 #endif 2554 xcb_create_window(connection, 2555 XCB_COPY_FROM_PARENT, m_plain_window, winId(), 2556 0, 0, devicew, deviceh, 2557 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 2558 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, values); 2559 xcb_map_window(connection, m_plain_window); 2560 xcb_flush(connection); 2561 //XSync (QX11Info::display (), false); 2562 //embedClient (m_plain_window); 2563 } 2564 //XClearWindow (QX11Info::display(), m_plain_window); 2565 } else { 2566 if (m_plain_window) { 2567 xcb_unmap_window(connection, m_plain_window); 2568 discardClient (); 2569 xcb_destroy_window(connection, m_plain_window); 2570 xcb_flush(connection); 2571 m_plain_window = 0; 2572 //XSync (QX11Info::display (), false); 2573 } 2574 } 2575 } 2576 } 2577 2578 KDE_NO_EXPORT void VideoOutput::embedded(WindowId handle) { 2579 kDebug () << "[01;35mwindowChanged[00m " << (int)clientWinId (); 2580 m_client_window = handle; 2581 if (clientWinId () && !resized_timer) 2582 resized_timer = startTimer (50); 2583 if (clientWinId()) 2584 setXSelectInput (clientWinId (), m_input_mask); 2585 } 2586 2587 KDE_NO_EXPORT void VideoOutput::resizeEvent (QResizeEvent *) { 2588 if (clientWinId () && !resized_timer) 2589 resized_timer = startTimer (50); 2590 } 2591 2592 KDE_NO_EXPORT void VideoOutput::timerEvent (QTimerEvent *e) { 2593 if (e->timerId () == resized_timer) { 2594 killTimer (resized_timer); 2595 resized_timer = 0; 2596 if (clientWinId ()) { 2597 xcb_connection_t* connection = QX11Info::connection(); 2598 #if QT_VERSION >= 0x050600 2599 uint32_t devicew = (uint32_t)(width() * devicePixelRatioF()); 2600 uint32_t deviceh = (uint32_t)(height() * devicePixelRatioF()); 2601 #else 2602 uint32_t devicew = width(); 2603 uint32_t deviceh = height(); 2604 #endif 2605 uint32_t values[] = { 0, 0, devicew, deviceh }; 2606 xcb_configure_window(connection, clientWinId(), 2607 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | 2608 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, 2609 values); 2610 xcb_flush(connection); 2611 } 2612 } 2613 } 2614 2615 WindowId VideoOutput::windowHandle () { 2616 //return m_plain_window ? clientWinId () : winId (); 2617 return m_plain_window ? m_plain_window : winId (); 2618 } 2619 2620 WindowId VideoOutput::ownHandle () { 2621 return winId (); 2622 } 2623 2624 WindowId VideoOutput::clientHandle () { 2625 return clientWinId (); 2626 } 2627 2628 void VideoOutput::setGeometry (const IRect &rect) { 2629 #if QT_VERSION >= 0x050600 2630 int x = (int)(rect.x() / devicePixelRatioF()); 2631 int y = (int)(rect.y() / devicePixelRatioF()); 2632 int w = (int)(rect.width() / devicePixelRatioF()); 2633 int h = (int)(rect.height() / devicePixelRatioF()); 2634 #else 2635 int x = rect.x (), y = rect.y (), w = rect.width (), h = rect.height (); 2636 #endif 2637 if (m_view->keepSizeRatio ()) { 2638 // scale video widget inside region 2639 int hfw = heightForWidth (w); 2640 if (hfw > 0) { 2641 if (hfw > h) { 2642 int old_w = w; 2643 w = int ((1.0 * h * w)/(1.0 * hfw)); 2644 x += (old_w - w) / 2; 2645 } else { 2646 y += (h - hfw) / 2; 2647 h = hfw; 2648 } 2649 } 2650 } 2651 setGeometry (x, y, w, h); 2652 setVisible (true); 2653 } 2654 2655 void VideoOutput::setAspect (float a) { 2656 m_aspect = a; 2657 QRect r = geometry (); 2658 #if QT_VERSION >= 0x050600 2659 int x = (int)(r.x() * devicePixelRatioF()); 2660 int y = (int)(r.y() * devicePixelRatioF()); 2661 int w = (int)(r.width() * devicePixelRatioF()); 2662 int h = (int)(r.height() * devicePixelRatioF()); 2663 #else 2664 int x = r.x(); 2665 int y = r.y(); 2666 int w = r.width(); 2667 int h = r.height(); 2668 #endif 2669 m_view->viewArea()->scheduleRepaint(IRect(x, y, w, h)); 2670 } 2671 2672 KDE_NO_EXPORT void VideoOutput::map () { 2673 setVisible (true); 2674 } 2675 2676 KDE_NO_EXPORT void VideoOutput::unmap () { 2677 setVisible (false); 2678 } 2679 2680 KDE_NO_EXPORT void VideoOutput::setMonitoring (Monitor m) { 2681 m_input_mask = 2682 //KeyPressMask | KeyReleaseMask | 2683 //EnterWindowMask | LeaveWindowMask | 2684 //FocusChangeMask | 2685 XCB_EVENT_MASK_EXPOSURE | 2686 //StructureNotifyMask | 2687 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; 2688 if (m & MonitorMouse) 2689 m_input_mask |= XCB_EVENT_MASK_POINTER_MOTION; 2690 if (m & MonitorKey) 2691 m_input_mask |= XCB_EVENT_MASK_KEY_PRESS; 2692 if (clientWinId ()) 2693 setXSelectInput (clientWinId (), m_input_mask); 2694 } 2695 2696 KDE_NO_EXPORT void VideoOutput::fullScreenChanged () { 2697 if (!(m_input_mask & XCB_EVENT_MASK_KEY_PRESS)) { // FIXME: store monitor when needed 2698 if (m_view->isFullScreen ()) 2699 m_input_mask |= XCB_EVENT_MASK_POINTER_MOTION; 2700 else 2701 m_input_mask &= ~XCB_EVENT_MASK_POINTER_MOTION; 2702 } 2703 if (clientWinId ()) 2704 setXSelectInput (clientWinId (), m_input_mask); 2705 } 2706 2707 KDE_NO_EXPORT int VideoOutput::heightForWidth (int w) const { 2708 if (m_aspect <= 0.01) 2709 return 0; 2710 return int (w/m_aspect); 2711 } 2712 2713 KDE_NO_EXPORT void VideoOutput::dropEvent (QDropEvent * de) { 2714 m_view->dropEvent (de); 2715 } 2716 2717 KDE_NO_EXPORT void VideoOutput::dragEnterEvent (QDragEnterEvent* dee) { 2718 m_view->dragEnterEvent (dee); 2719 } 2720 2721 KDE_NO_EXPORT void VideoOutput::contextMenuEvent (QContextMenuEvent * e) { 2722 m_view->controlPanel ()->popupMenu->exec (e->globalPos ()); 2723 } 2724 2725 KDE_NO_EXPORT void VideoOutput::setBackgroundColor (const QColor & c) { 2726 if (m_bgcolor != c.rgb ()) { 2727 m_bgcolor = c.rgb (); 2728 setCurrentBackgroundColor (c); 2729 } 2730 } 2731 2732 KDE_NO_EXPORT void VideoOutput::resetBackgroundColor () { 2733 setCurrentBackgroundColor (m_bgcolor); 2734 } 2735 2736 KDE_NO_EXPORT void VideoOutput::setCurrentBackgroundColor (const QColor & c) { 2737 QPalette palette; 2738 palette.setColor (backgroundRole(), c); 2739 setPalette (palette); 2740 if (clientWinId()) { 2741 xcb_connection_t* connection = QX11Info::connection(); 2742 const uint32_t values[] = { c.rgb() }; 2743 xcb_change_window_attributes(connection, clientWinId(), XCB_CW_BACK_PIXEL, values); 2744 xcb_flush(connection); 2745 } 2746 } 2747 2748 #include "viewarea.moc"