#if true != True #error "true != Xlib's True" #endif #if false != False #error "false != Xlib's False" #endif #define RGB(r, g, b) (((r) << 16) | ((g) << 8) | (b)) static const char icon_tga[] = { #include "../resources/icon.tga.h" }; typedef struct xctx { Display* disp; int scrn; /* X screen */ Window root; Window wnd; GC gc; /* Not sure if I wanna keep just one GC, but works ok on modern linux */ XFontStruct* xfs; XrmDatabase xrdb; Atom xa_delwnd; union { struct { u32 fg; u32 bg; u32 col[16]; } as; u32 arr[18]; } cols; } xctx; static int x11_err_handler(Display* disp, XErrorEvent* eev) { char buf[512] = {0}; assert(disp); assert(eev); XGetErrorText(disp, eev->error_code, buf, sizeof(buf) - 1); report(ERR, "X11: '%s' resource - %X; opcode %d(%d)\n", buf, eev->resourceid, eev->request_code, eev->minor_code); return 0; } static char ascii_lower(char c) { if(c >= 'A' && c <= 'Z') return c + ('a' - 'A'); else return c; } /* TODO: write tests maybe? */ /* TODO: parse #rgb */ /* TODO: parse X11 color names */ static long parse_color(const char* s, long len) { long col = 0; if(!s || len == 0) { report(WARN, "parse_color: invalid input"); goto fail; } if(s[0] == '#') { long i; if(len < 7) { goto fail; report(WARN, "parse_color: bad length"); } for(i = 1; i < len; i++) { char c = ascii_lower(s[i]); char r = 0; if(c >= '0' && c <= '9') r = c - '0'; else if(c >= 'a' && c <= 'f') r = c - 'a' + 10; else { goto fail; report(WARN, "parse_color: bad hexcode"); } col |= r << (20 - (i - 1)*4); } } else goto fail; return col; fail: return RGB(255, 0, 255); } static void x11_init(int width, int height, char* title, int argc, char** argv, xctx* ctx) { if(!ctx) report(FATAL, "x11_init: ctx == NULL"); ctx->disp = XOpenDisplay(NULL); if(!ctx->disp) report(FATAL, "XOpenDisplay"); XSetErrorHandler(x11_err_handler); ctx->scrn = DefaultScreen(ctx->disp); ctx->root = RootWindow(ctx->disp, ctx->scrn); { int xver = 0; int xrev = 0; int xrel = 0; char* xvendor = NULL; xver = XProtocolVersion(ctx->disp); xrev = XProtocolRevision(ctx->disp); xvendor = XServerVendor(ctx->disp); xrel = XVendorRelease(ctx->disp); report(INFO, "X11: Connected to X server '%s' %d\n", xvendor, xrel); report(INFO, "X11: Protocol ver %d.%d\n", xver, xrev); report(INFO, "X11: Screen %d %dx%d\n", ctx->scrn, DisplayWidth(ctx->disp, ctx->scrn), DisplayHeight(ctx->disp, ctx->scrn)); } /* TODO: make cfg_load() or something that loads the colorscheme */ { int i = 0; char* rcmgr = NULL; const long defcols[16] = { RGB(64,64,64), RGB(128,0,0), RGB(0,128,0), RGB(128,128,0), RGB(0,0,128), RGB(128,0,128), RGB(0,128,128), RGB(220,220,220), RGB(210,210,210), RGB(255,0,0), RGB(0,255,0), RGB(255,255,0), RGB(0,0,255), RGB(255,0,255), RGB(0,255,255), RGB(255,255,255) }; ctx->cols.as.fg = RGB(0, 0, 0); ctx->cols.as.bg = RGB(255, 255, 255); for(i = 0; i < 16; i++) ctx->cols.arr[i+2] = defcols[i]; XrmInitialize(); rcmgr = XResourceManagerString(ctx->disp); if(rcmgr && (ctx->xrdb = XrmGetStringDatabase(rcmgr))) { XrmValue val = {0}; char* type = NULL; if(XrmGetResource(ctx->xrdb, "foreground", "Foreground", &type, &val)) ctx->cols.as.fg = parse_color(val.addr, val.size - 1); if(XrmGetResource(ctx->xrdb, "background", "Background", &type, &val)) ctx->cols.as.bg = parse_color(val.addr, val.size - 1); for(i = 0; i < 16; i++) { char name[8] = {0}; char class[8] = {0}; snprintf(name, 8, "color%d", i); strcpy(class, name); class[0] = 'C'; if(XrmGetResource(ctx->xrdb, name, class, &type, &val)) ctx->cols.arr[i+2] = parse_color(val.addr, val.size - 1); } } else report(WARN, "X11: failed to load X Resources database\n"); } ctx->wnd = XCreateSimpleWindow(ctx->disp, ctx->root, 0, 0, width, height, 0, BlackPixel(ctx->disp, ctx->scrn), WhitePixel(ctx->disp, ctx->scrn)); XSelectInput(ctx->disp, ctx->wnd, KeyPressMask | KeyReleaseMask | KeymapStateMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | ExposureMask | StructureNotifyMask ); { Atom xa_wnd_type = {0}; Atom xa_wnd_types[3] = {0}; Atom xa_icon = {0}; XClassHint class = {0}; XSizeHints size = {0}; texture icon_tex = {0}; ssize icon_sz = 0; long* icon = NULL; char buf[512] = {0}; ctx->xa_delwnd = XInternAtom(ctx->disp, "WM_DELETE_WINDOW", false); xa_wnd_type = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE", false); xa_wnd_types[0] = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE_UTILITY", false); xa_wnd_types[1] = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE_DIALOG", false); xa_wnd_types[2] = XInternAtom(ctx->disp, "_NET_WM_WINDOW_TYPE_NORMAL", false); xa_icon = XInternAtom(ctx->disp, "_NET_WM_ICON", false); class.res_name = title; /* FIXME: add class arg */ snprintf(buf, sizeof(buf) - 1, "digital.cabin.%s", title); class.res_class = buf; size.flags = PSize | PMinSize | PMaxSize; size.width = size.min_width = size.max_width = width; size.height = size.min_height = size.max_height = height; Xutf8SetWMProperties(ctx->disp, ctx->wnd, title, class.res_name, argv, argc, &size, NULL, &class); XSetWMProtocols(ctx->disp, ctx->wnd, &ctx->xa_delwnd, 1); XChangeProperty(ctx->disp, ctx->wnd, xa_wnd_type, XA_ATOM, 32, PropModeReplace, (u8*)xa_wnd_types, lengthof(xa_wnd_types)); /* FIXME: add u8[] icon arg */ if(tga2tex_from_mem(&icon_tex, (u8*)icon_tga)) { icon_sz = 2 + icon_tex.width * icon_tex.height; icon = malloc(sizeof(long) * icon_sz); if(icon) { u32 x, y, y2; icon[0] = icon_tex.width; icon[1] = icon_tex.height; for(y = 0, y2 = icon_tex.height - 1; y < icon_tex.height; y++, y2--) for(x = 0; x < icon_tex.width; x++) (icon + 2)[x + icon_tex.height * y] = icon_tex.texels[x + icon_tex.height * y2].rgba; XChangeProperty(ctx->disp, ctx->wnd, xa_icon, XA_CARDINAL, 32, PropModeReplace, (u8*)icon, icon_sz); free(icon); } else report(ERR, "Could not allocate X ICON temporary buffer"); free(icon_tex.texels); } else report(ERR, "Could not read embedded icon.tga"); } ctx->gc = XCreateGC(ctx->disp, ctx->wnd, 0, NULL); ctx->xfs = XQueryFont(ctx->disp, XGContextFromGC(ctx->gc)); /* TODO: better font support */ XClearWindow(ctx->disp, ctx->wnd); XMapWindow(ctx->disp, ctx->wnd); } static void x11_cleanup(xctx* ctx) { if(!ctx) return; XSetCloseDownMode(ctx->disp, DestroyAll); XFreeFontInfo(NULL, ctx->xfs, 1); XFreeGC(ctx->disp, ctx->gc); XDestroyWindow(ctx->disp, ctx->wnd); XCloseDisplay(ctx->disp); }