File indexing completed on 2024-05-05 04:55:51

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