1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "mediawindow_impl.hxx"
22 #include "mediaevent_impl.hxx"
23 #include <mediamisc.hxx>
24 #include <bitmaps.hlst>
28 #include <string_view>
30 #include <sal/log.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/diagnose_ex.hxx>
33 #include <comphelper/DirectoryHelper.hxx>
34 #include <comphelper/scopeguard.hxx>
35 #include <tools/urlobj.hxx>
36 #include <unotools/securityoptions.hxx>
37 #include <vcl/bitmapex.hxx>
38 #include <vcl/sysdata.hxx>
39 #include <vcl/commandevent.hxx>
40 #include <vcl/event.hxx>
41 #include <vcl/ptrstyle.hxx>
42 #include <vcl/svapp.hxx>
44 #include <com/sun/star/awt/SystemPointer.hpp>
45 #include <com/sun/star/lang/XComponent.hpp>
46 #include <com/sun/star/media/XManager.hpp>
47 #include <com/sun/star/uno/XComponentContext.hpp>
48 using namespace ::com::sun::star
;
50 namespace avmedia::priv
{
52 MediaWindowControl::MediaWindowControl(vcl::Window
* pParent
)
53 : MediaControl(pParent
, MediaControlStyle::MultiLine
)
57 void MediaWindowControl::update()
61 static_cast< MediaWindowImpl
* >( GetParent() )->updateMediaItem( aItem
);
65 void MediaWindowControl::execute(const MediaItem
& rItem
)
67 static_cast<MediaWindowImpl
*>(GetParent())->executeMediaItem(rItem
);
70 MediaChildWindow::MediaChildWindow(vcl::Window
* pParent
)
71 : SystemChildWindow(pParent
, WB_CLIPCHILDREN
)
75 void MediaChildWindow::MouseMove( const MouseEvent
& rMEvt
)
77 const MouseEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt
.GetPosPixel() ) ),
78 rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(), rMEvt
.GetModifier() );
80 SystemChildWindow::MouseMove( rMEvt
);
81 GetParent()->MouseMove( aTransformedEvent
);
84 void MediaChildWindow::MouseButtonDown( const MouseEvent
& rMEvt
)
86 const MouseEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt
.GetPosPixel() ) ),
87 rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(), rMEvt
.GetModifier() );
89 SystemChildWindow::MouseButtonDown( rMEvt
);
90 GetParent()->MouseButtonDown( aTransformedEvent
);
93 void MediaChildWindow::MouseButtonUp( const MouseEvent
& rMEvt
)
95 const MouseEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt
.GetPosPixel() ) ),
96 rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(), rMEvt
.GetModifier() );
98 SystemChildWindow::MouseButtonUp( rMEvt
);
99 GetParent()->MouseButtonUp( aTransformedEvent
);
102 void MediaChildWindow::KeyInput( const KeyEvent
& rKEvt
)
104 SystemChildWindow::KeyInput( rKEvt
);
105 GetParent()->KeyInput( rKEvt
);
108 void MediaChildWindow::KeyUp( const KeyEvent
& rKEvt
)
110 SystemChildWindow::KeyUp( rKEvt
);
111 GetParent()->KeyUp( rKEvt
);
114 void MediaChildWindow::Command( const CommandEvent
& rCEvt
)
116 const CommandEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rCEvt
.GetMousePosPixel() ) ),
117 rCEvt
.GetCommand(), rCEvt
.IsMouseEvent(), rCEvt
.GetEventData() );
119 SystemChildWindow::Command( rCEvt
);
120 GetParent()->Command( aTransformedEvent
);
123 MediaWindowImpl::MediaWindowImpl(vcl::Window
* pParent
, MediaWindow
* pMediaWindow
, bool bInternalMediaControl
)
125 , DropTargetHelper(this)
126 , DragSourceHelper(this)
127 , mpMediaWindow(pMediaWindow
)
128 , mpMediaWindowControl(bInternalMediaControl
? VclPtr
<MediaWindowControl
>::Create(this) : nullptr)
130 if (mpMediaWindowControl
)
132 mpMediaWindowControl
->SetSizePixel(mpMediaWindowControl
->GetOptimalSize());
133 mpMediaWindowControl
->Show();
137 MediaWindowImpl::~MediaWindowImpl()
142 void MediaWindowImpl::dispose()
147 if (mxPlayerWindow
.is())
149 mxPlayerWindow
->removeKeyListener( uno::Reference
< awt::XKeyListener
>( mxEvents
) );
150 mxPlayerWindow
->removeMouseListener( uno::Reference
< awt::XMouseListener
>( mxEvents
) );
151 mxPlayerWindow
->removeMouseMotionListener( uno::Reference
< awt::XMouseMotionListener
>( mxEvents
) );
152 mxPlayerWindow
->dispose();
153 mxPlayerWindow
.clear();
156 uno::Reference
< lang::XComponent
> xComponent( mxPlayer
, uno::UNO_QUERY
);
157 if (xComponent
.is()) // this stops the player
158 xComponent
->dispose();
162 mpMediaWindow
= nullptr;
164 mpEmptyBmpEx
.reset();
165 mpAudioBmpEx
.reset();
166 mpMediaWindowControl
.disposeAndClear();
167 mpChildWindow
.disposeAndClear();
172 uno::Reference
<media::XPlayer
> MediaWindowImpl::createPlayer(const OUString
& rURL
, const OUString
& rReferer
, const OUString
*)
177 if (SvtSecurityOptions::isUntrustedReferer(rReferer
))
180 if (INetURLObject(rURL
).IsExoticProtocol())
183 uno::Reference
<media::XPlayer
> xPlayer
;
185 // currently there isn't anything else, throw any mime type to the media players
186 //if (!pMimeType || *pMimeType == AVMEDIA_MIMETYPE_COMMON)
188 const uno::Reference
<uno::XComponentContext
>& xContext(::comphelper::getProcessComponentContext());
189 const OUString sToolkitName
= Application::GetToolkitName();
190 if (sToolkitName
== "gtk4")
191 xPlayer
= createPlayer(rURL
, u
"com.sun.star.comp.avmedia.Manager_Gtk"_ustr
, xContext
);
192 else if (sToolkitName
.startsWith(u
"kf6") || sToolkitName
.startsWith(u
"qt6"))
193 xPlayer
= createPlayer(rURL
, u
"com.sun.star.comp.avmedia.Manager_Qt"_ustr
, xContext
);
195 xPlayer
= createPlayer(rURL
, u
"" AVMEDIA_MANAGER_SERVICE_NAME
""_ustr
, xContext
);
201 uno::Reference
< media::XPlayer
> MediaWindowImpl::createPlayer(
202 const OUString
& rURL
, const OUString
& rManagerServName
,
203 const uno::Reference
< uno::XComponentContext
>& xContext
)
205 uno::Reference
< media::XPlayer
> xPlayer
;
208 uno::Reference
< media::XManager
> xManager (
209 xContext
->getServiceManager()->createInstanceWithContext(rManagerServName
, xContext
),
212 xPlayer
= xManager
->createPlayer( rURL
);
214 SAL_INFO( "avmedia", "failed to create media player service " << rManagerServName
);
215 } catch ( const uno::Exception
& )
217 TOOLS_WARN_EXCEPTION( "avmedia", "couldn't create media player " << rManagerServName
);
222 void MediaWindowImpl::setURL( const OUString
& rURL
,
223 OUString
const& rTempURL
, OUString
const& rReferer
)
225 maReferer
= rReferer
;
226 if( rURL
== getURL() )
232 if( mxPlayerWindow
.is() )
234 mxPlayerWindow
->setVisible( false );
235 mxPlayerWindow
.clear();
239 mTempFileURL
.clear();
241 if (!rTempURL
.isEmpty())
244 mTempFileURL
= rTempURL
;
248 INetURLObject
aURL( rURL
);
250 if (aURL
.GetProtocol() != INetProtocol::NotValid
)
251 maFileURL
= aURL
.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous
);
257 // If the file with the given URL does not exist and a fallback is specified, then use it
258 if ( rURL
.startsWith("file:///")
259 && !comphelper::DirectoryHelper::fileExists(maFileURL
)
260 && maFallbackFileURL
.getLength() > 0 )
262 mediaURL
= maFallbackFileURL
;
265 mediaURL
= (!mTempFileURL
.isEmpty()) ? mTempFileURL
: maFileURL
;
267 mxPlayer
= createPlayer(mediaURL
, rReferer
, &m_sMimeType
);
272 const OUString
& MediaWindowImpl::getURL() const
277 void MediaWindowImpl::setFallbackURL( const OUString
& rURL
)
279 maFallbackFileURL
= rURL
;
282 const OUString
& MediaWindowImpl::getFallbackURL() const
284 return maFallbackFileURL
;
287 bool MediaWindowImpl::isValid() const
289 return mxPlayer
.is();
292 Size
MediaWindowImpl::getPreferredSize() const
298 awt::Size
aPrefSize( mxPlayer
->getPreferredPlayerWindowSize() );
300 aRet
.setWidth( aPrefSize
.Width
);
301 aRet
.setHeight( aPrefSize
.Height
);
307 bool MediaWindowImpl::start()
309 return mxPlayer
.is() && ( mxPlayer
->start(), true );
312 void MediaWindowImpl::updateMediaItem( MediaItem
& rItem
) const
315 rItem
.setState( MediaState::Play
);
317 rItem
.setState( ( getMediaTime() == 0.0 ) ? MediaState::Stop
: MediaState::Pause
);
319 rItem
.setDuration( getDuration() );
320 rItem
.setTime( getMediaTime() );
321 rItem
.setLoop( mxPlayer
.is() && mxPlayer
->isPlaybackLoop() );
322 rItem
.setMute( mxPlayer
.is() && mxPlayer
->isMute() );
323 rItem
.setVolumeDB( mxPlayer
.is() ? mxPlayer
->getVolumeDB() : 0 );
324 rItem
.setZoom( mxPlayerWindow
.is() ? mxPlayerWindow
->getZoomLevel() : media::ZoomLevel_NOT_AVAILABLE
);
325 rItem
.setFallbackURL( getFallbackURL() );
326 rItem
.setURL( getURL(), mTempFileURL
, maReferer
);
329 void MediaWindowImpl::executeMediaItem( const MediaItem
& rItem
)
332 comphelper::ScopeGuard
g([this] { this->mpItem
= nullptr; });
334 const AVMediaSetMask nMaskSet
= rItem
.getMaskSet();
337 if (nMaskSet
& AVMediaSetMask::URL
)
339 m_sMimeType
= rItem
.getMimeType();
340 setFallbackURL(rItem
.getFallbackURL());
341 setURL(rItem
.getURL(), rItem
.getTempURL(), rItem
.getReferer());
344 // set different states next
345 if (nMaskSet
& AVMediaSetMask::TIME
)
346 setMediaTime(std::min(rItem
.getTime(), getDuration()));
348 if (nMaskSet
& AVMediaSetMask::LOOP
&& mxPlayer
.is() )
349 mxPlayer
->setPlaybackLoop( rItem
.isLoop() );
351 if (nMaskSet
& AVMediaSetMask::MUTE
&& mxPlayer
.is() )
352 mxPlayer
->setMute( rItem
.isMute() );
354 if (nMaskSet
& AVMediaSetMask::VOLUMEDB
&& mxPlayer
.is() )
355 mxPlayer
->setVolumeDB( rItem
.getVolumeDB() );
357 if (nMaskSet
& AVMediaSetMask::ZOOM
&& mxPlayerWindow
.is() )
358 mxPlayerWindow
->setZoomLevel( rItem
.getZoom() );
360 // set play state at last
361 if (!(nMaskSet
& AVMediaSetMask::STATE
))
364 switch (rItem
.getState())
366 case MediaState::Play
:
373 case MediaState::Pause
:
380 case MediaState::Stop
:
393 void MediaWindowImpl::stop()
399 bool MediaWindowImpl::isPlaying() const
401 return( mxPlayer
.is() && mxPlayer
->isPlaying() );
404 double MediaWindowImpl::getDuration() const
406 return( mxPlayer
.is() ? mxPlayer
->getDuration() : 0.0 );
409 void MediaWindowImpl::setMediaTime( double fTime
)
412 mxPlayer
->setMediaTime( fTime
);
415 double MediaWindowImpl::getMediaTime() const
417 return( mxPlayer
.is() ? mxPlayer
->getMediaTime() : 0.0 );
420 void MediaWindowImpl::stopPlayingInternal(bool bStop
)
424 bStop
? mxPlayer
->stop() : mxPlayer
->start();
428 void MediaWindowImpl::onURLChanged()
430 //if (m_sMimeType == AVMEDIA_MIMETYPE_COMMON)
432 mpChildWindow
.disposeAndClear();
433 mpChildWindow
.reset(VclPtr
<MediaChildWindow
>::Create(this));
437 mpChildWindow
->SetHelpId(HID_AVMEDIA_PLAYERWINDOW
);
438 mxEvents
= new MediaEventListenersImpl(*mpChildWindow
);
443 uno::Reference
<media::XPlayerWindow
> xPlayerWindow
;
445 const Size
aSize(mpChildWindow
->GetSizePixel());
447 sal_IntPtr
nParentWindowHandle(0);
448 const SystemEnvData
* pEnvData
= mpChildWindow
->GetSystemData();
449 // tdf#139609 gtk doesn't need the handle, and fetching it is undesirable
450 if (!pEnvData
|| pEnvData
->toolkit
!= SystemEnvData::Toolkit::Gtk
)
451 nParentWindowHandle
= mpChildWindow
->GetParentWindowHandle();
452 uno::Sequence
<uno::Any
> aArgs
{
453 uno::Any(nParentWindowHandle
),
454 uno::Any(awt::Rectangle(aPoint
.X(), aPoint
.Y(), aSize
.Width(), aSize
.Height())),
455 uno::Any(reinterpret_cast<sal_IntPtr
>(mpChildWindow
.get())),
456 // Media item contains media properties, e.g. cropping.
457 uno::Any(reinterpret_cast<sal_IntPtr
>(mpItem
))
462 xPlayerWindow
= mxPlayer
->createPlayerWindow( aArgs
);
464 catch( const uno::RuntimeException
& )
466 // happens eg, on MacOSX where Java frames cannot be created from X11 window handles
469 mxPlayerWindow
= xPlayerWindow
;
471 if( xPlayerWindow
.is() )
473 xPlayerWindow
->addKeyListener( uno::Reference
< awt::XKeyListener
>( mxEvents
) );
474 xPlayerWindow
->addMouseListener( uno::Reference
< awt::XMouseListener
>( mxEvents
) );
475 xPlayerWindow
->addMouseMotionListener( uno::Reference
< awt::XMouseMotionListener
>( mxEvents
) );
476 xPlayerWindow
->addFocusListener( uno::Reference
< awt::XFocusListener
>( mxEvents
) );
480 mxPlayerWindow
.clear();
482 if( mxPlayerWindow
.is() )
483 mpChildWindow
->Show();
485 mpChildWindow
->Hide();
487 if( mpMediaWindowControl
)
491 updateMediaItem( aItem
);
492 mpMediaWindowControl
->setState( aItem
);
496 void MediaWindowImpl::setPosSize(const tools::Rectangle
& rRect
)
498 SetPosSizePixel(rRect
.TopLeft(), rRect
.GetSize());
501 void MediaWindowImpl::setPointer(PointerStyle aPointer
)
503 SetPointer(aPointer
);
506 mpChildWindow
->SetPointer(aPointer
);
508 if (!mxPlayerWindow
.is())
515 case PointerStyle::Cross
:
516 nPointer
= awt::SystemPointer::CROSS
;
518 case PointerStyle::Hand
:
519 nPointer
= awt::SystemPointer::HAND
;
521 case PointerStyle::Move
:
522 nPointer
= awt::SystemPointer::MOVE
;
524 case PointerStyle::Wait
:
525 nPointer
= awt::SystemPointer::WAIT
;
528 nPointer
= awt::SystemPointer::ARROW
;
532 mxPlayerWindow
->setPointerType(nPointer
);
535 void MediaWindowImpl::Resize()
537 const Size
aCurSize(GetOutputSizePixel());
538 const sal_Int32
nOffset(mpMediaWindowControl
? AVMEDIA_CONTROLOFFSET
: 0);
540 Size
aPlayerWindowSize(aCurSize
.Width() - (nOffset
<< 1),
541 aCurSize
.Height() - (nOffset
<< 1));
543 if (mpMediaWindowControl
)
545 const sal_Int32 nControlHeight
= mpMediaWindowControl
->GetSizePixel().Height();
546 const sal_Int32 nControlY
= std::max(aCurSize
.Height() - nControlHeight
- nOffset
, tools::Long(0));
548 aPlayerWindowSize
.setHeight( nControlY
- (nOffset
<< 1) );
549 mpMediaWindowControl
->SetPosSizePixel(Point(nOffset
, nControlY
), Size(aCurSize
.Width() - (nOffset
<< 1), nControlHeight
));
552 mpChildWindow
->SetPosSizePixel(Point(0, 0), aPlayerWindowSize
);
554 if (mxPlayerWindow
.is())
555 mxPlayerWindow
->setPosSize(0, 0, aPlayerWindowSize
.Width(), aPlayerWindowSize
.Height(), 0);
558 void MediaWindowImpl::StateChanged(StateChangedType eType
)
560 if (!mxPlayerWindow
.is())
563 // stop playing when going disabled or hidden
566 case StateChangedType::Visible
:
568 stopPlayingInternal(!IsVisible());
569 mxPlayerWindow
->setVisible(IsVisible());
573 case StateChangedType::Enable
:
575 stopPlayingInternal(!IsEnabled());
576 mxPlayerWindow
->setEnable(IsEnabled());
585 void MediaWindowImpl::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
&)
587 if (mxPlayerWindow
.is())
588 mxPlayerWindow
->update();
590 BitmapEx
* pLogo
= nullptr;
595 mpEmptyBmpEx
.reset(new BitmapEx(AVMEDIA_BMP_EMPTYLOGO
));
597 pLogo
= mpEmptyBmpEx
.get();
599 else if (!mxPlayerWindow
.is())
602 mpAudioBmpEx
.reset(new BitmapEx(AVMEDIA_BMP_AUDIOLOGO
));
604 pLogo
= mpAudioBmpEx
.get();
610 const Point
aBasePos(mpChildWindow
->GetPosPixel());
611 const tools::Rectangle
aVideoRect(aBasePos
, mpChildWindow
->GetSizePixel());
613 if (!pLogo
|| pLogo
->IsEmpty() || aVideoRect
.IsEmpty())
616 Size
aLogoSize(pLogo
->GetSizePixel());
617 const Color
aBackgroundColor(67, 67, 67);
619 rRenderContext
.SetLineColor(aBackgroundColor
);
620 rRenderContext
.SetFillColor(aBackgroundColor
);
621 rRenderContext
.DrawRect(aVideoRect
);
623 if ((aLogoSize
.Width() > aVideoRect
.GetWidth() || aLogoSize
.Height() > aVideoRect
.GetHeight() ) &&
624 (aLogoSize
.Height() > 0))
626 const double fLogoWH
= double(aLogoSize
.Width()) / aLogoSize
.Height();
628 if (fLogoWH
< (double(aVideoRect
.GetWidth()) / aVideoRect
.GetHeight()))
630 aLogoSize
.setWidth( tools::Long(aVideoRect
.GetHeight() * fLogoWH
) );
631 aLogoSize
.setHeight( aVideoRect
.GetHeight() );
635 aLogoSize
.setWidth( aVideoRect
.GetWidth() );
636 aLogoSize
.setHeight( tools::Long(aVideoRect
.GetWidth() / fLogoWH
) );
640 Point
aPoint(aBasePos
.X() + ((aVideoRect
.GetWidth() - aLogoSize
.Width()) >> 1),
641 aBasePos
.Y() + ((aVideoRect
.GetHeight() - aLogoSize
.Height()) >> 1));
643 rRenderContext
.DrawBitmapEx(aPoint
, aLogoSize
, *pLogo
);
646 void MediaWindowImpl::GetFocus()
650 void MediaWindowImpl::MouseMove(const MouseEvent
& rMEvt
)
653 mpMediaWindow
->MouseMove(rMEvt
);
656 void MediaWindowImpl::MouseButtonDown(const MouseEvent
& rMEvt
)
659 mpMediaWindow
->MouseButtonDown(rMEvt
);
662 void MediaWindowImpl::MouseButtonUp(const MouseEvent
& rMEvt
)
665 mpMediaWindow
->MouseButtonUp(rMEvt
);
668 void MediaWindowImpl::KeyInput(const KeyEvent
& rKEvt
)
671 mpMediaWindow
->KeyInput(rKEvt
);
674 void MediaWindowImpl::KeyUp(const KeyEvent
& rKEvt
)
677 mpMediaWindow
->KeyUp(rKEvt
);
680 void MediaWindowImpl::Command(const CommandEvent
& rCEvt
)
683 mpMediaWindow
->Command(rCEvt
);
686 sal_Int8
MediaWindowImpl::AcceptDrop(const AcceptDropEvent
& rEvt
)
688 return (mpMediaWindow
? mpMediaWindow
->AcceptDrop(rEvt
) : 0);
691 sal_Int8
MediaWindowImpl::ExecuteDrop(const ExecuteDropEvent
& rEvt
)
693 return (mpMediaWindow
? mpMediaWindow
->ExecuteDrop(rEvt
) : 0);
696 void MediaWindowImpl::StartDrag(sal_Int8 nAction
, const Point
& rPosPixel
)
699 mpMediaWindow
->StartDrag(nAction
, rPosPixel
);
704 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */