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"