00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <iostream>
00016 #include <cstdlib>
00017
00018
00019 #include <qdatetime.h>
00020 #include <qpainter.h>
00021 #include <qdir.h>
00022 #include <qtimer.h>
00023 #include <qprocess.h>
00024
00025
00026 #include "mythtv/mythcontext.h"
00027 #include "mythtv/mythdbcon.h"
00028
00029
00030 #include "zmplayer.h"
00031 #include "zmclient.h"
00032
00033 ZMPlayer::ZMPlayer(vector<Event *> *eventList, int *currentEvent, MythMainWindow *parent,
00034 const QString &window_name, const QString &theme_filename,
00035 const char *name)
00036 :MythThemedDialog(parent, window_name, theme_filename, name)
00037 {
00038 m_eventList = eventList;
00039 m_currentEvent = currentEvent;
00040
00041 wireUpTheme();
00042
00043 m_frameList = new vector<Frame*>;
00044 m_initalized = false;
00045 m_paused = false;
00046 m_useGL = (gContext->GetNumSetting("ZoneMinderUseOpenGL", 1) == 1);
00047 m_XvImage = NULL;
00048 m_XImage = NULL;
00049
00050 if (m_useGL)
00051 VERBOSE(VB_GENERAL, "MythZoneMinder: Using openGL for display");
00052 else
00053 VERBOSE(VB_GENERAL, "MythZoneMinder: Using Xv for display");
00054
00055 m_frameTimer = new QTimer(this);
00056 connect(m_frameTimer, SIGNAL(timeout()), this,
00057 SLOT(updateFrame()));
00058
00059 getEventInfo();
00060
00061 m_bFullScreen = false;
00062 setContext(1);
00063 }
00064
00065 ZMPlayer::~ZMPlayer()
00066 {
00067 stopPlayer();
00068
00069 m_frameTimer->deleteLater();
00070
00071 if (!m_frameList)
00072 delete m_frameList;
00073 }
00074
00075 void ZMPlayer::getEventInfo()
00076 {
00077 if (m_frameTimer)
00078 m_frameTimer->stop();
00079
00080 if (*m_currentEvent == -1)
00081 {
00082 stopPlayer();
00083
00084 if (m_noEventsText)
00085 m_noEventsText->show();
00086
00087 m_frameImage->SetImage(QString("mz_black.png"));
00088 m_frameImage->LoadImage();
00089
00090 m_eventText->SetText("");
00091 m_cameraText->SetText("");
00092 m_frameText->SetText("");
00093 m_dateText->SetText("");
00094
00095 return;
00096 }
00097 else
00098 {
00099 if (m_noEventsText)
00100 m_noEventsText->hide();
00101 }
00102
00103 Event *event = m_eventList->at(*m_currentEvent);
00104 if (!event)
00105 return;
00106
00107 m_curFrame = 0;
00108 m_lastFrame = 0;
00109
00110 m_eventText->SetText(QString(event->eventName + " (%1/%2)")
00111 .arg((*m_currentEvent) + 1)
00112 .arg(m_eventList->size()));
00113 m_cameraText->SetText(event->monitorName);
00114 m_dateText->SetText(event->startTime);
00115
00116
00117 m_frameList->clear();
00118 if (class ZMClient *zm = ZMClient::get())
00119 {
00120 zm->getFrameList(event->eventID, m_frameList);
00121 m_curFrame = 1;
00122 m_lastFrame = m_frameList->size();
00123 m_frameText->SetText(QString("%1/%2").arg(m_curFrame).arg(m_lastFrame));
00124 getFrame();
00125 }
00126 }
00127
00128 UITextType* ZMPlayer::getTextType(QString name)
00129 {
00130 UITextType* type = getUITextType(name);
00131
00132 if (!type)
00133 {
00134 cout << "ERROR: Failed to find '" << name << "' UI element in theme file\n"
00135 << "Bailing out!" << endl;
00136 exit(0);
00137 }
00138
00139 return type;
00140 }
00141
00142 void ZMPlayer::keyPressEvent(QKeyEvent *e)
00143 {
00144 if (!e) return;
00145
00146 bool handled = false;
00147 QStringList actions;
00148 gContext->GetMainWindow()->TranslateKeyPress("TV Playback", e, actions);
00149
00150 for (unsigned int i = 0; i < actions.size() && !handled; i++)
00151 {
00152 QString action = actions[i];
00153 handled = true;
00154
00155 if (action == "UP")
00156 {
00157 nextPrevWidgetFocus(false);
00158 }
00159 else if (action == "DOWN")
00160 {
00161 nextPrevWidgetFocus(true);
00162 }
00163 else if (action == "LEFT")
00164 {
00165 if (m_paused)
00166 {
00167 if (m_curFrame > 1)
00168 m_curFrame--;
00169 getFrame();
00170 }
00171 }
00172 else if (action == "RIGHT")
00173 {
00174 if (m_paused)
00175 {
00176 if (m_curFrame < m_lastFrame)
00177 m_curFrame++;
00178 getFrame();
00179 }
00180 }
00181 else if (action == "PAGEUP")
00182 {
00183 if (m_prevButton)
00184 m_prevButton->push();
00185 }
00186 else if (action == "PAGEDOWN")
00187 {
00188 if (m_nextButton)
00189 m_nextButton->push();
00190 }
00191 else if (action == "PAUSE")
00192 {
00193 if (m_playButton)
00194 m_playButton->push();
00195 }
00196 else if (action == "DELETE")
00197 {
00198 if (m_deleteButton)
00199 m_deleteButton->push();
00200 }
00201 else if (action == "TOGGLEASPECT")
00202 {
00203 if (m_eventList->size() == 0)
00204 return;
00205
00206 if (m_bFullScreen)
00207 {
00208 m_bFullScreen = false;
00209 setContext(1);
00210
00211 QPoint pos = m_frameImage->DisplayPos();
00212 QSize size = m_frameImage->GetSize(true);
00213 m_displayRect.setRect(pos.x(), pos.y(), size.width(), size.height());
00214 stopPlayer();
00215 initPlayer();
00216 displayFrame();
00217 }
00218 else
00219 {
00220 m_bFullScreen = true;
00221 setContext(2);
00222
00223 QPoint pos = m_frameFSImage->DisplayPos();
00224 QSize size = m_frameFSImage->GetSize(true);
00225 m_displayRect.setRect(pos.x(), pos.y(), size.width(), size.height());
00226 stopPlayer();
00227 initPlayer();
00228 displayFrame();
00229 }
00230
00231 if (!m_paused)
00232 m_frameTimer->start(1000 / 100);
00233
00234 updateForeground();
00235 }
00236 else if (action == "SELECT")
00237 {
00238 activateCurrent();
00239 }
00240 else
00241 handled = false;
00242 }
00243
00244 if (!handled)
00245 MythThemedDialog::keyPressEvent(e);
00246 }
00247
00248 void ZMPlayer::wireUpTheme()
00249 {
00250 m_frameImage = getUIImageType("frameimage");
00251
00252 if (m_frameImage)
00253 {
00254 QPoint pos = m_frameImage->DisplayPos();
00255 QSize size = m_frameImage->GetSize(true);
00256 m_displayRect.setRect(pos.x(), pos.y(), size.width(), size.height());
00257 }
00258
00259 m_frameFSImage = getUIImageType("framefsimage");
00260
00261 m_noEventsText = getTextType("noevents_text");
00262 m_eventText = getTextType("event_text");
00263 m_cameraText = getTextType("camera_text");
00264 m_frameText = getTextType("frame_text");
00265 m_dateText = getTextType("date_text");
00266
00267
00268 m_playButton = getUITextButtonType("play_button");
00269 if (m_playButton)
00270 {
00271 m_playButton->setText(tr("Pause"));
00272 connect(m_playButton, SIGNAL(pushed()), this, SLOT(playPressed()));
00273 }
00274
00275
00276 m_deleteButton = getUITextButtonType("delete_button");
00277 if (m_deleteButton)
00278 {
00279 m_deleteButton->setText(tr("Delete"));
00280 connect(m_deleteButton, SIGNAL(pushed()), this, SLOT(deletePressed()));
00281 }
00282
00283
00284 m_prevButton = getUITextButtonType("prev_button");
00285 if (m_prevButton)
00286 {
00287 m_prevButton->setText(tr("Previous"));
00288 connect(m_prevButton, SIGNAL(pushed()), this, SLOT(prevPressed()));
00289 }
00290
00291
00292 m_nextButton = getUITextButtonType("next_button");
00293 if (m_nextButton)
00294 {
00295 m_nextButton->setText(tr("Next"));
00296 connect(m_nextButton, SIGNAL(pushed()), this, SLOT(nextPressed()));
00297 }
00298
00299 buildFocusList();
00300 assignFirstFocus();
00301 }
00302
00303 void ZMPlayer::playPressed()
00304 {
00305 if (m_eventList->size() == 0)
00306 return;
00307
00308 if (m_paused)
00309 {
00310 m_frameTimer->start(1000/25);
00311 m_paused = false;
00312 if (m_playButton)
00313 m_playButton->setText(tr("Pause"));
00314 }
00315 else
00316 {
00317 m_frameTimer->stop();
00318 m_paused = true;
00319 if (m_playButton)
00320 m_playButton->setText(tr("Play"));
00321 }
00322 }
00323
00324 void ZMPlayer::deletePressed()
00325 {
00326 if (m_eventList->size() == 0 || *m_currentEvent > (int) m_eventList->size() - 1)
00327 return;
00328
00329 Event *event = m_eventList->at(*m_currentEvent);
00330 if (event)
00331 {
00332 m_frameTimer->stop();
00333
00334 if (class ZMClient *zm = ZMClient::get())
00335 zm->deleteEvent(event->eventID);
00336
00337 m_eventList->erase(m_eventList->begin() + *m_currentEvent);
00338 if (*m_currentEvent > (int)m_eventList->size() - 1)
00339 *m_currentEvent = m_eventList->size() - 1;
00340
00341 getEventInfo();
00342
00343 if (m_eventList->size() > 0)
00344 {
00345 m_frameTimer->start(1000 / 25);
00346 m_paused = false;
00347 }
00348 }
00349 }
00350
00351 void ZMPlayer::nextPressed()
00352 {
00353 if (m_eventList->size() == 0)
00354 return;
00355
00356 if (*m_currentEvent >= (int) m_eventList->size() - 1)
00357 return;
00358
00359 (*m_currentEvent)++;
00360
00361 getEventInfo();
00362
00363 if (m_paused)
00364 playPressed();
00365 }
00366
00367 void ZMPlayer::prevPressed()
00368 {
00369 if (m_eventList->size() == 0)
00370 return;
00371
00372 if (*m_currentEvent <= 0)
00373 return;
00374
00375 if (*m_currentEvent > (int) m_eventList->size())
00376 *m_currentEvent = m_eventList->size();
00377
00378 (*m_currentEvent)--;
00379
00380 getEventInfo();
00381
00382 if (m_paused)
00383 playPressed();
00384 }
00385
00386 void ZMPlayer::updateFrame(void)
00387 {
00388 if (!m_lastFrame)
00389 return;
00390
00391 m_frameTimer->stop();
00392
00393 m_curFrame++;
00394 if (m_curFrame > m_lastFrame)
00395 {
00396 m_paused = true;
00397 m_curFrame = 0;
00398 if (m_playButton)
00399 m_playButton->setText(tr("Play"));
00400 return;
00401 }
00402
00403 getFrame();
00404 }
00405
00406 void ZMPlayer::getFrame(void)
00407 {
00408 if (m_eventList->size() == 0)
00409 return;
00410
00411 Event *event = m_eventList->at(*m_currentEvent);
00412 if (event)
00413 {
00414 if (class ZMClient *zm = ZMClient::get())
00415 zm->getEventFrame(event->monitorID, event->eventID, m_curFrame, m_image);
00416
00417 displayFrame();
00418
00419 if (!m_paused)
00420 {
00421 if (m_curFrame < (int) m_frameList->size())
00422 {
00423 double delta = m_frameList->at(m_curFrame)->delta - m_frameList->at(m_curFrame - 1)->delta;
00424
00425
00426 if (delta < 0.1)
00427 delta = 0.1;
00428
00429 m_frameTimer->start((int) (1000 * delta));
00430 }
00431 else
00432 m_frameTimer->start(1000 / 100);
00433 }
00434 }
00435 }
00436
00437 #define TEXTURE_WIDTH 1024
00438 #define TEXTURE_HEIGHT 1024
00439
00440 bool ZMPlayer::initPlayer(void)
00441 {
00442 bool res;
00443
00444 if (m_useGL)
00445 res = initPlayerGl();
00446 else
00447 res = initPlayerXv();
00448
00449 return res;
00450 }
00451
00452 bool ZMPlayer::initPlayerGl(void)
00453 {
00454 m_initalized = false;
00455
00456 Window parent = winId();
00457
00458 m_dis = XOpenDisplay(gContext->GetX11Display());
00459 if (m_dis == NULL)
00460 {
00461 VERBOSE(VB_IMPORTANT, "ERROR: Unable to open display");
00462 return false;
00463 }
00464
00465 m_screenNum = DefaultScreen(m_dis);
00466
00467 if (!glXQueryExtension(m_dis, NULL, NULL))
00468 {
00469 VERBOSE(VB_IMPORTANT, "ERROR: X server has no OpenGL GLX extension");
00470 return false;
00471 }
00472
00473 int configuration[] = {GLX_DOUBLEBUFFER,GLX_RGBA,GLX_DEPTH_SIZE, 24, None};
00474 XVisualInfo *vi;
00475
00476 vi = glXChooseVisual(m_dis, m_screenNum, configuration);
00477 if (vi == NULL)
00478 {
00479 VERBOSE(VB_IMPORTANT, "ERROR: no appropriate RGB visual with depth buffer");
00480 return false;
00481 }
00482
00483 m_cx = glXCreateContext(m_dis, vi, NULL, GL_TRUE);
00484 if (m_cx == NULL)
00485 {
00486 VERBOSE(VB_IMPORTANT, "ERROR: couldn't create rendering context");
00487 return false;
00488 }
00489
00490 glDisable(GL_ALPHA_TEST);
00491 glDisable(GL_BLEND);
00492 glDisable(GL_DEPTH_TEST);
00493 glDisable(GL_DITHER);
00494 glDisable(GL_FOG);
00495 glDisable(GL_LIGHTING);
00496 glDisable(GL_LOGIC_OP);
00497 glDisable(GL_STENCIL_TEST);
00498 glDisable(GL_TEXTURE_1D);
00499 glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
00500 glPixelTransferi(GL_RED_SCALE, 1);
00501 glPixelTransferi(GL_RED_BIAS, 0);
00502 glPixelTransferi(GL_GREEN_SCALE, 1);
00503 glPixelTransferi(GL_GREEN_BIAS, 0);
00504 glPixelTransferi(GL_BLUE_SCALE, 1);
00505 glPixelTransferi(GL_BLUE_BIAS, 0);
00506 glPixelTransferi(GL_ALPHA_SCALE, 1);
00507 glPixelTransferi(GL_ALPHA_BIAS, 0);
00508
00509 m_win = XCreateSimpleWindow (m_dis, parent,
00510 m_displayRect.x(), m_displayRect.y(),
00511 m_displayRect.width(), m_displayRect.height(),
00512 2, 0, 0);
00513
00514 if (m_win == None)
00515 {
00516 VERBOSE(VB_IMPORTANT, "ERROR: Unable to create window\n");
00517 return false;
00518 }
00519
00520 XMapWindow (m_dis, m_win);
00521 XMoveWindow(m_dis, m_win, m_displayRect.x(), m_displayRect.y());
00522
00523 glXMakeCurrent(m_dis, m_win, m_cx);
00524
00525 glTexImage2D(GL_TEXTURE_2D, 0, 3, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGB,
00526 GL_UNSIGNED_BYTE, NULL);
00527 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00528 glEnable(GL_TEXTURE_2D);
00529 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
00530 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
00531 glShadeModel(GL_FLAT);
00532
00533
00534 XWindowAttributes win_attrib;
00535 XGetWindowAttributes(m_dis, m_win, &win_attrib);
00536
00537 m_initalized = true;
00538 return true;
00539 }
00540
00541 bool ZMPlayer::initPlayerXv(void)
00542 {
00543 bool useXV = true;
00544
00545 m_initalized = false;
00546
00547 Window parent = winId();
00548
00549 m_dis = XOpenDisplay(gContext->GetX11Display());
00550 if (m_dis == NULL)
00551 {
00552 VERBOSE(VB_IMPORTANT, "ERROR: Unable to open display\n");
00553 return false;
00554 }
00555
00556 m_screenNum = DefaultScreen(m_dis);
00557
00558 m_win = XCreateSimpleWindow (m_dis, parent,
00559 m_displayRect.x(), m_displayRect.y(),
00560 m_displayRect.width(), m_displayRect.height(),
00561 2, 0, 0);
00562
00563 if (m_win == None)
00564 {
00565 VERBOSE(VB_IMPORTANT, "ERROR: Unable to create window");
00566 return false;
00567 }
00568
00569 XMapWindow (m_dis, m_win);
00570 XMoveWindow(m_dis, m_win, m_displayRect.x(), m_displayRect.y());
00571
00572 m_XVport = -1;
00573
00574 m_gc = XCreateGC(m_dis, m_win, 0, NULL);
00575 if (m_gc ==NULL)
00576 {
00577 VERBOSE(VB_GENERAL, "ERROR: Unable to create gc");
00578 return false;
00579 }
00580
00581 m_rgba = (char *) malloc(m_displayRect.width() * m_displayRect.height() * 4);
00582
00583 m_haveXV = useXV;
00584
00585 if (useXV)
00586 {
00587 m_XVport = getXvPortId(m_dis);
00588 if (m_XVport == -1)
00589 {
00590 VERBOSE(VB_GENERAL, "WARNING: Couldn't find free Xv adaptor with RGB XvImage support");
00591 VERBOSE(VB_GENERAL, "Falling back to XImage - slow and ugly rescaling");
00592 m_haveXV = false;
00593 }
00594 else
00595 VERBOSE(VB_GENERAL, "MythZoneMinder: Using Xv for scaling");
00596 }
00597 else
00598 VERBOSE(VB_GENERAL, "MythZoneMinder: Forcing use of software scaling");
00599
00600 m_initalized = true;
00601 return true;
00602 }
00603
00604 int ZMPlayer::getXvPortId(Display *dpy)
00605 {
00606 int portNum, numImages;
00607 unsigned int i, j, k, numAdapt;
00608 XvImageFormatValues *formats;
00609 XvAdaptorInfo *info;
00610
00611 portNum = -1;
00612
00613 if (Success != XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &numAdapt, &info))
00614 {
00615 VERBOSE(VB_IMPORTANT, "No Xv adaptors found!");
00616 return -1;
00617 }
00618
00619 VERBOSE(VB_GENERAL, QString("Found %1 Xv adaptors").arg(numAdapt));
00620
00621 for (i = 0; i < numAdapt; i++)
00622 {
00623 if (info[i].type & XvImageMask)
00624 {
00625
00626 formats = XvListImageFormats(dpy, info[i].base_id, &numImages);
00627
00628 for (j = 0; j < (unsigned int) numImages; j++)
00629 {
00630 if (formats[j].id == RGB24)
00631 {
00632
00633 for (k = 0; k < info[i].num_ports; k++)
00634 {
00635
00636 if (Success == XvGrabPort(dpy, info[i].base_id + k, CurrentTime))
00637 {
00638 portNum = info[i].base_id + k;
00639 break;
00640 }
00641 }
00642 }
00643 if (portNum != -1)
00644 break;
00645 }
00646 XFree(formats);
00647 }
00648 if (portNum != -1)
00649 break;
00650 }
00651
00652 XvFreeAdaptorInfo(info);
00653 return portNum;
00654 }
00655
00656 void ZMPlayer::stopPlayer(void)
00657 {
00658 m_frameTimer->stop();
00659
00660 if (!m_initalized)
00661 return;
00662
00663 m_initalized = false;
00664
00665 if (m_useGL)
00666 glXDestroyContext(m_dis, m_cx);
00667 else
00668 {
00669 if (m_XVport != -1)
00670 XvUngrabPort(m_dis, m_XVport, CurrentTime);
00671 XFreeGC(m_dis, m_gc);
00672 }
00673
00674 XDestroyWindow(m_dis, m_win);
00675 XCloseDisplay(m_dis);
00676 }
00677
00678 void ZMPlayer::displayFrame(void)
00679 {
00680 if (m_useGL)
00681 displayFrameGl();
00682 else
00683 displayFrameXv();
00684 }
00685
00686 void ZMPlayer::displayFrameGl(void)
00687 {
00688 if (m_eventList->size() == 0)
00689 return;
00690
00691 if (!m_initalized)
00692 if (!initPlayer())
00693 return;
00694
00695 if (m_image.isNull())
00696 return;
00697
00698 glXMakeCurrent(m_dis, m_win, m_cx);
00699
00700 m_image = m_image.swapRGB();
00701 unsigned char *data = m_image.bits();
00702
00703 m_frameText->SetText(QString("%1/%2").arg(m_curFrame).arg(m_lastFrame));
00704
00705 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_image.width(), m_image.height(),
00706 GL_RGBA, GL_UNSIGNED_BYTE, data);
00707
00708 glViewport(0, 0, m_displayRect.width(), m_displayRect.height());
00709
00710 glLoadIdentity();
00711 glTranslatef(-1.0,1.0,0.0);
00712 glScalef((GLfloat)TEXTURE_WIDTH / (GLfloat) m_image.width(),
00713 -(GLfloat)TEXTURE_HEIGHT / (GLfloat) m_image.height(), 1);
00714 glBegin(GL_QUADS);
00715 glTexCoord2f(0.0,0.0); glVertex2f(0,0);
00716 glTexCoord2f(0.0,1.0); glVertex2f(0,2);
00717 glTexCoord2f(1.0,1.0); glVertex2f(2,2);
00718 glTexCoord2f(1.0,0.0); glVertex2f(2,0);
00719 glEnd();
00720 glXSwapBuffers(m_dis, m_win);
00721 }
00722
00723 void ZMPlayer::displayFrameXv(void)
00724 {
00725 if (m_eventList->size() == 0)
00726 return;
00727
00728 if (!m_initalized)
00729 if (!initPlayer())
00730 return;
00731
00732 if (m_image.isNull())
00733 return;
00734
00735 if (m_haveXV && !m_XvImage)
00736 {
00737 m_XvImage = XvCreateImage(m_dis, m_XVport, RGB24, m_rgba,
00738 m_image.width(), m_image.height());
00739 if (m_XvImage == NULL)
00740 {
00741 VERBOSE(VB_GENERAL, "WARNING: Unable to create XVImage");
00742 VERBOSE(VB_GENERAL, "Falling back to XImage - slow and ugly rescaling");
00743 m_haveXV = false;
00744 }
00745 }
00746
00747 if (!m_haveXV && !m_XImage)
00748 {
00749 m_XImage = XCreateImage(m_dis, XDefaultVisual(m_dis, m_screenNum), 24, ZPixmap, 0,
00750 m_rgba, m_displayRect.width(), m_displayRect.height(),
00751 32, 4 * m_displayRect.width());
00752 if (m_XImage == NULL)
00753 {
00754 VERBOSE(VB_IMPORTANT, "ERROR: Unable to create XImage");
00755 return;
00756 }
00757 }
00758
00759 if (m_haveXV)
00760 {
00761 unsigned char *data = m_image.bits();
00762 memcpy(m_rgba, data, m_image.width() * m_image.height() * 4);
00763
00764 m_frameText->SetText(QString("%1/%2").arg(m_curFrame).arg(m_lastFrame));
00765
00766 XvPutImage(m_dis, m_XVport, m_win, m_gc, m_XvImage, 0, 0,
00767 m_image.width(), m_image.height(),
00768 0, 0, m_displayRect.width(), m_displayRect.height());
00769 }
00770 else
00771 {
00772
00773 m_image = m_image.scale(m_displayRect.width(), m_displayRect.height());
00774 unsigned char *data = m_image.bits();
00775 memcpy(m_rgba, data, m_image.width() * m_image.height() * 4);
00776
00777 m_frameText->SetText(QString("%1/%2").arg(m_curFrame).arg(m_lastFrame));
00778
00779 XPutImage(m_dis, m_win, m_gc, m_XImage, 0, 0, 0, 0,
00780 m_displayRect.width(), m_displayRect.height());
00781 }
00782 }