File indexing completed on 2024-04-14 14:20:26

0001 /*
0002 
0003   Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org>
0004 
0005   Permission is hereby granted, free of charge, to any person obtaining a
0006   copy of this software and associated documentation files (the "Software"),
0007   to deal in the Software without restriction, including without limitation
0008   the rights to use, copy, modify, merge, publish, distribute, sublicense,
0009   and/or sell copies of the Software, and to permit persons to whom the
0010   Software is furnished to do so, subject to the following conditions:
0011 
0012   The above copyright notice and this permission notice shall be included in
0013   all copies or substantial portions of the Software.
0014 
0015   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0018   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0020   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
0021   DEALINGS IN THE SOFTWARE.
0022 
0023 */
0024 
0025 #include "kxerrorhandler.h"
0026 
0027 #include "netwm_def.h"
0028 
0029 #include <stdio.h>
0030 #include <QX11Info>
0031 
0032 class KXErrorHandlerPrivate
0033 {
0034 public:
0035     KXErrorHandlerPrivate(Display *dpy) :
0036         first_request(XNextRequest(dpy)),
0037         display(dpy),
0038         was_error(false)
0039     {
0040     }
0041     unsigned long first_request;
0042     Display *display;
0043     bool was_error;
0044     XErrorEvent error_event;
0045 };
0046 
0047 KXErrorHandler **KXErrorHandler::handlers = nullptr;
0048 int KXErrorHandler::pos = 0;
0049 int KXErrorHandler::size = 0;
0050 
0051 KXErrorHandler::KXErrorHandler(Display *dpy)
0052     :   user_handler1(nullptr),
0053         user_handler2(nullptr),
0054         old_handler(XSetErrorHandler(handler_wrapper)),
0055         d(new KXErrorHandlerPrivate(dpy))
0056 {
0057     addHandler();
0058 }
0059 
0060 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0061 KXErrorHandler::KXErrorHandler(bool (*handler)(int request, int error_code, unsigned long resource_id), Display *dpy)
0062     :   user_handler1(handler),
0063         user_handler2(nullptr),
0064         old_handler(XSetErrorHandler(handler_wrapper)),
0065         d(new KXErrorHandlerPrivate(dpy))
0066 {
0067     addHandler();
0068 }
0069 #endif
0070 
0071 KXErrorHandler::KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy)
0072     :   user_handler1(nullptr),
0073         user_handler2(handler),
0074         old_handler(XSetErrorHandler(handler_wrapper)),
0075         d(new KXErrorHandlerPrivate(dpy))
0076 {
0077     addHandler();
0078 }
0079 
0080 KXErrorHandler::~KXErrorHandler()
0081 {
0082     XSetErrorHandler(old_handler);
0083     Q_ASSERT_X(this == handlers[ pos - 1 ], "KXErrorHandler", "out of order");
0084     --pos;
0085     delete d;
0086 }
0087 
0088 void KXErrorHandler::addHandler()
0089 {
0090     if (size == pos) {
0091         size += 16;
0092         handlers = static_cast< KXErrorHandler ** >(realloc(handlers, size * sizeof(KXErrorHandler *)));
0093     }
0094     handlers[ pos++ ] = this;
0095 }
0096 
0097 bool KXErrorHandler::error(bool sync) const
0098 {
0099     if (sync) {
0100         XSync(d->display, False);
0101     }
0102     return d->was_error;
0103 }
0104 
0105 XErrorEvent KXErrorHandler::errorEvent() const
0106 {
0107     return d->error_event;
0108 }
0109 
0110 int KXErrorHandler::handler_wrapper(Display *dpy, XErrorEvent *e)
0111 {
0112     --pos;
0113     int ret = handlers[ pos ]->handle(dpy, e);
0114     ++pos;
0115     return ret;
0116 }
0117 
0118 int KXErrorHandler::handle(Display *dpy, XErrorEvent *e)
0119 {
0120     if (dpy == d->display
0121             // e->serial >= d->first_request , compare like X timestamps to handle wrapping
0122             && NET::timestampCompare(e->serial, d->first_request) >= 0) {
0123         // it's for us
0124         //qDebug( "Handling: %p", static_cast< void* >( this ));
0125         bool error = false;
0126         if (user_handler1 != nullptr) {
0127             if (user_handler1(e->request_code, e->error_code, e->resourceid)) {
0128                 error = true;
0129             }
0130         } else if (user_handler2 != nullptr) {
0131             if (user_handler2(dpy, e) != 0) {
0132                 error = true;
0133             }
0134         } else { // no handler set, simply set that there was an error
0135             error = true;
0136         }
0137         if (error && !d->was_error) {
0138             // only remember the first
0139             d->was_error = true;
0140             d->error_event = *e;
0141         }
0142         return 0;
0143     }
0144     //qDebug( "Going deeper: %p", static_cast< void* >( this ));
0145     return old_handler(dpy, e);
0146 }
0147 
0148 QByteArray KXErrorHandler::errorMessage(const XErrorEvent &event, Display *dpy)
0149 {
0150     // "Error: <error> (<value>), Request: <request>(<value>), Resource: <value>"
0151     QByteArray ret;
0152     char tmp[ 256 ];
0153     char num[ 256 ];
0154 #if 0 // see below
0155     if (event.request_code < 128)  // core request
0156 #endif
0157     {
0158         XGetErrorText(dpy, event.error_code, tmp, 255);
0159         if (char *paren = strchr(tmp, '(')) { // the explanation in parentheses just makes
0160             *paren = '\0';    // it more verbose and is not really useful
0161         }
0162         // the various casts are to get overloads non-ambiguous :-/
0163         ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
0164         sprintf(num, "%d", event.request_code);
0165         XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 256);
0166         ret += QByteArray(", request: ") + (const char *)tmp + '[' + QByteArray::number(event.request_code) + ']';
0167         if (event.resourceid != 0) {
0168             ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
0169         }
0170     }
0171 #if 0
0172     else { // extensions
0173         // XGetErrorText() currently has a bug that makes it fail to find text
0174         // for some errors (when error==error_base), also XGetErrorDatabaseText()
0175         // requires the right extension name, so it is needed to get info about
0176         // all extensions. However that is almost impossible:
0177         // - Xlib itself has it, but in internal data.
0178         // - Opening another X connection now can cause deadlock with server grabs.
0179         // - Fetching it at startup means a bunch of roundtrips.
0180         // So if this becomes more useful in the future, do the roundtrips at startup,
0181         // or fetch it in kded and export as an env.var or something.
0182         Display *dpy2 = XOpenDisplay(XDisplayString(dpy));
0183         int nextensions;
0184         char **extensions = XListExtensions(dpy2, &nextensions);
0185         int *majors = NULL;
0186         int *error_bases = NULL;
0187         if (extensions == NULL) {
0188             nextensions = 0;
0189         } else {
0190             majors = new int[ nextensions ];
0191             error_bases = new int[ nextensions ];
0192             for (int i = 0;
0193                     i < nextensions;
0194                     ++i) {
0195                 int dummy;
0196                 if (!XQueryExtension(dpy2, extensions[ i ], &majors[ i ], &dummy, &error_bases[ i ])) {
0197                     majors[ i ] = 0;
0198                     error_bases[ i ] = 0;
0199                 }
0200             }
0201         }
0202         XGetErrorText(dpy, event.error_code, tmp, 255);
0203         int index = -1;
0204         int base = 0;
0205         for (int i = 0;
0206                 i < nextensions;
0207                 ++i)
0208             if (error_bases[ i ] != 0
0209                     && event.error_code >= error_bases[ i ] && (index == -1 || error_bases[ i ] > base)) {
0210                 index = i;
0211                 base = error_bases[ i ];
0212             }
0213         if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed,
0214             // or it has a bug that causes not finding all errors, check ourselves
0215             if (index != -1) {
0216                 qsnprintf(num, 255, "%s.%d", extensions[ index ], event.error_code - base);
0217                 XGetErrorDatabaseText(dpy, "XProtoError", num, "<unknown>", tmp, 255);
0218             } else {
0219                 strcpy(tmp, "<unknown>");
0220             }
0221         }
0222         if (char *paren = strchr(tmp, '(')) {
0223             *paren = '\0';
0224         }
0225         if (index != -1)
0226             ret = QByteArray("error: ") + (const char *)tmp + '[' + (const char *)extensions[ index ]
0227                   + '+' + QByteArray::number(event.error_code - base) + ']';
0228         else {
0229             ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
0230         }
0231         tmp[ 0 ] = '\0';
0232         for (int i = 0;
0233                 i < nextensions;
0234                 ++i)
0235             if (majors[ i ] == event.request_code) {
0236                 qsnprintf(num, 255, "%s.%d", extensions[ i ], event.minor_code);
0237                 XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 255);
0238                 ret += QByteArray(", request: ") + (const char *)tmp + '[' + (const char *)extensions[ i ] + '+'
0239                        + QByteArray::number(event.minor_code) + ']';
0240             }
0241         if (tmp[ 0 ] == '\0')  // not found???
0242             ret += QByteArray(", request <unknown> [") + QByteArray::number(event.request_code) + ':'
0243                    + QByteArray::number(event.minor_code) + ']';
0244         if (event.resourceid != 0) {
0245             ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
0246         }
0247         if (extensions != NULL) {
0248             XFreeExtensionList(extensions);
0249         }
0250         delete[] majors;
0251         delete[] error_bases;
0252         XCloseDisplay(dpy2);
0253     }
0254 #endif
0255     return ret;
0256 }
0257 
0258 Display *KXErrorHandler::display()
0259 {
0260     return QX11Info::display();
0261 }