mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-17 11:35:08 -04:00
C client: Implement error dialog on linux
This commit is contained in:
parent
d5faaf9c26
commit
eb007e91cd
@ -2,6 +2,8 @@
|
|||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "Funcs.h"
|
||||||
|
|
||||||
static void ErrorHandler_FailCore(ReturnCode result, const char* raw_msg, void* ctx);
|
static void ErrorHandler_FailCore(ReturnCode result, const char* raw_msg, void* ctx);
|
||||||
static void ErrorHandler_Backtrace(STRING_TRANSIENT String* str, void* ctx);
|
static void ErrorHandler_Backtrace(STRING_TRANSIENT String* str, void* ctx);
|
||||||
static void ErrorHandler_Registers(STRING_TRANSIENT String* str, void* ctx);
|
static void ErrorHandler_Registers(STRING_TRANSIENT String* str, void* ctx);
|
||||||
@ -119,12 +121,240 @@ static void ErrorHandler_Backtrace(STRING_TRANSIENT String* str, void* ctx) {
|
|||||||
String_AppendConst(str, "\r\n");
|
String_AppendConst(str, "\r\n");
|
||||||
}
|
}
|
||||||
#elif CC_BUILD_NIX
|
#elif CC_BUILD_NIX
|
||||||
|
#include <X11/X.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/keysym.h>
|
||||||
|
|
||||||
|
Display* dpy;
|
||||||
|
unsigned long X11_Col(UInt8 r, UInt8 g, UInt8 b) {
|
||||||
|
Colormap cmap = XDefaultColormap(dpy, DefaultScreen(dpy));
|
||||||
|
XColor col = { 0 };
|
||||||
|
col.red = r << 8;
|
||||||
|
col.green = g << 8;
|
||||||
|
col.blue = b << 8;
|
||||||
|
col.flags = DoRed | DoGreen | DoBlue;
|
||||||
|
|
||||||
|
XAllocColor(dpy, cmap, &col);
|
||||||
|
return col.pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Window win;
|
||||||
|
GC gc;
|
||||||
|
unsigned long white, black, background;
|
||||||
|
unsigned long btnBorder, highlight, shadow;
|
||||||
|
} X11Window;
|
||||||
|
|
||||||
|
static void X11Window_Init(X11Window* w) {
|
||||||
|
w->black = BlackPixel(dpy, DefaultScreen(dpy));
|
||||||
|
w->white = WhitePixel(dpy, DefaultScreen(dpy));
|
||||||
|
w->background = X11_Col(206, 206, 206);
|
||||||
|
|
||||||
|
w->btnBorder = X11_Col(60, 60, 60);
|
||||||
|
w->highlight = X11_Col(144, 144, 144);
|
||||||
|
w->shadow = X11_Col(49, 49, 49);
|
||||||
|
|
||||||
|
w->win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 100, 100,
|
||||||
|
0, w->black, w->background);
|
||||||
|
XSelectInput(dpy, w->win, ExposureMask | StructureNotifyMask |
|
||||||
|
KeyReleaseMask | PointerMotionMask |
|
||||||
|
ButtonPressMask | ButtonReleaseMask );
|
||||||
|
|
||||||
|
w->gc = XCreateGC(dpy, w->win, 0, NULL);
|
||||||
|
XSetForeground(dpy, w->gc, w->black);
|
||||||
|
XSetBackground(dpy, w->gc, w->background);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void X11Window_Free(X11Window* w) {
|
||||||
|
XFreeGC(dpy, w->gc);
|
||||||
|
XDestroyWindow(dpy, w->win);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int X, Y, Width, Height;
|
||||||
|
int LineHeight, Descent;
|
||||||
|
const char* Text;
|
||||||
|
} X11Textbox;
|
||||||
|
|
||||||
|
static void X11Textbox_Measure(X11Textbox* t, XFontStruct* font) {
|
||||||
|
String str = String_FromReadonly(t->Text);
|
||||||
|
int direction, ascent, descent, end, lines = 0;
|
||||||
|
XCharStruct overall;
|
||||||
|
|
||||||
|
for (end = 0; end >= 0; lines++) {
|
||||||
|
end = String_IndexOf(&str, '\n', 0);
|
||||||
|
Int32 len = end == -1 ? str.length : end;
|
||||||
|
|
||||||
|
XTextExtents(font, str.buffer, len, &direction, &ascent, &descent, &overall);
|
||||||
|
t->Width = max(overall.width, t->Width);
|
||||||
|
if (end >= 0) str = String_UNSAFE_SubstringAt(&str, end + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
t->LineHeight = ascent + descent;
|
||||||
|
t->Descent = descent;
|
||||||
|
t->Height = t->LineHeight * lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void X11Textbox_Draw(X11Textbox* t, X11Window* w) {
|
||||||
|
String str = String_FromReadonly(t->Text);
|
||||||
|
int end, y = t->Y + t->LineHeight - t->Descent; /* TODO: is -Descent even right? */
|
||||||
|
|
||||||
|
for (end = 0; end >= 0; y += t->LineHeight) {
|
||||||
|
end = String_IndexOf(&str, '\n', 0);
|
||||||
|
Int32 len = end == -1 ? str.length : end;
|
||||||
|
|
||||||
|
XDrawString(dpy, w->win, w->gc, t->X, y, str.buffer, len);
|
||||||
|
if (end >= 0) str = String_UNSAFE_SubstringAt(&str, end + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int X, Y, Width, Height;
|
||||||
|
bool Clicked;
|
||||||
|
X11Textbox Text;
|
||||||
|
} X11Button;
|
||||||
|
|
||||||
|
static void X11Button_Draw(X11Button* b, X11Window* w) {
|
||||||
|
XSetForeground(dpy, w->gc, w->btnBorder);
|
||||||
|
XDrawRectangle(dpy, w->win, w->gc, b->X, b->Y,
|
||||||
|
b->Width, b->Height);
|
||||||
|
|
||||||
|
X11Textbox* t = &b->Text;
|
||||||
|
int begX = b->X + 1, endX = b->X + b->Width - 1;
|
||||||
|
int begY = b->Y + 1, endY = b->Y + b->Height - 1;
|
||||||
|
|
||||||
|
if (b->Clicked) {
|
||||||
|
XSetForeground(dpy, w->gc, w->highlight);
|
||||||
|
XDrawRectangle(dpy, w->win, w->gc, begX, begY,
|
||||||
|
endX - begX, endY - begY);
|
||||||
|
} else {
|
||||||
|
XSetForeground(dpy, w->gc, w->white);
|
||||||
|
XDrawLine(dpy, w->win, w->gc, begX, begY,
|
||||||
|
endX - 1, begY);
|
||||||
|
XDrawLine(dpy, w->win, w->gc, begX, begY,
|
||||||
|
begX, endY - 1);
|
||||||
|
|
||||||
|
XSetForeground(dpy, w->gc, w->highlight);
|
||||||
|
XDrawLine(dpy, w->win, w->gc, begX + 1, endY - 1,
|
||||||
|
endX - 1, endY - 1);
|
||||||
|
XDrawLine(dpy, w->win, w->gc, endX - 1, begY + 1,
|
||||||
|
endX - 1, endY - 1);
|
||||||
|
|
||||||
|
XSetForeground(dpy, w->gc, w->shadow);
|
||||||
|
XDrawLine(dpy, w->win, w->gc, begX, endY, endX, endY);
|
||||||
|
XDrawLine(dpy, w->win, w->gc, endX, begY, endX, endY);
|
||||||
|
}
|
||||||
|
|
||||||
|
XSetForeground(dpy, w->gc, w->black);
|
||||||
|
t->X = b->X + b->Clicked + (b->Width - t->Width) / 2;
|
||||||
|
t->Y = b->Y + b->Clicked + (b->Height - t->Height) / 2;
|
||||||
|
X11Textbox_Draw(t, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int X11Button_Contains(X11Button* b, int x, int y) {
|
||||||
|
return x >= b->X && x < (b->X + b->Width) &&
|
||||||
|
y >= b->Y && y < (b->Y + b->Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void X11_MessageBox(const char* title, const char* text, X11Window* w) {
|
||||||
|
X11Button ok = { 0 };
|
||||||
|
X11Textbox body = { 0 };
|
||||||
|
|
||||||
|
X11Window_Init(w);
|
||||||
|
XMapWindow(dpy, w->win);
|
||||||
|
XStoreName(dpy, w->win, title);
|
||||||
|
|
||||||
|
Atom wmDelete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
||||||
|
XSetWMProtocols(dpy, w->win, &wmDelete, 1);
|
||||||
|
|
||||||
|
XFontStruct* font = XQueryFont(dpy, XGContextFromGC(w->gc));
|
||||||
|
if (!font) return;
|
||||||
|
|
||||||
|
/* Compute size of widgets */
|
||||||
|
body.Text = text;
|
||||||
|
X11Textbox_Measure(&body, font);
|
||||||
|
ok.Text.Text = "OK";
|
||||||
|
X11Textbox_Measure(&ok.Text, font);
|
||||||
|
ok.Width = ok.Text.Width + 70;
|
||||||
|
ok.Height = ok.Text.Height + 10;
|
||||||
|
|
||||||
|
/* Compute size and position of window */
|
||||||
|
int width = body.Width + 20;
|
||||||
|
int height = body.Height + 20 + ok.Height + 20;
|
||||||
|
int x = DisplayWidth (dpy, DefaultScreen(dpy))/2 - width/2;
|
||||||
|
int y = DisplayHeight(dpy, DefaultScreen(dpy))/2 - height/2;
|
||||||
|
XMoveResizeWindow(dpy, w->win, x, y, width, height);
|
||||||
|
|
||||||
|
/* Adjust bounds of widgets */
|
||||||
|
body.X = 10; body.Y = 10;
|
||||||
|
ok.X = width/2 - ok.Width/2;
|
||||||
|
ok.Y = height - ok.Height - 10;
|
||||||
|
|
||||||
|
XFreeFontInfo(NULL, font, 1);
|
||||||
|
XUnmapWindow(dpy, w->win); /* Make window non resizeable */
|
||||||
|
|
||||||
|
XSizeHints hints = { 0 };
|
||||||
|
hints.flags = PSize | PMinSize | PMaxSize;
|
||||||
|
hints.min_width = hints.max_width = hints.base_width = width;
|
||||||
|
hints.min_height = hints.max_height = hints.base_height = height;
|
||||||
|
|
||||||
|
XSetWMNormalHints(dpy, w->win, &hints);
|
||||||
|
XMapRaised(dpy, w->win);
|
||||||
|
XFlush(dpy);
|
||||||
|
|
||||||
|
XEvent e;
|
||||||
|
int mouseX = -1, mouseY = -1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
XNextEvent(dpy, &e);
|
||||||
|
|
||||||
|
switch (e.type)
|
||||||
|
{
|
||||||
|
case ButtonPress:
|
||||||
|
case ButtonRelease:
|
||||||
|
if (e.xbutton.button != Button1) break;
|
||||||
|
int over = X11Button_Contains(&ok, mouseX, mouseY);
|
||||||
|
|
||||||
|
if (ok.Clicked && e.type == ButtonRelease) {
|
||||||
|
if (over) return;
|
||||||
|
}
|
||||||
|
ok.Clicked = e.type == ButtonPress && over;
|
||||||
|
/* fallthrough to redraw window */
|
||||||
|
|
||||||
|
case Expose:
|
||||||
|
case MapNotify:
|
||||||
|
XClearWindow(dpy, w->win);
|
||||||
|
X11Textbox_Draw(&body, w);
|
||||||
|
X11Button_Draw(&ok, w);
|
||||||
|
XFlush(dpy);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeyRelease:
|
||||||
|
if (XLookupKeysym(&e.xkey, 0) == XK_Escape) return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ClientMessage:
|
||||||
|
if (e.xclient.data.l[0] == wmDelete) return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionNotify:
|
||||||
|
mouseX = e.xmotion.x; mouseY = e.xmotion.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ErrorHandler_Init(const char* logFile) {
|
void ErrorHandler_Init(const char* logFile) {
|
||||||
/* TODO: Implement this */
|
/* TODO: Implement this */
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorHandler_ShowDialog(const char* title, const char* msg) {
|
void ErrorHandler_ShowDialog(const char* title, const char* msg) {
|
||||||
/* TODO: Implement this */
|
X11Window w = { 0 };
|
||||||
|
dpy = DisplayDevice_Meta[0];
|
||||||
|
|
||||||
|
X11_MessageBox(title, msg, &w);
|
||||||
|
X11Window_Free(&w);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorHandler_Backtrace(STRING_TRANSIENT String* str, void* ctx) {
|
void ErrorHandler_Backtrace(STRING_TRANSIENT String* str, void* ctx) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user