File indexing completed on 2024-04-21 15:38:31

0001 /* This file is part of the KMPlayer application
0002    Copyright (C) 2003 Koos Vriezen <koos.vriezen@xs4all.nl>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012     General Public License for more details.
0013 
0014    You should have received a copy of the GNU General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "config-kmplayer.h"
0021 #include <stdio.h>
0022 #include <string.h>
0023 #include <math.h>
0024 #include <libgen.h>
0025 #include <dcopclient.h>
0026 #include <qcstring.h>
0027 #include <qtimer.h>
0028 #include <qfile.h>
0029 #include <qurl.h>
0030 #include <qthread.h>
0031 #include <qmutex.h>
0032 #include <qdom.h>
0033 #include "kmplayer_backend.h"
0034 #include "kmplayer_callback_stub.h"
0035 #include "kmplayer_callback.h"
0036 #include "xineplayer.h"
0037 #include <X11/X.h>
0038 #include <X11/Xlib.h>
0039 #include <X11/Xutil.h>
0040 #include <X11/keysym.h>
0041 #include <X11/Xatom.h>
0042 #include <X11/Xutil.h>
0043 #include <X11/extensions/XShm.h>
0044 
0045 #include <xine.h>
0046 #include <xine/xineutils.h>
0047 
0048 #ifndef XShmGetEventBase
0049 extern int XShmGetEventBase(Display *);
0050 #endif
0051 
0052 #define MWM_HINTS_DECORATIONS   (1L << 1)
0053 #define PROP_MWM_HINTS_ELEMENTS 5
0054 typedef struct {
0055     uint32_t  flags;
0056     uint32_t  functions;
0057     uint32_t  decorations;
0058     int32_t   input_mode;
0059     uint32_t  status;
0060 } MWMHints;
0061 
0062 
0063 static KXinePlayer * xineapp;
0064 static KMPlayer::Callback_stub * callback;
0065 static QMutex mutex (true);
0066 
0067 static xine_t              *xine;
0068 static xine_stream_t       *stream;
0069 static xine_stream_t       *sub_stream;
0070 static xine_video_port_t   *vo_port;
0071 static xine_audio_port_t   *ao_port;
0072 static xine_post_t         *post_plugin;
0073 static xine_event_queue_t  *event_queue;
0074 static xine_cfg_entry_t     audio_vis_cfg_entry;
0075 static x11_visual_t         vis;
0076 static char                 configfile[2048];
0077 
0078 static Display             *display;
0079 static Window               wid;
0080 static bool                 window_created;
0081 static bool                 xine_verbose;
0082 static bool                 xine_vverbose;
0083 static bool                 wants_config;
0084 static bool                 audio_vis;
0085 static int                  screen;
0086 static int                  completion_event;
0087 static int                  repeat_count;
0088 static int                  xpos, ypos, width, height;
0089 static int                  movie_width, movie_height, movie_length, movie_pos;
0090 static int                  movie_brightness = 32767;
0091 static int                  movie_contrast = 32767;
0092 static int                  movie_hue = 32767;
0093 static int                  movie_saturation = 32767;
0094 static int                  movie_volume = 32767;
0095 static double               pixel_aspect;
0096 
0097 static int                  running = 0;
0098 static volatile int         firstframe = 0;
0099 static const int            event_finished = QEvent::User;
0100 static const int            event_progress = QEvent::User + 2;
0101 static const int            event_url = QEvent::User + 3;
0102 static const int            event_size = QEvent::User + 4;
0103 static const int            event_title = QEvent::User + 5;
0104 static const int            event_video = QEvent::User + 6;
0105 static QString mrl;
0106 static QString sub_mrl;
0107 static QString rec_mrl;
0108 static QString alang, slang;
0109 static QStringList alanglist, slanglist;
0110 
0111 static QString elmentry ("entry");
0112 static QString elmitem ("item");
0113 static QString attname ("name");
0114 static QString atttype ("type");
0115 static QString attdefault ("DEFAULT");
0116 static QString attvalue ("value");
0117 static QString attstart ("START");
0118 static QString attend ("end");
0119 static QString valrange ("range");
0120 static QString valnum ("num");
0121 static QString valbool ("bool");
0122 static QString valenum ("enum");
0123 static QString valstring ("string");
0124 
0125 extern "C" {
0126 
0127 static void dest_size_cb(void * /*data*/, int /*video_width*/, int /*video_height*/, double /*video_pixel_aspect*/,
0128         int *dest_width, int *dest_height, double *dest_pixel_aspect)  {
0129 
0130     *dest_width        = width;
0131     *dest_height       = height;
0132     *dest_pixel_aspect = pixel_aspect;
0133 }
0134 
0135 static void frame_output_cb(void * /*data*/, int /*video_width*/, int /*video_height*/,
0136         double /*video_pixel_aspect*/, int *dest_x, int *dest_y,
0137         int *dest_width, int *dest_height, 
0138         double *dest_pixel_aspect, int *win_x, int *win_y) {
0139     if (running && firstframe) {
0140         firstframe = 0;
0141         int pos;
0142         fprintf(stderr, "first frame\n");
0143         mutex.lock ();
0144         xine_get_pos_length (stream, 0, &pos, &movie_length);
0145         movie_width = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_WIDTH);
0146         movie_height = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
0147         mutex.unlock ();
0148         QApplication::postEvent (xineapp, new XineMovieParamEvent (movie_length, movie_width, movie_height, alanglist, slanglist, true));
0149         
0150     }
0151 
0152     *dest_x            = 0;
0153     *dest_y            = 0;
0154     *win_x             = xpos;
0155     *win_y             = ypos;
0156     *dest_width        = width;
0157     *dest_height       = height;
0158     *dest_pixel_aspect = pixel_aspect;
0159 }
0160 
0161 static void xine_config_cb (void * /*user_data*/, xine_cfg_entry_t * entry) {
0162     fprintf (stderr, "xine_config_cb %s\n", entry->enum_values[entry->num_value]);
0163     if (!stream)
0164         return;
0165     mutex.lock ();
0166     if (post_plugin) {
0167         xine_post_wire_audio_port (xine_get_audio_source (stream), ao_port);
0168         xine_post_dispose (xine, post_plugin);
0169         post_plugin = 0L;
0170     }
0171     if (audio_vis && strcmp (entry->enum_values[entry->num_value], "none")) {
0172         post_plugin = xine_post_init (xine, entry->enum_values[entry->num_value], 0, &ao_port, &vo_port);
0173         xine_post_wire (xine_get_audio_source (stream), (xine_post_in_t *) xine_post_input (post_plugin, (char *) "audio in"));
0174     }
0175     mutex.unlock ();
0176 }
0177 
0178 static void event_listener(void * /*user_data*/, const xine_event_t *event) {
0179     if (event->stream != stream)
0180         return; // not interested in sub_stream events
0181     switch(event->type) { 
0182         case XINE_EVENT_UI_PLAYBACK_FINISHED:
0183             fprintf (stderr, "XINE_EVENT_UI_PLAYBACK_FINISHED\n");
0184             if (repeat_count-- > 0)
0185                 xine_play (stream, 0, 0);
0186             else
0187                 QApplication::postEvent (xineapp, new QEvent ((QEvent::Type) event_finished));
0188             break;
0189         case XINE_EVENT_PROGRESS:
0190             QApplication::postEvent (xineapp, new XineProgressEvent (((xine_progress_data_t *) event->data)->percent));
0191             break;
0192         case XINE_EVENT_MRL_REFERENCE:
0193             fprintf(stderr, "XINE_EVENT_MRL_REFERENCE %s\n", 
0194             ((xine_mrl_reference_data_t*)event->data)->mrl);
0195             QApplication::postEvent (xineapp, new XineURLEvent (QString::fromLocal8Bit (((xine_mrl_reference_data_t*)event->data)->mrl)));
0196             break;
0197         case XINE_EVENT_FRAME_FORMAT_CHANGE:
0198             fprintf (stderr, "XINE_EVENT_FRAME_FORMAT_CHANGE\n");
0199             break;
0200         case XINE_EVENT_UI_SET_TITLE:
0201             {
0202                 xine_ui_data_t * data = (xine_ui_data_t *) event->data;
0203                 QApplication::postEvent(xineapp, new XineTitleEvent(data->str));
0204                 fprintf (stderr, "Set title event %s\n", data->str);
0205             }
0206             break;
0207         case XINE_EVENT_UI_CHANNELS_CHANGED: {
0208             fprintf (stderr, "Channel changed event %d\n", firstframe);
0209             mutex.lock ();
0210             int w = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_WIDTH);
0211             int h = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
0212             int pos, l, nr;
0213             xine_get_pos_length (stream, 0, &pos, &l);
0214             char * langstr = new char [66];
0215             alanglist.clear ();
0216             slanglist.clear ();
0217 
0218             nr =xine_get_stream_info(stream,XINE_STREAM_INFO_MAX_AUDIO_CHANNEL);
0219             // if nrch > 25) nrch = 25
0220             for (int i = 0; i < nr; ++i) {
0221                 if (!xine_get_audio_lang (stream, i, langstr))
0222                     continue;
0223                 QString ls = QString::fromLocal8Bit (langstr).stripWhiteSpace();
0224                 if (ls.isEmpty ())
0225                     continue;
0226                 if (!slang.isEmpty () && alang == ls)
0227                     xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, i);
0228                 alanglist.push_back (ls);
0229                 fprintf (stderr, "alang %s\n", langstr);
0230             }
0231             nr = xine_get_stream_info(stream, XINE_STREAM_INFO_MAX_SPU_CHANNEL);
0232             // if nrch > 25) nrch = 25
0233             for (int i = 0; i < nr; ++i) {
0234                 if (!xine_get_spu_lang (stream, i, langstr))
0235                     continue;
0236                 QString ls = QString::fromLocal8Bit (langstr).stripWhiteSpace();
0237                 if (ls.isEmpty ())
0238                     continue;
0239                 if (!slang.isEmpty () && slang == ls)
0240                     xine_set_param (stream, XINE_PARAM_SPU_CHANNEL, i);
0241                 slanglist.push_back (ls);
0242                 fprintf (stderr, "slang %s\n", langstr);
0243             }
0244             delete langstr;
0245             mutex.unlock ();
0246             movie_width = w;
0247             movie_height = h;
0248             movie_length = l;
0249             QApplication::postEvent (xineapp, new XineMovieParamEvent (l, w, h, alanglist, slanglist, firstframe));
0250             if (running && firstframe)
0251                 firstframe = 0;
0252             if (window_created && w > 0 && h > 0) {
0253                 XLockDisplay (display);
0254                 XResizeWindow (display, wid, movie_width, movie_height);
0255                 XFlush (display);
0256                 XUnlockDisplay (display);
0257             }
0258             break;
0259         }
0260         case XINE_EVENT_INPUT_MOUSE_MOVE:
0261             break;
0262         default:
0263             fprintf (stderr, "event_listener %d\n", event->type);
0264 
0265     }
0266 }
0267 
0268 } // extern "C"
0269 
0270 using namespace KMPlayer;
0271 
0272 Backend::Backend ()
0273     : DCOPObject (QCString ("Backend")) {
0274 }
0275 
0276 Backend::~Backend () {}
0277 
0278 void Backend::setURL (QString url) {
0279     mrl = url;
0280 }
0281 
0282 void Backend::setSubTitleURL (QString url) {
0283     sub_mrl = url;
0284 }
0285 
0286 void Backend::play (int repeat_count) {
0287     xineapp->play (repeat_count);
0288 }
0289 
0290 void Backend::stop () {
0291     QTimer::singleShot (0, xineapp, SLOT (stop ()));
0292 }
0293 
0294 void Backend::pause () {
0295     xineapp->pause ();
0296 }
0297 
0298 void Backend::seek (int pos, bool /*absolute*/) {
0299     xineapp->seek (pos);
0300 }
0301 
0302 void Backend::hue (int h, bool) {
0303     xineapp->hue (65535 * (h + 100) / 200);
0304 }
0305 
0306 void Backend::saturation (int s, bool) {
0307     xineapp->saturation (65535 * (s + 100) / 200);
0308 }
0309 
0310 void Backend::contrast (int c, bool) {
0311     xineapp->contrast (65535 * (c + 100) / 200);
0312 }
0313 
0314 void Backend::brightness (int b, bool) {
0315     xineapp->brightness (65535 * (b + 100) / 200);
0316 }
0317 
0318 void Backend::volume (int v, bool) {
0319     xineapp->volume (v);
0320 }
0321 
0322 void Backend::frequency (int) {
0323 }
0324 
0325 void Backend::setAudioLang (int id, QString al) {
0326     xineapp->setAudioLang (id, al);
0327 }
0328 
0329 void Backend::setSubtitle (int id, QString sl) {
0330     xineapp->setSubtitle (id, sl);
0331 }
0332 
0333 void Backend::quit () {
0334     delete callback;
0335     callback = 0L;
0336     if (running)
0337         stop ();
0338     else
0339         QTimer::singleShot (0, qApp, SLOT (quit ()));
0340 }
0341 
0342 bool updateConfigEntry (const QString & name, const QString & value) {
0343     fprintf (stderr, "%s=%s\n", name.ascii (), (const char *) value.local8Bit ());
0344     bool changed = false;
0345     xine_cfg_entry_t cfg_entry;
0346     if (!xine_config_lookup_entry (xine, name.ascii (), &cfg_entry))
0347         return false;
0348     if (cfg_entry.type == XINE_CONFIG_TYPE_STRING ||
0349             cfg_entry.type == XINE_CONFIG_TYPE_UNKNOWN) {
0350         changed = strcmp (cfg_entry.str_value, value.ascii ());
0351         cfg_entry.str_value = (char *) value.ascii ();
0352     } else {
0353         changed = cfg_entry.num_value != value.toInt ();
0354         cfg_entry.num_value = value.toInt ();
0355     }
0356     xine_config_update_entry (xine,  &cfg_entry);
0357     return changed;
0358 }
0359 
0360 void Backend::setConfig (QByteArray data) {
0361     QString err;
0362     int line, column;
0363     QDomDocument dom;
0364     if (dom.setContent (data, false, &err, &line, &column)) {
0365         if (dom.childNodes().length () == 1) {
0366             for (QDomNode node = dom.firstChild().firstChild();
0367                     !node.isNull ();
0368                     node = node.nextSibling ()) {
0369                 QDomNamedNodeMap attr = node.attributes ();
0370                 updateConfigEntry (attr.namedItem (attname).nodeValue (),
0371                                    attr.namedItem (attvalue).nodeValue ());
0372             }
0373             xine_config_save (xine, configfile);
0374         } else
0375             err = QString ("invalid data");
0376     }
0377     if (callback)
0378         callback->errorMessage (0, err);
0379 }
0380 
0381 bool Backend::isPlaying () {
0382     mutex.lock ();
0383     bool b = running &&
0384         (xine_get_status (stream) == XINE_STATUS_PLAY) &&
0385         (xine_get_param (stream, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE);
0386     mutex.unlock ();
0387     return b;
0388 }
0389 
0390 KXinePlayer::KXinePlayer (int _argc, char ** _argv)
0391   : QApplication (_argc, _argv, false) {
0392 }
0393 
0394 void KXinePlayer::init () {
0395     xpos    = 0;
0396     ypos    = 0;
0397     width   = 320;
0398     height  = 200;
0399 
0400     XLockDisplay(display);
0401     if (window_created)
0402         wid = XCreateSimpleWindow(display, XDefaultRootWindow(display),
0403                 xpos, ypos, width, height, 1, 0, 0);
0404     XSelectInput (display, wid,
0405                   (PointerMotionMask | ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask)); // | SubstructureNotifyMask));
0406     XWindowAttributes attr;
0407     XGetWindowAttributes(display, wid, &attr);
0408     width = attr.width;
0409     height = attr.height;
0410     if (XShmQueryExtension(display) == True)
0411         completion_event = XShmGetEventBase(display) + ShmCompletion;
0412     else
0413         completion_event = -1;
0414     if (window_created) {
0415         fprintf (stderr, "map %lu\n", wid);
0416         XMapRaised(display, wid);
0417         XSync(display, False);
0418     }
0419     //double d->res_h = 1.0 * DisplayWidth(display, screen) / DisplayWidthMM(display, screen);
0420     //double d->res_v = 1.0 * DisplayHeight(display, screen) / DisplayHeightMM(display, screen);
0421     XUnlockDisplay(display);
0422     vis.display           = display;
0423     vis.screen            = screen;
0424     vis.d                 = wid;
0425     vis.dest_size_cb      = dest_size_cb;
0426     vis.frame_output_cb   = frame_output_cb;
0427     vis.user_data         = NULL;
0428     //pixel_aspect          = d->res_v / d->res_h;
0429 
0430     //if(fabs(pixel_aspect - 1.0) < 0.01)
0431         pixel_aspect = 1.0;
0432 
0433     const char *const * pp = xine_list_post_plugins_typed (xine, XINE_POST_TYPE_AUDIO_VISUALIZATION);
0434     int i;
0435     for (i = 0; pp[i]; i++);
0436     const char ** options = new const char * [i+2];
0437     options[0] = "none";
0438     for (i = 0; pp[i]; i++)
0439         options[i+1] = pp[i];
0440     options[i+1] = 0L;
0441     xine_config_register_enum (xine, "audio.visualization", 0, (char ** ) options, 0L, 0L, 0, xine_config_cb, 0L);
0442     if (!callback)
0443         QTimer::singleShot (10, this, SLOT (play ()));
0444 }
0445 
0446 KXinePlayer::~KXinePlayer () {
0447     if (window_created) {
0448         XLockDisplay (display);
0449         fprintf (stderr, "unmap %lu\n", wid);
0450         XUnmapWindow (display,  wid);
0451         XDestroyWindow(display,  wid);
0452         XSync (display, False);
0453         XUnlockDisplay (display);
0454     }
0455     xineapp = 0L;
0456 }
0457 
0458 void getConfigEntries (QByteArray & buf) {
0459     xine_cfg_entry_t entry;
0460     QDomDocument doc;
0461     QDomElement root = doc.createElement (QString ("document"));
0462     for (int i = xine_config_get_first_entry (xine, &entry);
0463             i;
0464             i = xine_config_get_next_entry (xine, &entry)) {
0465         QDomElement elm = doc.createElement (elmentry);
0466         elm.setAttribute (attname, QString (entry.key));
0467         if (entry.type == XINE_CONFIG_TYPE_STRING || entry.type == XINE_CONFIG_TYPE_UNKNOWN) {
0468             elm.setAttribute (atttype, valstring);
0469             elm.setAttribute (attvalue, QString (entry.str_value));
0470         } else {
0471             elm.setAttribute (attdefault, QString::number (entry.num_default));
0472             elm.setAttribute (attvalue, QString::number (entry.num_value));
0473             switch (entry.type) {
0474                 case XINE_CONFIG_TYPE_RANGE:
0475                     elm.setAttribute (atttype, valrange);
0476                     elm.setAttribute (attstart, QString::number (entry.range_min));
0477                     elm.setAttribute (attend, QString::number (entry.range_max));
0478                     break;
0479                 case XINE_CONFIG_TYPE_ENUM:
0480                     elm.setAttribute (atttype, valenum);
0481                     for (int i = 0; entry.enum_values[i]; i++) {
0482                         QDomElement item = doc.createElement (elmitem);
0483                         item.setAttribute (attvalue, QString (entry.enum_values[i]));
0484                         elm.appendChild (item);
0485                     }
0486                     break;
0487                 case XINE_CONFIG_TYPE_NUM:
0488                     elm.setAttribute (atttype, valnum);
0489                     break;
0490                 case XINE_CONFIG_TYPE_BOOL:
0491                     elm.setAttribute (atttype, valbool);
0492                     break;
0493                 default:
0494                     fprintf (stderr, "unhandled config type: %d\n", entry.type);
0495             }
0496         }
0497         if (entry.help)
0498             elm.appendChild (doc.createTextNode (QString::fromUtf8 (entry.help)));
0499         root.appendChild (elm);
0500     }
0501     doc.appendChild (root);
0502     QCString exp = doc.toCString ();
0503     buf = exp;
0504     buf.resize (exp.length ()); // strip terminating \0
0505 }
0506 
0507 void KXinePlayer::play (int repeat) {
0508     fprintf (stderr, "play mrl: '%s'\n", (const char *) mrl.local8Bit ());
0509     mutex.lock ();
0510     repeat_count = repeat;
0511     if (running) {
0512         if (xine_get_status (stream) == XINE_STATUS_PLAY &&
0513             xine_get_param (stream, XINE_PARAM_SPEED) == XINE_SPEED_PAUSE)
0514             xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
0515         mutex.unlock ();
0516         return;
0517     }
0518     movie_pos = 0;
0519     movie_width = 0;
0520     movie_height = 0;
0521 
0522     if (mrl.startsWith ("cdda://"))
0523         mrl = QString ("cdda:/") + mrl.mid (7);
0524     stream = xine_stream_new (xine, ao_port, vo_port);
0525     event_queue = xine_event_new_queue (stream);
0526     xine_event_create_listener_thread (event_queue, event_listener, NULL);
0527     if (mrl == "cdda:/") {
0528         int nr;
0529         char ** mrls = xine_get_autoplay_mrls (xine, "CD", &nr);
0530         running = 1;
0531         for (int i = 0; i < nr; i++) {
0532             QString m (mrls[i]);
0533             QString title;
0534             if (xine_open (stream, mrls[i])) {
0535                 const char * t = xine_get_meta_info (stream, XINE_META_INFO_TITLE);
0536                 if (t && t[0])
0537                     title = QString::fromUtf8 (t);
0538                 xine_close (stream);
0539             }
0540             if (callback)
0541                 callback->subMrl (m, title);
0542             else
0543                 printf ("track %s\n", m.utf8 ().data ());
0544         }
0545         mutex.unlock ();
0546         finished ();
0547         return;
0548     }
0549 
0550     xine_gui_send_vo_data(stream, XINE_GUI_SEND_VIDEOWIN_VISIBLE, (void *) 1);
0551 
0552     running = 1;
0553     QString mrlsetup = mrl;
0554     if (!rec_mrl.isEmpty ()) {
0555         char * rm = strdup (rec_mrl.local8Bit ());
0556         char *bn = basename (rm);
0557         char *dn = dirname (rm);
0558         if (bn)
0559             updateConfigEntry (QString ("media.capture.save_dir"), QString::fromLocal8Bit (dn));
0560         mrlsetup += QString ("#save:") + QString::fromLocal8Bit (bn);
0561         free (rm);
0562     }
0563     if (!xine_open (stream, (const char *) mrlsetup.local8Bit ())) {
0564         fprintf(stderr, "Unable to open mrl '%s'\n", (const char *) mrl.local8Bit ());
0565         mutex.unlock ();
0566         finished ();
0567         return;
0568     }
0569     xine_set_param (stream, XINE_PARAM_VO_SATURATION, movie_saturation);
0570     xine_set_param (stream, XINE_PARAM_VO_BRIGHTNESS, movie_brightness);
0571     xine_set_param (stream, XINE_PARAM_VO_CONTRAST, movie_contrast);
0572     xine_set_param (stream, XINE_PARAM_VO_HUE, movie_hue);
0573 
0574     if (!sub_mrl.isEmpty ()) {
0575         fprintf(stderr, "Using subtitles from '%s'\n", (const char *) sub_mrl.local8Bit ());
0576         sub_stream = xine_stream_new (xine, NULL, vo_port);
0577         if (xine_open (sub_stream, (const char *) sub_mrl.local8Bit ())) {
0578             xine_stream_master_slave (stream, sub_stream,
0579                     XINE_MASTER_SLAVE_PLAY | XINE_MASTER_SLAVE_STOP);
0580         } else {
0581             fprintf(stderr, "Unable to open subtitles from '%s'\n", (const char *) sub_mrl.local8Bit ());
0582             xine_dispose (sub_stream);
0583             sub_stream = 0L;
0584         }
0585     } 
0586     if (!xine_play (stream, 0, 0)) {
0587         fprintf(stderr, "Unable to play mrl '%s'\n", (const char *) mrl.local8Bit ());
0588         mutex.unlock ();
0589         finished ();
0590         return;
0591     }
0592     audio_vis = false;
0593     if (xine_get_stream_info (stream, XINE_STREAM_INFO_HAS_VIDEO))
0594         QApplication::postEvent(xineapp, new QEvent((QEvent::Type)event_video));
0595     else
0596         audio_vis = xine_config_lookup_entry
0597             (xine, "audio.visualization", &audio_vis_cfg_entry);
0598     mutex.unlock ();
0599     if (audio_vis)
0600         xine_config_cb (0L, &audio_vis_cfg_entry);
0601     if (callback)
0602         firstframe = 1;
0603 }
0604 
0605 void KXinePlayer::stop () {
0606     if (!running) return;
0607     fprintf(stderr, "stop\n");
0608     mutex.lock ();
0609     repeat_count = 0;
0610     if (sub_stream)
0611         xine_stop (sub_stream);
0612     xine_stop (stream);
0613     mutex.unlock ();
0614     QTimer::singleShot (10, this, SLOT (postFinished ()));
0615 }
0616 
0617 void KXinePlayer::postFinished () {
0618     QApplication::postEvent (xineapp, new QEvent ((QEvent::Type) event_finished));
0619 }
0620 
0621 void KXinePlayer::pause () {
0622     if (!running) return;
0623     mutex.lock ();
0624     if (xine_get_status (stream) == XINE_STATUS_PLAY) {
0625         if (xine_get_param (stream, XINE_PARAM_SPEED) == XINE_SPEED_PAUSE)
0626             xine_set_param (stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
0627         else
0628             xine_set_param (stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE);
0629     }
0630     mutex.unlock ();
0631 }
0632 
0633 void KXinePlayer::finished () {
0634     QTimer::singleShot (10, this, SLOT (stop ()));
0635 }
0636 
0637 void KXinePlayer::setAudioLang (int id, const QString & al) {
0638     alang = al;
0639     mutex.lock ();
0640     if (xine_get_status (stream) == XINE_STATUS_PLAY)
0641         xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, id);
0642     mutex.unlock ();
0643 }
0644 
0645 void KXinePlayer::setSubtitle (int id, const QString & sl) {
0646     slang = sl;
0647     mutex.lock ();
0648     if (xine_get_status (stream) == XINE_STATUS_PLAY)
0649         xine_set_param (stream, XINE_PARAM_SPU_CHANNEL, id);
0650     mutex.unlock ();
0651 }
0652 
0653 void KXinePlayer::updatePosition () {
0654     if (!running || !callback) return;
0655     int pos;
0656     mutex.lock ();
0657     xine_get_pos_length (stream, 0, &pos, &movie_length);
0658     mutex.unlock ();
0659     if (movie_pos != pos) {
0660         movie_pos = pos;
0661         callback->moviePosition (pos/100);
0662     }
0663     QTimer::singleShot (500, this, SLOT (updatePosition ()));
0664 }
0665 
0666 void KXinePlayer::saturation (int val) {
0667     movie_saturation = val;
0668     if (running) {
0669         mutex.lock ();
0670         xine_set_param (stream, XINE_PARAM_VO_SATURATION, val);
0671         mutex.unlock ();
0672     }
0673 }
0674 
0675 void KXinePlayer::hue (int val) {
0676     movie_hue = val;
0677     if (running) {
0678         mutex.lock ();
0679         xine_set_param (stream, XINE_PARAM_VO_HUE, val);
0680         mutex.unlock ();
0681     }
0682 }
0683 
0684 void KXinePlayer::contrast (int val) {
0685     movie_contrast = val;
0686     if (running) {
0687         mutex.lock ();
0688         xine_set_param (stream, XINE_PARAM_VO_CONTRAST, val);
0689         mutex.unlock ();
0690     }
0691 }
0692 
0693 void KXinePlayer::brightness (int val) {
0694     movie_brightness = val;
0695     if (running) {
0696         mutex.lock ();
0697         xine_set_param (stream, XINE_PARAM_VO_BRIGHTNESS, val);
0698         mutex.unlock ();
0699     }
0700 }
0701 
0702 void KXinePlayer::volume (int val) {
0703     movie_volume = val;
0704     if (running) {
0705         mutex.lock ();
0706         xine_set_param( stream, XINE_PARAM_AUDIO_VOLUME, val);
0707         mutex.unlock ();
0708     }
0709 }
0710 
0711 void KXinePlayer::seek (int val) {
0712     if (running) {
0713         fprintf(stderr, "seek %d\n", val);
0714         mutex.lock ();
0715         if (!xine_play (stream, 0, val * 100)) {
0716             fprintf(stderr, "Unable to seek to %d :-(\n", val);
0717         }
0718         mutex.unlock ();
0719     }
0720 }
0721 
0722 bool KXinePlayer::event (QEvent * e) {
0723     switch (e->type()) {
0724         case event_finished: {
0725             fprintf (stderr, "event_finished\n");
0726             if (audio_vis) {
0727                 audio_vis_cfg_entry.num_value = 0;
0728                 xine_config_cb (0L, &audio_vis_cfg_entry);
0729             }
0730             mutex.lock ();
0731             running = 0;
0732             firstframe = 0;
0733             if (sub_stream) {
0734                 xine_dispose (sub_stream);
0735                 sub_stream = 0L;
0736             }
0737             if (stream) {
0738                 xine_event_dispose_queue (event_queue);
0739                 xine_dispose (stream);
0740                 stream = 0L;
0741             }
0742             mutex.unlock ();
0743             //XLockDisplay (display);
0744             //XClearWindow (display, wid);
0745             //XUnlockDisplay (display);
0746             if (callback)
0747                 callback->finished ();
0748             else
0749                 QTimer::singleShot (0, this, SLOT (quit ()));
0750             break;
0751         }
0752         case event_size: {
0753             if (callback) {
0754                 XineMovieParamEvent * se = static_cast <XineMovieParamEvent *> (e);
0755                 if (se->length < 0) se->length = 0;
0756                 callback->movieParams (se->length/100, se->width, se->height, se->height ? 1.0*se->width/se->height : 1.0, se->alang, se->slang);
0757                 if (se->first_frame) {
0758                     callback->playing ();
0759                     QTimer::singleShot (500, this, SLOT (updatePosition ()));
0760                 }
0761             }
0762             break;
0763         }
0764         case event_progress: {
0765             XineProgressEvent * pe = static_cast <XineProgressEvent *> (e);                
0766             if (callback)
0767                 callback->loadingProgress (pe->progress);
0768             break;
0769         }
0770         case event_url: {
0771             XineURLEvent * ue = static_cast <XineURLEvent *> (e);                
0772             if (callback)
0773                 callback->subMrl (ue->url, QString ());
0774             break;
0775         }
0776         case event_title: {
0777             XineTitleEvent * ue = static_cast <XineTitleEvent *> (e);                
0778             if (callback)
0779                 callback->statusMessage ((int) KMPlayer::Callback::stat_newtitle, ue->title);
0780             break;
0781         }
0782         case event_video:
0783             if (callback)
0784                 callback->statusMessage ((int) KMPlayer::Callback::stat_hasvideo, QString ());
0785             break;
0786         default:
0787             return false;
0788     }
0789     return true;
0790 }
0791 
0792 void KXinePlayer::saveState (QSessionManager & sm) {
0793     if (callback)
0794         sm.setRestartHint (QSessionManager::RestartNever);
0795 }
0796 
0797 XineMovieParamEvent::XineMovieParamEvent(int l, int w, int h, const QStringList & a, const QStringList & s, bool ff)
0798   : QEvent ((QEvent::Type) event_size),
0799     length (l), width (w), height (h), alang (a), slang (s) , first_frame (ff)
0800 {}
0801 
0802 XineURLEvent::XineURLEvent (const QString & u)
0803   : QEvent ((QEvent::Type) event_url), url (u) 
0804 {}
0805 
0806 XineTitleEvent::XineTitleEvent (const char * t)
0807   : QEvent ((QEvent::Type) event_title), title (QString::fromUtf8 (t)) 
0808 {
0809     QUrl::decode (title);
0810 }
0811 
0812 XineProgressEvent::XineProgressEvent (const int p)
0813   : QEvent ((QEvent::Type) event_progress), progress (p) 
0814 {}
0815 
0816 //static bool translateCoordinates (int wx, int wy, int mx, int my) {
0817 //    movie_width
0818 class XEventThread : public QThread {
0819 protected:
0820     void run () {
0821         Time prev_click_time = 0;
0822         int prev_click_x = 0;
0823         int prev_click_y = 0;
0824         while (true) {
0825             XEvent   xevent;
0826             XNextEvent(display, &xevent);
0827             switch(xevent.type) {
0828                 case ClientMessage:
0829                     if (xevent.xclient.format == 8 &&
0830                             !strncmp(xevent.xclient.data.b, "quit_now", 8)) {
0831                         fprintf(stderr, "request quit\n");
0832                         return;
0833                     }
0834                     break;
0835                 case KeyPress:
0836                     {
0837                         XKeyEvent  kevent;
0838                         KeySym     ksym;
0839                         char       kbuf[256];
0840                         int        len;
0841 
0842                         kevent = xevent.xkey;
0843 
0844                         XLockDisplay(display);
0845                         len = XLookupString(&kevent, kbuf, sizeof(kbuf), &ksym, NULL);
0846                         XUnlockDisplay(display);
0847                         fprintf(stderr, "keypressed 0x%x 0x%x\n", kevent.keycode, ksym);
0848 
0849                         switch (ksym) {
0850 
0851                             case XK_q:
0852                             case XK_Q:
0853                                 xineapp->lock ();
0854                                 xineapp->stop ();
0855                                 xineapp->unlock ();
0856                                 break;
0857 
0858                             case XK_p: // previous
0859                                 mutex.lock ();
0860                                 if (stream) {
0861                                     xine_event_t xine_event =  { 
0862                                         XINE_EVENT_INPUT_PREVIOUS,
0863                                         stream, 0L, 0, { 0, 0 }
0864                                     };
0865                                     xine_event_send (stream, &xine_event);
0866                                 } 
0867                                 mutex.unlock ();
0868                                 break;
0869 
0870                             case XK_n: // next
0871                                 mutex.lock ();
0872                                 if (stream) {
0873                                     xine_event_t xine_event =  { 
0874                                         XINE_EVENT_INPUT_NEXT,
0875                                         stream, 0L, 0, { 0, 0 }
0876                                     };
0877                                     xine_event_send (stream, &xine_event);
0878                                 } 
0879                                 mutex.unlock ();
0880                                 break;
0881 
0882                             case XK_u: // up menu
0883                                 mutex.lock ();
0884                                 if (stream) {
0885                                     xine_event_t xine_event =  { 
0886                                         XINE_EVENT_INPUT_MENU1,
0887                                         stream, 0L, 0, { 0, 0 }
0888                                     };
0889                                     xine_event_send (stream, &xine_event);
0890                                 } 
0891                                 mutex.unlock ();
0892                                 break;
0893 
0894                             case XK_r: // root menu
0895                                 mutex.lock ();
0896                                 if (stream) {
0897                                     xine_event_t xine_event =  { 
0898                                         XINE_EVENT_INPUT_MENU3,
0899                                         stream, 0L, 0, { 0, 0 }
0900                                     };
0901                                     xine_event_send (stream, &xine_event);
0902                                 } 
0903                                 mutex.unlock ();
0904                                 break;
0905 
0906                             case XK_Up:
0907                                 xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME,
0908                                         (xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) + 1));
0909                                 break;
0910 
0911                             case XK_Down:
0912                                 xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME,
0913                                         (xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) - 1));
0914                                 break;
0915 
0916                             case XK_plus:
0917                                 xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, 
0918                                         (xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) + 1));
0919                                 break;
0920 
0921                             case XK_minus:
0922                                 xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, 
0923                                         (xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) - 1));
0924                                 break;
0925 
0926                             case XK_space:
0927                                 if(xine_get_param(stream, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE)
0928                                     xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE);
0929                                 else
0930                                     xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
0931                                 break;
0932 
0933                         }
0934                     }
0935                     break;
0936 
0937                 case Expose:
0938                     if(xevent.xexpose.count != 0 || !stream || xevent.xexpose.window != wid)
0939                         break;
0940                     mutex.lock ();
0941                     xine_gui_send_vo_data(stream, XINE_GUI_SEND_EXPOSE_EVENT, &xevent);
0942                     mutex.unlock ();
0943                     break;
0944 
0945                 case ConfigureNotify:
0946                     {
0947                         Window           tmp_win;
0948 
0949                         width  = xevent.xconfigure.width;
0950                         height = xevent.xconfigure.height;
0951                         if((xevent.xconfigure.x == 0) && (xevent.xconfigure.y == 0)) {
0952                             XLockDisplay(display);
0953                             XTranslateCoordinates(display, xevent.xconfigure.window,
0954                                     DefaultRootWindow(xevent.xconfigure.display),
0955                                     0, 0, &xpos, &ypos, &tmp_win);
0956                             XUnlockDisplay(display);
0957                         }
0958                         else {
0959                             xpos = xevent.xconfigure.x;
0960                             ypos = xevent.xconfigure.y;
0961                         }
0962                     }
0963 
0964                     break;
0965                 case MotionNotify:
0966                     if (stream) {
0967                         XMotionEvent *mev = (XMotionEvent *) &xevent;
0968                         x11_rectangle_t rect = { mev->x, mev->y, 0, 0 };
0969                         if (xine_gui_send_vo_data (stream, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*) &rect) == -1)
0970                             break;
0971                         xine_input_data_t data;
0972                         data.x = rect.x;
0973                         data.y = rect.y;
0974                         data.button = 0;
0975                         xine_event_t xine_event =  { 
0976                                 XINE_EVENT_INPUT_MOUSE_MOVE,
0977                                 stream, &data, sizeof (xine_input_data_t),
0978                                 { 0 , 0 }
0979                         };
0980                         mutex.lock ();
0981                         xine_event_send (stream, &xine_event);
0982                         mutex.unlock ();
0983                     }
0984                     break;
0985                 case ButtonPress: {
0986                     XButtonEvent *bev = (XButtonEvent *) &xevent;
0987                     int dx = prev_click_x - bev->x;
0988                     int dy = prev_click_y - bev->y;
0989                     if (bev->time - prev_click_time < 400 &&
0990                             (dx * dx + dy * dy) < 25) {
0991                         xineapp->lock ();
0992                         if (callback)
0993                             callback->toggleFullScreen ();
0994                         xineapp->unlock ();
0995                     }
0996                     prev_click_time = bev->time;
0997                     prev_click_x = bev->x;
0998                     prev_click_y = bev->y;
0999                     if (stream) {
1000                         fprintf(stderr, "ButtonPress\n");
1001                         XButtonEvent *bev = (XButtonEvent *) &xevent;
1002                         x11_rectangle_t rect = { bev->x, bev->y, 0, 0 };
1003                         if (xine_gui_send_vo_data (stream, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*) &rect) == -1)
1004                             break;
1005                         xine_input_data_t data;
1006                         data.x = rect.x;
1007                         data.y = rect.y;
1008                         data.button = 1;
1009                         xine_event_t xine_event =  { 
1010                                 XINE_EVENT_INPUT_MOUSE_BUTTON,
1011                                 stream, &data, sizeof (xine_input_data_t),
1012                                 { 0, 0 }
1013                         };
1014                         mutex.lock ();
1015                         xine_event_send (stream, &xine_event);
1016                         mutex.unlock ();
1017                     }
1018                     break;
1019                 }
1020                 case NoExpose:
1021                     //fprintf (stderr, "NoExpose %lu\n", xevent.xnoexpose.drawable);
1022                     break;
1023                 case CreateNotify:
1024                     fprintf (stderr, "CreateNotify: %lu %lu %d,%d %dx%d\n",
1025                             xevent.xcreatewindow.window, xevent.xcreatewindow.parent,
1026                             xevent.xcreatewindow.x, xevent.xcreatewindow.y,
1027                             xevent.xcreatewindow.width, xevent.xcreatewindow.height);
1028                     break;
1029                 case DestroyNotify:
1030                     fprintf (stderr, "DestroyNotify: %lu\n", xevent.xdestroywindow.window);
1031                     break;
1032                 default:
1033                     if (xevent.type < LASTEvent)
1034                         fprintf (stderr, "event %d\n", xevent.type);
1035             }
1036 
1037             if(xevent.type == completion_event && stream)
1038                 xine_gui_send_vo_data(stream, XINE_GUI_SEND_COMPLETION_EVENT, &xevent);
1039         }
1040     }
1041 };
1042 
1043 int main(int argc, char **argv) {
1044     const char *dvd_device = 0L;
1045     const char *vcd_device = 0L;
1046     const char *grab_device = 0L;
1047     if (!XInitThreads ()) {
1048         fprintf (stderr, "XInitThreads () failed\n");
1049         return 1;
1050     }
1051     display = XOpenDisplay(NULL);
1052     screen  = XDefaultScreen(display);
1053 
1054     snprintf(configfile, sizeof (configfile), "%s%s", xine_get_homedir(), "/.xine/config2");
1055     xineapp = new KXinePlayer (argc, argv);
1056     window_created = true;
1057     QString vo_driver ("auto");
1058     QString ao_driver ("auto");
1059     for (int i = 1; i < argc; i++) {
1060         if (!strcmp (argv [i], "-vo") && ++i < argc) {
1061             vo_driver = argv [i];
1062         } else if (!strcmp (argv [i], "-ao") && ++i < argc) {
1063             ao_driver = argv [i];
1064         } else if (!strcmp (argv [i], "-dvd-device") && ++i < argc) {
1065             dvd_device = argv [i];
1066         } else if (!strcmp (argv [i], "-vcd-device") && ++i < argc) {
1067             vcd_device = argv [i];
1068         } else if (!strcmp (argv [i], "-vd") && ++i < argc) {
1069             grab_device = argv [i];
1070         } else if ((!strcmp (argv [i], "-wid") ||
1071                     !strcmp (argv [i], "-window-id")) && ++i < argc) {
1072             wid = atol (argv [i]);
1073             window_created = false;
1074         } else if (!strcmp (argv [i], "-root")) {
1075             wid =  XDefaultRootWindow (display);
1076             window_created = false;
1077         } else if (!strcmp (argv [i], "-window")) {
1078             ;
1079         } else if (!strcmp (argv [i], "-sub") && ++i < argc) {
1080             sub_mrl = QString (argv [i]);
1081         } else if (!strcmp (argv [i], "-lang") && ++i < argc) {
1082             slang = alang = QString (argv [i]);
1083         } else if (!strcmp (argv [i], "-v")) {
1084             xine_verbose = true;
1085         } else if (!strcmp (argv [i], "-vv")) {
1086             xine_verbose = xine_vverbose = true;
1087         } else if (!strcmp (argv [i], "-c")) {
1088             wants_config = true;
1089         } else if (!strcmp (argv [i], "-f") && ++i < argc) {
1090             strncpy (configfile, argv [i], sizeof (configfile));
1091             configfile[sizeof (configfile) - 1] = 0;
1092         } else if (!strcmp (argv [i], "-cb") && ++i < argc) {
1093             QString str = argv [i];
1094             int pos = str.find ('/');
1095             if (pos > -1) {
1096                 fprintf (stderr, "callback is %s %s\n", str.left (pos).ascii (), str.mid (pos + 1).ascii ());
1097                 callback = new KMPlayer::Callback_stub 
1098                     (str.left (pos).ascii (), str.mid (pos + 1).ascii ());
1099             }
1100         } else if (!strcmp (argv [i], "-rec") && i < argc - 1) {
1101             rec_mrl = QString::fromLocal8Bit (argv [++i]);
1102         } else if (!strcmp (argv [i], "-loop") && i < argc - 1) {
1103             repeat_count = atol (argv [++i]);
1104         } else {
1105             if (mrl.startsWith ("-session")) {
1106                 delete xineapp;
1107                 return 1;
1108             }
1109             mrl = QString::fromLocal8Bit (argv [i]);
1110         }
1111     }
1112     bool config_changed = !QFile (configfile).exists ();
1113 
1114     if (!callback && mrl.isEmpty ()) {
1115         fprintf (stderr, "usage: %s [-vo (xv|xshm)] [-ao (arts|esd|..)] "
1116                 "[-f <xine config file>] [-dvd-device <device>] "
1117                 "[-vcd-device <device>] [-vd <video device>] "
1118                 "[-wid <X11 Window>|-window-id <X11 Window>|-root] "
1119                 "[-sub <subtitle url>] [-lang <lang>] [(-v|-vv)] "
1120                 "[-cb <DCOP callback name> [-c]] "
1121                 "[-loop <repeat>] [<url>]\n", argv[0]);
1122         delete xineapp;
1123         return 1;
1124     }
1125 
1126     XEventThread * eventThread = new XEventThread;
1127     eventThread->start ();
1128 
1129     DCOPClient dcopclient;
1130     dcopclient.registerAs ("kxineplayer");
1131     Backend player;
1132 
1133     xine = xine_new();
1134     if (xine_verbose)
1135         xine_engine_set_param (xine, XINE_ENGINE_PARAM_VERBOSITY, xine_vverbose ? XINE_VERBOSITY_DEBUG : XINE_VERBOSITY_LOG);
1136     xine_config_load(xine, configfile);
1137     xine_init(xine);
1138 
1139     xineapp->init ();
1140 
1141     if (dvd_device)
1142         config_changed |= updateConfigEntry (QString ("input.dvd_device"), QString (dvd_device));
1143     if (vcd_device)
1144         config_changed |= updateConfigEntry (QString ("input.vcd_device"), QString (vcd_device));
1145     if (grab_device)
1146         config_changed |= updateConfigEntry (QString ("media.video4linux.video_device"), QString (grab_device));
1147 
1148     if (config_changed)
1149         xine_config_save (xine, configfile);
1150 
1151     QStringList vos = QStringList::split (',', vo_driver);
1152     for (int i = 0; i < vos.size (); i++) {
1153         if (vos[i] == "x11")
1154             vos[i] = "xshm";
1155         else if (vos[i] == "gl")
1156             vos[i] = "opengl";
1157         fprintf (stderr, "trying video driver %s ..\n", vos[i].ascii ());
1158         vo_port = xine_open_video_driver(xine, vos[i].ascii (),
1159                 XINE_VISUAL_TYPE_X11, (void *) &vis);
1160         if (vo_port)
1161             break;
1162     }
1163     if (!vo_port)
1164         fprintf (stderr, "no video driver found\n");
1165     QStringList aos = QStringList::split (',', ao_driver);
1166     for (int i = 0; i < aos.size (); i++) {
1167         fprintf (stderr, "trying audio driver %s ..\n", aos[i].ascii ());
1168         ao_port = xine_open_audio_driver (xine, aos[i].ascii (), NULL);
1169         if (ao_port)
1170             break;
1171     }
1172     if (!ao_port)
1173         fprintf (stderr, "audio driver initialisation failed\n");
1174     stream = xine_stream_new (xine, ao_port, vo_port);
1175 
1176     QByteArray buf;
1177     if (wants_config) {
1178         /* TODO? Opening the output drivers in front, will add more config
1179                  settings. Unfortunately, that also adds a second in startup..
1180         const char *const * aops = xine_list_audio_output_plugins (xine);
1181         for (const char *const* aop = aops; *aop; aop++) {
1182             xine_audio_port_t * ap = xine_open_audio_driver (xine, *aop, 0L);
1183             xine_close_audio_driver (xine, ap);
1184             fprintf (stderr, "audio output: %s\n", *aop);
1185         }
1186         const char *const * vops = xine_list_video_output_plugins (xine);
1187         for (const char *const* vop = vops; *vop; vop++) {
1188             xine_video_port_t * vp = xine_open_video_driver (xine, *vop, XINE_VISUAL_TYPE_NONE, 0L);
1189             xine_close_video_driver (xine, vp);
1190             fprintf (stderr, "vidio output: %s\n", *vop);
1191         }*/
1192         getConfigEntries (buf);
1193     }
1194     if (callback)
1195         callback->started (dcopclient.appId (), buf);
1196     else
1197         ;//printf ("%s\n", QString (buf).ascii ());
1198     xineapp->exec ();
1199 
1200     if (sub_stream)
1201         xine_dispose (sub_stream);
1202     if (stream) {
1203         xine_event_dispose_queue (event_queue);
1204         xine_dispose (stream);
1205     }
1206     if (ao_port)
1207         xine_close_audio_driver (xine, ao_port);
1208     if (vo_port)
1209         xine_close_video_driver (xine, vo_port);
1210     XLockDisplay(display);
1211     XEvent ev;
1212     ev.xclient.type = ClientMessage;
1213     ev.xclient.serial = 0;
1214     ev.xclient.send_event = true;
1215     ev.xclient.display = display;
1216     ev.xclient.window = wid;
1217     ev.xclient.message_type = XInternAtom (display, "XINE", false);
1218     ev.xclient.format = 8;
1219     strcpy(ev.xclient.data.b, "quit_now");
1220     XSendEvent (display, wid, false, StructureNotifyMask, &ev);
1221     XFlush (display);
1222     XUnlockDisplay(display);
1223     eventThread->wait (500);
1224     delete eventThread;
1225 
1226     xineapp->stop ();
1227     delete xineapp;
1228 
1229     xine_exit (xine);
1230 
1231     fprintf (stderr, "closing display\n");
1232     XCloseDisplay (display);
1233     fprintf (stderr, "done\n");
1234     return 0;
1235 }
1236 
1237 #include "xineplayer.moc"