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"