00001
00002 #include <map>
00003 #include <vector>
00004
00005 #include "config.h"
00006 #include "mythlogging.h"
00007 #include "mythuihelper.h"
00008
00009 #ifdef USING_X11
00010 #include "mythxdisplay.h"
00011 #ifndef V_INTERLACE
00012 #define V_INTERLACE (0x010)
00013 #endif
00014 extern "C" {
00015 #include <X11/extensions/Xinerama.h>
00016 #include <X11/extensions/xf86vmode.h>
00017 }
00018 typedef int (*XErrorCallbackType)(Display *, XErrorEvent *);
00019 typedef std::vector<XErrorEvent> XErrorVectorType;
00020 std::map<Display*, XErrorVectorType> xerrors;
00021 std::map<Display*, XErrorCallbackType> xerror_handlers;
00022 std::map<Display*, MythXDisplay*> xdisplays;
00023 #endif // USING_X11
00024
00025 #include <QMutex>
00026
00027
00028 #ifdef USING_X11
00029
00030 static int ErrorHandler(Display *d, XErrorEvent *xeev)
00031 {
00032 xerrors[d].push_back(*xeev);
00033 return 0;
00034 }
00035
00036 void LockMythXDisplays(bool lock)
00037 {
00038 if (lock)
00039 {
00040 std::map<Display*, MythXDisplay*>::iterator it;
00041 for (it = xdisplays.begin(); it != xdisplays.end(); ++it)
00042 it->second->Lock();
00043 }
00044 else
00045 {
00046 std::map<Display*, MythXDisplay*>::reverse_iterator it;
00047 for (it = xdisplays.rbegin(); it != xdisplays.rend(); ++it)
00048 it->second->Unlock();
00049 }
00050 }
00051
00052 MythXDisplay *GetMythXDisplay(Display *d)
00053 {
00054 if (xdisplays.count(d))
00055 return xdisplays[d];
00056 return NULL;
00057 }
00058
00059 MythXDisplay *OpenMythXDisplay(void)
00060 {
00061 MythXDisplay *disp = new MythXDisplay();
00062 if (disp && disp->Open())
00063 return disp;
00064
00065 LOG(VB_GENERAL, LOG_CRIT, "MythXOpenDisplay() failed");
00066 delete disp;
00067 return NULL;
00068 }
00069
00070 MythXDisplay::MythXDisplay()
00071 : m_disp(NULL), m_screen_num(0), m_screen(NULL),
00072 m_depth(0), m_black(0), m_gc(0),
00073 m_root(0), m_lock(QMutex::Recursive)
00074 {
00075 }
00076
00077 MythXDisplay::~MythXDisplay()
00078 {
00079 MythXLocker locker(this);
00080 if (m_disp)
00081 {
00082 if (m_gc)
00083 XFreeGC(m_disp, m_gc);
00084 StopLog();
00085 if (xdisplays.count(m_disp))
00086 xdisplays.erase(m_disp);
00087 XCloseDisplay(m_disp);
00088 m_disp = NULL;
00089 }
00090 }
00091
00092 bool MythXDisplay::Open(void)
00093 {
00094 MythXLocker locker(this);
00095
00096 QString dispStr = GetMythUI()->GetX11Display();
00097 const char *dispCStr = NULL;
00098 if (!dispStr.isEmpty())
00099 dispCStr = dispStr.toAscii().constData();
00100
00101 m_disp = XOpenDisplay(dispCStr);
00102 if (!m_disp)
00103 return false;
00104
00105 xdisplays[m_disp] = this;
00106 m_screen_num = DefaultScreen(m_disp);
00107 m_screen = DefaultScreenOfDisplay(m_disp);
00108 m_black = XBlackPixel(m_disp, m_screen_num);
00109 m_depth = DefaultDepthOfScreen(m_screen);
00110 m_root = DefaultRootWindow(m_disp);
00111
00112 return true;
00113 }
00114
00115 bool MythXDisplay::CreateGC(Window win)
00116 {
00117 StartLog();
00118 XLOCK(this, m_gc = XCreateGC(m_disp, win, 0, NULL));
00119 return StopLog();
00120 }
00121
00122 void MythXDisplay::SetForeground(unsigned long color)
00123 {
00124 if (!m_gc)
00125 return;
00126 XLOCK(this, XSetForeground(m_disp, m_gc, color));
00127 }
00128
00129 void MythXDisplay::FillRectangle(Window win, const QRect &rect)
00130 {
00131 if (!m_gc)
00132 return;
00133 XLOCK(this, XFillRectangle(m_disp, win, m_gc,
00134 rect.left(), rect.top(),
00135 rect.width(), rect.height()));
00136 }
00137
00138 void MythXDisplay::MoveResizeWin(Window win, const QRect &rect)
00139 {
00140 XLOCK(this, XMoveResizeWindow(m_disp, win,
00141 rect.left(), rect.top(),
00142 rect.width(), rect.height()));
00143 }
00144
00145 int MythXDisplay::GetNumberXineramaScreens(void)
00146 {
00147 MythXLocker locker(this);
00148 int nr_xinerama_screens = 0;
00149 int event_base = 0, error_base = 0;
00150 if (XineramaQueryExtension(m_disp, &event_base, &error_base) &&
00151 XineramaIsActive(m_disp))
00152 {
00153 XFree(XineramaQueryScreens(m_disp, &nr_xinerama_screens));
00154 }
00155 return nr_xinerama_screens;
00156 }
00157
00158 QSize MythXDisplay::GetDisplaySize(void)
00159 {
00160 MythXLocker locker(this);
00161 int displayWidthPixel = DisplayWidth( m_disp, m_screen_num);
00162 int displayHeightPixel = DisplayHeight(m_disp, m_screen_num);
00163 return QSize(displayWidthPixel, displayHeightPixel);
00164 }
00165
00166 QSize MythXDisplay::GetDisplayDimensions(void)
00167 {
00168 MythXLocker locker(this);
00169 int displayWidthMM = DisplayWidthMM( m_disp, m_screen_num);
00170 int displayHeightMM = DisplayHeightMM(m_disp, m_screen_num);
00171 return QSize(displayWidthMM, displayHeightMM);
00172 }
00173
00174 float MythXDisplay::GetRefreshRate(void)
00175 {
00176 XF86VidModeModeLine mode_line;
00177 int dot_clock;
00178 MythXLocker locker(this);
00179
00180 if (!XF86VidModeGetModeLine(m_disp, m_screen_num, &dot_clock, &mode_line))
00181 {
00182 LOG(VB_GENERAL, LOG_ERR, "X11 ModeLine query failed");
00183 return -1;
00184 }
00185
00186 double rate = mode_line.htotal * mode_line.vtotal;
00187
00188
00189 if (rate == 0.0 || dot_clock == 0)
00190 {
00191 LOG(VB_GENERAL, LOG_ERR, "X11 ModeLine query returned zeroes");
00192 return -1;
00193 }
00194
00195 rate = (dot_clock * 1000.0) / rate;
00196
00197 if (((mode_line.flags & V_INTERLACE) != 0) &&
00198 rate > 24.5 && rate < 30.5)
00199 {
00200 LOG(VB_PLAYBACK, LOG_INFO,
00201 "Doubling refresh rate for interlaced display.");
00202 rate *= 2.0;
00203 }
00204
00205 return rate;
00206 }
00207
00208 void MythXDisplay::Sync(bool flush)
00209 {
00210 XLOCK(this, XSync(m_disp, flush));
00211 }
00212
00213 void MythXDisplay::StartLog(void)
00214 {
00215 if (!m_disp || xerror_handlers.count(m_disp))
00216 return;
00217
00218 Sync();
00219 XLOCK(this, xerror_handlers[m_disp] = XSetErrorHandler(ErrorHandler));
00220 }
00221
00222 bool MythXDisplay::StopLog(void)
00223 {
00224 if (!(m_disp && xerror_handlers.count(m_disp)))
00225 return false;
00226
00227 Sync();
00228 XErrorCallbackType old_handler = xerror_handlers[m_disp];
00229 XLOCK(this, XSetErrorHandler(old_handler));
00230 xerror_handlers.erase(m_disp);
00231 return CheckErrors();
00232 }
00233
00234 bool MythXDisplay::CheckErrors(Display *disp)
00235 {
00236 if (!disp)
00237 CheckOrphanedErrors();
00238
00239 Display *d = disp ? disp : m_disp;
00240 if (!d)
00241 return false;
00242
00243 if (!xerrors.count(d))
00244 return true;
00245
00246 MythXLocker locker(this);
00247 Sync();
00248 const std::vector<XErrorEvent>& events = xerrors[d];
00249
00250 if (events.size() < 1)
00251 return true;
00252
00253 for (int i = events.size() -1; i>=0; --i)
00254 {
00255 char buf[200];
00256 XGetErrorText(d, events[i].error_code, buf, sizeof(buf));
00257 LOG(VB_GENERAL, LOG_ERR,
00258 QString("\n"
00259 "XError type: %1\n"
00260 " serial no: %2\n"
00261 " err code: %3 (%4)\n"
00262 " req code: %5\n"
00263 " minor code: %6\n"
00264 "resource id: %7\n")
00265 .arg(events[i].type)
00266 .arg(events[i].serial)
00267 .arg(events[i].error_code).arg(buf)
00268 .arg(events[i].request_code)
00269 .arg(events[i].minor_code)
00270 .arg(events[i].resourceid));
00271 }
00272 xerrors.erase(d);
00273 return false;
00274 }
00275
00276 void MythXDisplay::CheckOrphanedErrors(void)
00277 {
00278 if (xerrors.size() < 1)
00279 return;
00280
00281 std::map<Display*, XErrorVectorType>::iterator errors = xerrors.begin();
00282 for (; errors != xerrors.end(); ++errors)
00283 if (!xerror_handlers.count(errors->first))
00284 CheckErrors(errors->first);
00285 }
00286
00287 #endif // USING_X11