File indexing completed on 2024-05-05 04:00:01

0001 /*
0002     SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "kxerrorhandler_p.h"
0008 #include <config-kwindowsystem.h>
0009 
0010 #include <fixx11h.h>
0011 
0012 #include "netwm_def.h"
0013 
0014 #include <stdio.h>
0015 
0016 #include <QByteArray>
0017 #include <QString>
0018 
0019 class KXErrorHandlerPrivate
0020 {
0021 public:
0022     KXErrorHandlerPrivate(Display *dpy)
0023         : first_request(XNextRequest(dpy))
0024         , display(dpy)
0025         , was_error(false)
0026     {
0027     }
0028     unsigned long first_request;
0029     Display *display;
0030     bool was_error;
0031     XErrorEvent error_event;
0032 };
0033 
0034 KXErrorHandler **KXErrorHandler::handlers = nullptr;
0035 int KXErrorHandler::pos = 0;
0036 int KXErrorHandler::size = 0;
0037 
0038 KXErrorHandler::KXErrorHandler(Display *dpy)
0039     : user_handler1(nullptr)
0040     , user_handler2(nullptr)
0041     , old_handler(XSetErrorHandler(handler_wrapper))
0042     , d(new KXErrorHandlerPrivate(dpy))
0043 {
0044     addHandler();
0045 }
0046 
0047 KXErrorHandler::KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy)
0048     : user_handler1(nullptr)
0049     , user_handler2(handler)
0050     , old_handler(XSetErrorHandler(handler_wrapper))
0051     , d(new KXErrorHandlerPrivate(dpy))
0052 {
0053     addHandler();
0054 }
0055 
0056 KXErrorHandler::~KXErrorHandler()
0057 {
0058     XSetErrorHandler(old_handler);
0059     Q_ASSERT_X(this == handlers[pos - 1], "KXErrorHandler", "out of order");
0060     --pos;
0061     delete d;
0062 }
0063 
0064 void KXErrorHandler::addHandler()
0065 {
0066     if (size == pos) {
0067         size += 16;
0068         handlers = static_cast<KXErrorHandler **>(realloc(handlers, size * sizeof(KXErrorHandler *)));
0069     }
0070     handlers[pos++] = this;
0071 }
0072 
0073 bool KXErrorHandler::error(bool sync) const
0074 {
0075     if (sync) {
0076         XSync(d->display, False);
0077     }
0078     return d->was_error;
0079 }
0080 
0081 XErrorEvent KXErrorHandler::errorEvent() const
0082 {
0083     return d->error_event;
0084 }
0085 
0086 int KXErrorHandler::handler_wrapper(Display *dpy, XErrorEvent *e)
0087 {
0088     --pos;
0089     int ret = handlers[pos]->handle(dpy, e);
0090     ++pos;
0091     return ret;
0092 }
0093 
0094 int KXErrorHandler::handle(Display *dpy, XErrorEvent *e)
0095 {
0096     if (dpy == d->display
0097         // e->serial >= d->first_request , compare like X timestamps to handle wrapping
0098         && NET::timestampCompare(e->serial, d->first_request) >= 0) {
0099         // it's for us
0100         // qDebug( "Handling: %p", static_cast< void* >( this ));
0101         bool error = false;
0102         if (user_handler1 != nullptr) {
0103             if (user_handler1(e->request_code, e->error_code, e->resourceid)) {
0104                 error = true;
0105             }
0106         } else if (user_handler2 != nullptr) {
0107             if (user_handler2(dpy, e) != 0) {
0108                 error = true;
0109             }
0110         } else { // no handler set, simply set that there was an error
0111             error = true;
0112         }
0113         if (error && !d->was_error) {
0114             // only remember the first
0115             d->was_error = true;
0116             d->error_event = *e;
0117         }
0118         return 0;
0119     }
0120     // qDebug( "Going deeper: %p", static_cast< void* >( this ));
0121     return old_handler(dpy, e);
0122 }
0123 
0124 QByteArray KXErrorHandler::errorMessage(const XErrorEvent &event, Display *dpy)
0125 {
0126     // "Error: <error> (<value>), Request: <request>(<value>), Resource: <value>"
0127     QByteArray ret;
0128     char tmp[256];
0129 #if 0 // see below
0130     char num[ 256 ];
0131     if (event.request_code < 128)  // core request
0132 #endif
0133     {
0134         XGetErrorText(dpy, event.error_code, tmp, 255);
0135         if (char *paren = strchr(tmp, '(')) { // the explanation in parentheses just makes
0136             *paren = '\0'; // it more verbose and is not really useful
0137         }
0138         // the various casts are to get overloads non-ambiguous :-/
0139         /*
0140                 ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
0141                 sprintf(num, "%d", event.request_code);
0142                 XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 256);
0143                 ret += QByteArray(", request: ") + (const char *)tmp + '[' + QByteArray::number(event.request_code) + ']';
0144                 if (event.resourceid != 0) {
0145                     ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
0146                 }
0147         */
0148     }
0149 #if 0
0150     else { // extensions
0151         // XGetErrorText() currently has a bug that makes it fail to find text
0152         // for some errors (when error==error_base), also XGetErrorDatabaseText()
0153         // requires the right extension name, so it is needed to get info about
0154         // all extensions. However that is almost impossible:
0155         // - Xlib itself has it, but in internal data.
0156         // - Opening another X connection now can cause deadlock with server grabs.
0157         // - Fetching it at startup means a bunch of roundtrips.
0158         // So if this becomes more useful in the future, do the roundtrips at startup,
0159         // or fetch it in kded and export as an env.var or something.
0160         Display *dpy2 = XOpenDisplay(XDisplayString(dpy));
0161         int nextensions;
0162         char **extensions = XListExtensions(dpy2, &nextensions);
0163         int *majors = nullptr;
0164         int *error_bases = nullptr;
0165         if (extensions == nullptr) {
0166             nextensions = 0;
0167         } else {
0168             majors = new int[ nextensions ];
0169             error_bases = new int[ nextensions ];
0170             for (int i = 0;
0171                     i < nextensions;
0172                     ++i) {
0173                 int dummy;
0174                 if (!XQueryExtension(dpy2, extensions[ i ], &majors[ i ], &dummy, &error_bases[ i ])) {
0175                     majors[ i ] = 0;
0176                     error_bases[ i ] = 0;
0177                 }
0178             }
0179         }
0180         XGetErrorText(dpy, event.error_code, tmp, 255);
0181         int index = -1;
0182         int base = 0;
0183         for (int i = 0;
0184                 i < nextensions;
0185                 ++i)
0186             if (error_bases[ i ] != 0
0187                     && event.error_code >= error_bases[ i ] && (index == -1 || error_bases[ i ] > base)) {
0188                 index = i;
0189                 base = error_bases[ i ];
0190             }
0191         if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed,
0192             // or it has a bug that causes not finding all errors, check ourselves
0193             if (index != -1) {
0194                 qsnprintf(num, 255, "%s.%d", extensions[ index ], event.error_code - base);
0195                 XGetErrorDatabaseText(dpy, "XProtoError", num, "<unknown>", tmp, 255);
0196             } else {
0197                 strcpy(tmp, "<unknown>");
0198             }
0199         }
0200         if (char *paren = strchr(tmp, '(')) {
0201             *paren = '\0';
0202         }
0203         if (index != -1)
0204             ret = QByteArray("error: ") + (const char *)tmp + '[' + (const char *)extensions[ index ]
0205                   + '+' + QByteArray::number(event.error_code - base) + ']';
0206         else {
0207             ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
0208         }
0209         tmp[ 0 ] = '\0';
0210         for (int i = 0;
0211                 i < nextensions;
0212                 ++i)
0213             if (majors[ i ] == event.request_code) {
0214                 qsnprintf(num, 255, "%s.%d", extensions[ i ], event.minor_code);
0215                 XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 255);
0216                 ret += QByteArray(", request: ") + (const char *)tmp + '[' + (const char *)extensions[ i ] + '+'
0217                        + QByteArray::number(event.minor_code) + ']';
0218             }
0219         if (tmp[ 0 ] == '\0')  // not found???
0220             ret += QByteArray(", request <unknown> [") + QByteArray::number(event.request_code) + ':'
0221                    + QByteArray::number(event.minor_code) + ']';
0222         if (event.resourceid != 0) {
0223             ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
0224         }
0225         if (extensions != nullptr) {
0226             XFreeExtensionList(extensions);
0227         }
0228         delete[] majors;
0229         delete[] error_bases;
0230         XCloseDisplay(dpy2);
0231     }
0232 #endif
0233     return ret;
0234 }