// (C) 2005 Max Howell (max.howell@methylblue.com) // See COPYING file for licensing information #define CODEINE_DEBUG_PREFIX "VideoWindow" #include //std::log10 #include #include //::makeStandardCaption #include #include #include #include #include #include #include #include #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, SIGNAL(timeout()), 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(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(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 = NULL; xineEvent.data_length = 0; xine_event_send( m_stream, &xineEvent ); return true; } default: return false; } } case TQEvent::MouseButtonPress: #define mouseEvent static_cast(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(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