Files
RedBear-OS/local/recipes/kde/kf6-kwindowsystem/source/src/kxerrorhandler.cpp
T
2026-04-14 10:51:06 +01:00

235 lines
7.8 KiB
C++

/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#include "kxerrorhandler_p.h"
#include <config-kwindowsystem.h>
#include <fixx11h.h>
#include "netwm_def.h"
#include <stdio.h>
#include <QByteArray>
#include <QString>
class KXErrorHandlerPrivate
{
public:
KXErrorHandlerPrivate(Display *dpy)
: first_request(XNextRequest(dpy))
, display(dpy)
, was_error(false)
{
}
unsigned long first_request;
Display *display;
bool was_error;
XErrorEvent error_event;
};
KXErrorHandler **KXErrorHandler::handlers = nullptr;
int KXErrorHandler::pos = 0;
int KXErrorHandler::size = 0;
KXErrorHandler::KXErrorHandler(Display *dpy)
: user_handler1(nullptr)
, user_handler2(nullptr)
, old_handler(XSetErrorHandler(handler_wrapper))
, d(new KXErrorHandlerPrivate(dpy))
{
addHandler();
}
KXErrorHandler::KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy)
: user_handler1(nullptr)
, user_handler2(handler)
, old_handler(XSetErrorHandler(handler_wrapper))
, d(new KXErrorHandlerPrivate(dpy))
{
addHandler();
}
KXErrorHandler::~KXErrorHandler()
{
XSetErrorHandler(old_handler);
Q_ASSERT_X(this == handlers[pos - 1], "KXErrorHandler", "out of order");
--pos;
delete d;
}
void KXErrorHandler::addHandler()
{
if (size == pos) {
size += 16;
handlers = static_cast<KXErrorHandler **>(realloc(handlers, size * sizeof(KXErrorHandler *)));
}
handlers[pos++] = this;
}
bool KXErrorHandler::error(bool sync) const
{
if (sync) {
XSync(d->display, False);
}
return d->was_error;
}
XErrorEvent KXErrorHandler::errorEvent() const
{
return d->error_event;
}
int KXErrorHandler::handler_wrapper(Display *dpy, XErrorEvent *e)
{
--pos;
int ret = handlers[pos]->handle(dpy, e);
++pos;
return ret;
}
int KXErrorHandler::handle(Display *dpy, XErrorEvent *e)
{
if (dpy == d->display
// e->serial >= d->first_request , compare like X timestamps to handle wrapping
&& NET::timestampCompare(e->serial, d->first_request) >= 0) {
// it's for us
// qDebug( "Handling: %p", static_cast< void* >( this ));
bool error = false;
if (user_handler1 != nullptr) {
if (user_handler1(e->request_code, e->error_code, e->resourceid)) {
error = true;
}
} else if (user_handler2 != nullptr) {
if (user_handler2(dpy, e) != 0) {
error = true;
}
} else { // no handler set, simply set that there was an error
error = true;
}
if (error && !d->was_error) {
// only remember the first
d->was_error = true;
d->error_event = *e;
}
return 0;
}
// qDebug( "Going deeper: %p", static_cast< void* >( this ));
return old_handler(dpy, e);
}
QByteArray KXErrorHandler::errorMessage(const XErrorEvent &event, Display *dpy)
{
// "Error: <error> (<value>), Request: <request>(<value>), Resource: <value>"
QByteArray ret;
char tmp[256];
#if 0 // see below
char num[ 256 ];
if (event.request_code < 128) // core request
#endif
{
XGetErrorText(dpy, event.error_code, tmp, 255);
if (char *paren = strchr(tmp, '(')) { // the explanation in parentheses just makes
*paren = '\0'; // it more verbose and is not really useful
}
// the various casts are to get overloads non-ambiguous :-/
/*
ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
sprintf(num, "%d", event.request_code);
XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 256);
ret += QByteArray(", request: ") + (const char *)tmp + '[' + QByteArray::number(event.request_code) + ']';
if (event.resourceid != 0) {
ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
}
*/
}
#if 0
else { // extensions
// XGetErrorText() currently has a bug that makes it fail to find text
// for some errors (when error==error_base), also XGetErrorDatabaseText()
// requires the right extension name, so it is needed to get info about
// all extensions. However that is almost impossible:
// - Xlib itself has it, but in internal data.
// - Opening another X connection now can cause deadlock with server grabs.
// - Fetching it at startup means a bunch of roundtrips.
// So if this becomes more useful in the future, do the roundtrips at startup,
// or fetch it in kded and export as an env.var or something.
Display *dpy2 = XOpenDisplay(XDisplayString(dpy));
int nextensions;
char **extensions = XListExtensions(dpy2, &nextensions);
int *majors = nullptr;
int *error_bases = nullptr;
if (extensions == nullptr) {
nextensions = 0;
} else {
majors = new int[ nextensions ];
error_bases = new int[ nextensions ];
for (int i = 0;
i < nextensions;
++i) {
int dummy;
if (!XQueryExtension(dpy2, extensions[ i ], &majors[ i ], &dummy, &error_bases[ i ])) {
majors[ i ] = 0;
error_bases[ i ] = 0;
}
}
}
XGetErrorText(dpy, event.error_code, tmp, 255);
int index = -1;
int base = 0;
for (int i = 0;
i < nextensions;
++i)
if (error_bases[ i ] != 0
&& event.error_code >= error_bases[ i ] && (index == -1 || error_bases[ i ] > base)) {
index = i;
base = error_bases[ i ];
}
if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed,
// or it has a bug that causes not finding all errors, check ourselves
if (index != -1) {
qsnprintf(num, 255, "%s.%d", extensions[ index ], event.error_code - base);
XGetErrorDatabaseText(dpy, "XProtoError", num, "<unknown>", tmp, 255);
} else {
strcpy(tmp, "<unknown>");
}
}
if (char *paren = strchr(tmp, '(')) {
*paren = '\0';
}
if (index != -1)
ret = QByteArray("error: ") + (const char *)tmp + '[' + (const char *)extensions[ index ]
+ '+' + QByteArray::number(event.error_code - base) + ']';
else {
ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
}
tmp[ 0 ] = '\0';
for (int i = 0;
i < nextensions;
++i)
if (majors[ i ] == event.request_code) {
qsnprintf(num, 255, "%s.%d", extensions[ i ], event.minor_code);
XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 255);
ret += QByteArray(", request: ") + (const char *)tmp + '[' + (const char *)extensions[ i ] + '+'
+ QByteArray::number(event.minor_code) + ']';
}
if (tmp[ 0 ] == '\0') // not found???
ret += QByteArray(", request <unknown> [") + QByteArray::number(event.request_code) + ':'
+ QByteArray::number(event.minor_code) + ']';
if (event.resourceid != 0) {
ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
}
if (extensions != nullptr) {
XFreeExtensionList(extensions);
}
delete[] majors;
delete[] error_bases;
XCloseDisplay(dpy2);
}
#endif
return ret;
}