You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
385 lines
9.8 KiB
385 lines
9.8 KiB
// (C) 2005 Max Howell (max.howell@methylblue.com)
|
|
// See COPYING file for licensing information
|
|
|
|
#define CODEINE_DEBUG_PREFIX "VideoWindow"
|
|
|
|
#include <cmath> //std::log10
|
|
#include <cstdlib>
|
|
#include <tdeapplication.h> //::makeStandardCaption
|
|
#include <tdeconfig.h>
|
|
#include <kiconloader.h>
|
|
#include <tdepopupmenu.h>
|
|
#include <twin.h>
|
|
#include <tqcursor.h>
|
|
#include <tqevent.h>
|
|
#include <tqslider.h>
|
|
#include <X11/Xlib.h>
|
|
|
|
#include "../debug.h"
|
|
#include "../mxcl.library.h"
|
|
#include "actions.h"
|
|
#include "slider.h"
|
|
#include "theStream.h"
|
|
#include "xineEngine.h"
|
|
|
|
|
|
namespace Codeine
|
|
{
|
|
namespace X
|
|
{
|
|
// we get thread locks if we don't cache these values
|
|
// (I don't know which ones exactly)
|
|
Display *d;
|
|
int s, w;
|
|
}
|
|
|
|
|
|
void
|
|
VideoWindow::initVideo()
|
|
{
|
|
X::d = XOpenDisplay( std::getenv("DISPLAY") );
|
|
X::s = DefaultScreen( X::d );
|
|
X::w = winId();
|
|
|
|
XLockDisplay( X::d );
|
|
XSelectInput( X::d, X::w, ExposureMask );
|
|
|
|
{
|
|
using X::d; using X::s;
|
|
|
|
//these are Xlib macros
|
|
double w = DisplayWidth( d, s ) * 1000 / DisplayWidthMM( d, s );
|
|
double h = DisplayHeight( d, s ) * 1000 / DisplayHeightMM( d, s );
|
|
|
|
m_displayRatio = w / h;
|
|
}
|
|
|
|
connect( &m_timer, TQ_SIGNAL(timeout()), TQ_SLOT(hideCursor()) );
|
|
|
|
XUnlockDisplay( X::d );
|
|
}
|
|
|
|
void
|
|
VideoWindow::cleanUpVideo()
|
|
{
|
|
XCloseDisplay( X::d );
|
|
}
|
|
|
|
void*
|
|
VideoWindow::x11Visual() const
|
|
{
|
|
DEBUG_FUNC_INFO
|
|
|
|
x11_visual_t* visual = new x11_visual_t;
|
|
|
|
visual->display = X::d;
|
|
visual->screen = X::s;
|
|
visual->d = winId();//X::w;
|
|
visual->dest_size_cb = &VideoWindow::destSizeCallBack;
|
|
visual->frame_output_cb = &VideoWindow::frameOutputCallBack;
|
|
visual->user_data = (void*)this;
|
|
|
|
return visual;
|
|
}
|
|
|
|
void
|
|
VideoWindow::destSizeCallBack(
|
|
void* p, int /*video_width*/, int /*video_height*/,
|
|
double /*video_aspect*/, int* dest_width,
|
|
int* dest_height, double* dest_aspect )
|
|
{
|
|
if( !p )
|
|
return;
|
|
|
|
#define vw static_cast<VideoWindow*>(p)
|
|
|
|
*dest_width = vw->width();
|
|
*dest_height = vw->height();
|
|
*dest_aspect = vw->m_displayRatio;
|
|
}
|
|
|
|
void
|
|
VideoWindow::frameOutputCallBack(
|
|
void* p, int video_width, int video_height, double video_aspect,
|
|
int* dest_x, int* dest_y, int* dest_width, int* dest_height,
|
|
double* dest_aspect, int* win_x, int* win_y )
|
|
{
|
|
if( !p )
|
|
return;
|
|
|
|
*dest_x = 0;
|
|
*dest_y = 0 ;
|
|
*dest_width = vw->width();
|
|
*dest_height = vw->height();
|
|
*win_x = vw->x();
|
|
*win_y = vw->y();
|
|
*dest_aspect = vw->m_displayRatio;
|
|
|
|
// correct size with video aspect
|
|
// TODO what's this about?
|
|
if( video_aspect >= vw->m_displayRatio )
|
|
video_width = (int) ( (double) (video_width * video_aspect / vw->m_displayRatio + 0.5) );
|
|
else
|
|
video_height = (int) ( (double) (video_height * vw->m_displayRatio / video_aspect) + 0.5);
|
|
|
|
#undef vw
|
|
}
|
|
|
|
void
|
|
VideoWindow::contextMenuEvent( TQContextMenuEvent *e )
|
|
{
|
|
e->accept();
|
|
|
|
TDEPopupMenu popup;
|
|
|
|
if( state() == Engine::Playing )
|
|
popup.insertItem( SmallIconSet("media-playback-pause"), i18n("Pause"), 1 );
|
|
else
|
|
action( "play" )->plug( &popup );
|
|
|
|
popup.insertSeparator();
|
|
|
|
if (TheStream::url().protocol() == "dvd")
|
|
{
|
|
action("toggle_dvd_menu")->plug(&popup);
|
|
popup.insertSeparator();
|
|
}
|
|
|
|
if( !((TDEToggleAction*)actionCollection()->action( "fullscreen" ))->isChecked() )
|
|
action( "reset_zoom" )->plug( &popup );
|
|
action( "capture_frame" )->plug( &popup );
|
|
popup.insertSeparator();
|
|
action( "video_settings" )->plug( &popup );
|
|
popup.insertSeparator();
|
|
action( "fullscreen" )->plug( &popup );
|
|
//show zoom information?
|
|
|
|
if( e->state() & TQt::MetaButton ) { //only on track end, or for special users
|
|
popup.insertSeparator();
|
|
action( "file_quit" )->plug( &popup );
|
|
}
|
|
|
|
if( popup.exec( e->globalPos() ) == 1 && state() == Engine::Playing )
|
|
// we check we are still paused as the menu generates a modal event loop
|
|
// so anything might have happened in the meantime.
|
|
pause();
|
|
}
|
|
|
|
bool
|
|
VideoWindow::event( TQEvent *e )
|
|
{
|
|
//TODO it would perhaps make things more responsive to
|
|
// deactivate mouse tracking and use the x11Event() function to transfer mouse move events?
|
|
// perhaps even better would be a x11 implementation
|
|
|
|
switch( e->type() )
|
|
{
|
|
case TQEvent::DragEnter:
|
|
case TQEvent::Drop:
|
|
//FIXME why don't we just ignore the event? It should propogate down
|
|
return TQApplication::sendEvent( tqApp->mainWidget(), e );
|
|
|
|
case TQEvent::Resize:
|
|
if( !TheStream::url().isEmpty() ) {
|
|
const TQSize defaultSize = TheStream::defaultVideoSize();
|
|
const bool notDefaultSize = width() != defaultSize.width() && height() != defaultSize.height();
|
|
|
|
Codeine::action( "reset_zoom" )->setEnabled( notDefaultSize );
|
|
|
|
//showOSD( i18n("Scale: %1%").arg( size()
|
|
}
|
|
break;
|
|
|
|
case TQEvent::Leave:
|
|
m_timer.stop();
|
|
break;
|
|
|
|
// Xlib.h sucks fucking balls!!!!11!!1!
|
|
#undef FocusOut
|
|
case TQEvent::FocusOut:
|
|
// if the user summons some dialog via a shortcut or whatever we need to ensure
|
|
// the mouse gets shown, because if it is modal, we won't get mouse events after
|
|
// it is shown! This works because we are always the focus widget.
|
|
// @see MainWindow::MainWindow where we setFocusProxy()
|
|
case TQEvent::Enter:
|
|
case TQEvent::MouseMove:
|
|
case TQEvent::MouseButtonPress:
|
|
unsetCursor();
|
|
if( hasFocus() )
|
|
// see above comment
|
|
m_timer.start( CURSOR_HIDE_TIMEOUT, true );
|
|
break;
|
|
|
|
case TQEvent::MouseButtonDblClick:
|
|
Codeine::action( "fullscreen" )->activate();
|
|
break;
|
|
|
|
default: ;
|
|
}
|
|
|
|
if( !m_xine )
|
|
return TQWidget::event( e );
|
|
|
|
switch( e->type() )
|
|
{
|
|
case TQEvent::Close:
|
|
stop();
|
|
return false;
|
|
|
|
case VideoWindow::ExposeEvent:
|
|
//see VideoWindow::x11Event()
|
|
|
|
return true;
|
|
|
|
// Xlib.h sucks fucking balls!!!!11!!1!
|
|
#undef KeyPress
|
|
case TQEvent::KeyPress: {
|
|
if( m_url.protocol() != "dvd" )
|
|
// let MainWindow handle this
|
|
return TQWidget::event( e );
|
|
|
|
//FIXME left and right keys don't work during DVDs
|
|
|
|
int keyCode = XINE_EVENT_INPUT_UP;
|
|
|
|
//#define XINE_EVENT_INPUT_UP 110
|
|
//#define XINE_EVENT_INPUT_DOWN 111
|
|
//#define XINE_EVENT_INPUT_LEFT 112
|
|
//#define XINE_EVENT_INPUT_RIGHT 113
|
|
//#define XINE_EVENT_INPUT_SELECT 114
|
|
|
|
switch( static_cast<TQKeyEvent*>(e)->key() ) {
|
|
case Key_Return:
|
|
case Key_Enter: keyCode++;
|
|
case Key_Right: keyCode++;
|
|
case Key_Left: keyCode++;
|
|
case Key_Down: keyCode++;
|
|
case Key_Up:
|
|
{
|
|
//this whole shebang is cheeky as xine doesn't
|
|
//guarentee the codes will stay the same
|
|
|
|
xine_event_t xineEvent;
|
|
|
|
xineEvent.type = keyCode;
|
|
xineEvent.data = nullptr;
|
|
xineEvent.data_length = 0;
|
|
|
|
xine_event_send( m_stream, &xineEvent );
|
|
|
|
return true;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
case TQEvent::MouseButtonPress:
|
|
|
|
#define mouseEvent static_cast<TQMouseEvent*>(e)
|
|
|
|
if( mouseEvent->button() != TQt::LeftButton )
|
|
return false;
|
|
|
|
mouseEvent->accept();
|
|
|
|
//FALL THROUGH
|
|
|
|
case TQEvent::MouseMove:
|
|
{
|
|
x11_rectangle_t x11Rect;
|
|
xine_event_t xineEvent;
|
|
xine_input_data_t xineInput;
|
|
|
|
x11Rect.x = mouseEvent->x();
|
|
x11Rect.y = mouseEvent->y();
|
|
x11Rect.w = 0;
|
|
x11Rect.h = 0;
|
|
|
|
xine_port_send_gui_data( m_videoPort, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&x11Rect );
|
|
|
|
xineEvent.type = e->type() == TQEvent::MouseMove ? XINE_EVENT_INPUT_MOUSE_MOVE : XINE_EVENT_INPUT_MOUSE_BUTTON;
|
|
xineEvent.data = &xineInput;
|
|
xineEvent.data_length = sizeof( xine_input_data_t );
|
|
xineInput.button = 1; //HACK e->type() == TQEvent::MouseMove ? 0 : 1;
|
|
xineInput.x = x11Rect.x;
|
|
xineInput.y = x11Rect.y;
|
|
xine_event_send( m_stream, &xineEvent );
|
|
|
|
return e->type() == TQEvent::MouseMove ? false : true;
|
|
|
|
#undef mouseEvent
|
|
}
|
|
|
|
case TQEvent::Wheel:
|
|
{
|
|
//TODO seek amount should depend on the length, basically seek at most say 30s, and at least 0.5s
|
|
//TODO this is replicated (somewhat) in MainWindow::keyPressEvent
|
|
|
|
int pos, time, length;
|
|
xine_get_pos_length( m_stream, &pos, &time, &length );
|
|
pos += int(std::log10( (double)length ) * static_cast<TQWheelEvent*>(e)->delta());
|
|
|
|
seek( pos > 0 ? (uint)pos : 0 );
|
|
|
|
return true;
|
|
}
|
|
|
|
default: ;
|
|
}
|
|
|
|
return TQWidget::event( e );
|
|
}
|
|
|
|
bool
|
|
VideoWindow::x11Event( XEvent *e )
|
|
{
|
|
if( m_stream && e->type == Expose && e->xexpose.count == 0 ) {
|
|
xine_port_send_gui_data(
|
|
m_videoPort,
|
|
XINE_GUI_SEND_EXPOSE_EVENT,
|
|
e );
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
VideoWindow::hideCursor()
|
|
{
|
|
setCursor( TQt::BlankCursor );
|
|
}
|
|
|
|
TQSize
|
|
VideoWindow::sizeHint() const //virtual
|
|
{
|
|
TQSize s = TheStream::profile()->readSizeEntry( "Preferred Size" );
|
|
|
|
if( !s.isValid() )
|
|
s = TheStream::defaultVideoSize();
|
|
|
|
if( s.isValid() && !s.isNull() )
|
|
return s;
|
|
|
|
return minimumSizeHint();
|
|
}
|
|
|
|
TQSize
|
|
VideoWindow::minimumSizeHint() const //virtual
|
|
{
|
|
const int x = fontMetrics().width( "x" ) * 4;
|
|
|
|
return TQSize( x * 12, x * 4 ); //FIXME
|
|
}
|
|
|
|
void
|
|
VideoWindow::resetZoom()
|
|
{
|
|
TheStream::profile()->deleteEntry( "Preferred Size" );
|
|
topLevelWidget()->adjustSize();
|
|
}
|
|
|
|
} //namespace Codeine
|