From eb007e91cd56fe9132cae80bc3afa1a83f9e0b17 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 9 Sep 2018 13:44:18 +1000 Subject: [PATCH] C client: Implement error dialog on linux --- src/ErrorHandler.c | 232 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 231 insertions(+), 1 deletion(-) diff --git a/src/ErrorHandler.c b/src/ErrorHandler.c index 66866a219..6b18a982d 100644 --- a/src/ErrorHandler.c +++ b/src/ErrorHandler.c @@ -2,6 +2,8 @@ #include "Platform.h" #include "Chat.h" #include "Window.h" +#include "Funcs.h" + 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_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"); } #elif CC_BUILD_NIX +#include +#include +#include +#include + +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) { /* TODO: Implement this */ } 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) {