#define _MILSKO #define USE_X11 #include "milkwm.h" #include #include typedef struct mwm_hints { unsigned long flags; unsigned long functions; unsigned long decorations; long input_mode; unsigned long status; } mwm_hints_t; enum mwm_hints_enum { MWM_HINTS_FUNCTIONS = (1L << 0), MWM_HINTS_DECORATIONS = (1L << 1), MWM_FUNC_ALL = (1L << 0), MWM_FUNC_RESIZE = (1L << 1), MWM_FUNC_MOVE = (1L << 2), MWM_FUNC_MINIMIZE = (1L << 3), MWM_FUNC_MAXIMIZE = (1L << 4), MWM_FUNC_CLOSE = (1L << 5), MWM_DECOR_ALL = (1L << 0), MWM_DECOR_BORDER = (1L << 1), MWM_DECOR_RESIZEH = (1L << 2), MWM_DECOR_TITLE = (1L << 3), MWM_DECOR_MENU = (1L << 4), MWM_DECOR_MINIMIZE = (1L << 5), MWM_DECOR_MAXIMIZE = (1L << 6) }; Display* xdisplay; pthread_t xthread; pthread_mutex_t xmutex; static pthread_mutex_t focusmutex; static Atom milkwm_set_focus; static int red_max = 0, red_shift; static int green_max = 0, green_shift; static int blue_max = 0, blue_shift; typedef struct window { MwWidget frame; Window client; int working; } window_t; static window_t* windows = NULL; static Window* wnd = NULL; static void* old_handler; static int error_happened = 0; static int ignore_error(Display* disp, XErrorEvent* ev) { error_happened = 1; return 0; } static void begin_error(void) { old_handler = XSetErrorHandler(ignore_error); error_happened = 0; } static void end_error(void) { XSync(xdisplay, False); XSetErrorHandler(old_handler); } static void* x11_thread_routine(void* arg) { while(1) { pthread_mutex_lock(&xmutex); if(root != NULL) { pthread_mutex_unlock(&xmutex); break; } pthread_mutex_unlock(&xmutex); } loop_x(); XCloseDisplay(xdisplay); return NULL; } static Window focus = None, nofocus; static void set_focus(Window w) { int i; for(i = 0; i < arrlen(windows); i++) { pthread_mutex_lock(&focusmutex); if(windows[i].frame != NULL && windows[i].client == w) { pthread_mutex_unlock(&focusmutex); wm_focus(windows[i].frame, 1); } else if(windows[i].frame != NULL && focus != w && focus == windows[i].client) { pthread_mutex_unlock(&focusmutex); wm_focus(windows[i].frame, 0); } else { pthread_mutex_unlock(&focusmutex); } } pthread_mutex_lock(&focusmutex); focus = w; if(w == None) { XSetInputFocus(xdisplay, nofocus, RevertToParent, CurrentTime); } else { XSetInputFocus(xdisplay, w, RevertToParent, CurrentTime); } pthread_mutex_unlock(&focusmutex); } static unsigned long generate_color(int r, int g, int b) { if(red_max == 0) { XColor xc; xc.red = (int)r * 256; xc.green = (int)g * 256; xc.blue = (int)b * 256; XAllocColor(xdisplay, DefaultColormap(xdisplay, DefaultScreen(xdisplay)), &xc); return xc.pixel; } else { unsigned long px; px |= (r * red_max / 255) << red_shift; px |= (g * green_max / 255) << green_shift; px |= (b * blue_max / 255) << blue_shift; return px; } } static Pixmap render_image(Window wnd, const char* path, int tile, int fit) { Pixmap px; XWindowAttributes xwa; GC gc; int w, h; unsigned int ch; XImage* xi; int y, x; int pw, ph; int width, height; unsigned char* d = stbi_load(path, &w, &h, &ch, 3); if(d == NULL) return None; XGetWindowAttributes(xdisplay, wnd, &xwa); if(tile) { width = w; height = h; tile = 1; } else { double sx = (double)w / xwa.width; double sy = (double)h / xwa.height; if(fit ? (sx > sy) : (sx < sy)) { width = w / sx; height = h / sx; } else { width = w / sy; height = h / sy; } } px = XCreatePixmap(xdisplay, wnd, (pw = tile ? width : xwa.width), (ph = tile ? height : xwa.height), xwa.depth); gc = XCreateGC(xdisplay, px, 0, NULL); xi = XCreateImage(xdisplay, DefaultVisual(xdisplay, DefaultScreen(xdisplay)), xwa.depth, ZPixmap, 0, NULL, width, height, 32, 0); xi->data = malloc(xi->bytes_per_line * height); memset(xi->data, 0, xi->bytes_per_line * height); for(y = 0; y < height; y++) { for(x = 0; x < width; x++) { unsigned long px; int ix, iy; int ax, ay; unsigned char* rgb; ix = x * w / width; iy = y * h / height; rgb = &d[(iy * w + ix) * 3]; px = generate_color(rgb[0], rgb[1], rgb[2]); XPutPixel(xi, x, y, px); } } XPutImage(xdisplay, px, gc, xi, 0, 0, (pw - width) / 2, (ph - height) / 2, width, height); XDestroyImage(xi); XFreeGC(xdisplay, gc); free(d); return px; } /* this can be optimized by backends p much, so we do this in backend... */ void set_background_x(void) { const char* str = NULL; const char* type = NULL; xl_node_t* n; if(wm_config == NULL || wm_config->root == NULL) { n = NULL; } else { n = wm_config->root->first_child; } while(n != NULL) { if(n->type == XL_NODE_NODE && strcmp(n->name, "Wallpaper") == 0) { xl_attribute_t* a = n->first_attribute; while(a != NULL) { if(strcmp(a->key, "Type") == 0) type = a->value; a = a->next; } str = n->text; } n = n->next; } if(type != NULL && strcmp(type, "Solid") == 0 && str != NULL) { MwRGB rgb; XColor xc; MwParseColorNoAllocate(str, &rgb); xc.red = rgb.red * 256; xc.green = rgb.green * 256; xc.blue = rgb.blue * 256; XAllocColor(xdisplay, DefaultColormap(xdisplay, DefaultScreen(xdisplay)), &xc); XSetWindowBackground(xdisplay, DefaultRootWindow(xdisplay), xc.pixel); } else if(type != NULL && (strcmp(type, "Tile") == 0 || strcmp(type, "Fill") == 0 || strcmp(type, "Fit") == 0) && str != NULL) { XWindowAttributes xwa; Pixmap px; XGetWindowAttributes(xdisplay, DefaultRootWindow(xdisplay), &xwa); if((px = render_image(DefaultRootWindow(xdisplay), str, strcmp(type, "Tile") == 0 ? 1 : 0, strcmp(type, "Fit") == 0 ? 1 : 0)) != None) { XSetWindowBackgroundPixmap(xdisplay, DefaultRootWindow(xdisplay), px); XFreePixmap(xdisplay, px); } } XClearWindow(xdisplay, DefaultRootWindow(xdisplay)); XFlush(xdisplay); } int init_x(void) { void* old; XSetWindowAttributes xswa; Cursor cur; XVisualInfo xvi; XVisualInfo* pxvi; int n, i; xswa.override_redirect = True; xdisplay = XOpenDisplay(NULL); begin_error(); XSelectInput(xdisplay, DefaultRootWindow(xdisplay), SubstructureRedirectMask | SubstructureNotifyMask); end_error(); if(error_happened) return 1; nofocus = XCreateSimpleWindow(xdisplay, DefaultRootWindow(xdisplay), -10, -10, 1, 1, 0, 0, 0); XChangeWindowAttributes(xdisplay, nofocus, CWOverrideRedirect, &xswa); XMapWindow(xdisplay, nofocus); set_focus(None); XChangeProperty(xdisplay, DefaultRootWindow(xdisplay), XInternAtom(xdisplay, "_NET_SUPPORTING_WM_CHECK", False), XA_WINDOW, 32, PropModeReplace, (unsigned char*)&nofocus, 1); XChangeProperty(xdisplay, nofocus, XInternAtom(xdisplay, "_NET_SUPPORTING_WM_CHECK", False), XA_WINDOW, 32, PropModeReplace, (unsigned char*)&nofocus, 1); XChangeProperty(xdisplay, nofocus, XInternAtom(xdisplay, "_NET_WM_NAME", False), XInternAtom(xdisplay, "UTF8_STRING", False), 8, PropModeReplace, (unsigned char*)"milkwm", 6); cur = MwLLX11CreateCursor(xdisplay, &MwCursorDefault, &MwCursorDefaultMask); XDefineCursor(xdisplay, DefaultRootWindow(xdisplay), cur); XFreeCursor(xdisplay, cur); milkwm_set_focus = XInternAtom(xdisplay, "MILKWM_SET_FOCUS", False); xvi.visualid = XVisualIDFromVisual(DefaultVisual(xdisplay, DefaultScreen(xdisplay))); pxvi = XGetVisualInfo(xdisplay, VisualIDMask, &xvi, &n); i = 0; while(!((n << i) & pxvi->red_mask)) i++; red_max = pxvi->red_mask >> i; red_shift = i; i = 0; while(!((n << i) & pxvi->green_mask)) i++; green_max = pxvi->green_mask >> i; green_shift = i; i = 0; while(!((n << i) & pxvi->blue_mask)) i++; blue_max = pxvi->blue_mask >> i; blue_shift = i; set_background_x(); pthread_mutex_init(&xmutex, NULL); pthread_mutex_init(&focusmutex, NULL); pthread_create(&xthread, NULL, x11_thread_routine, NULL); return 0; } static void save(Window w) { XGrabButton(xdisplay, 1, 0, w, True, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeSync, GrabModeAsync, None, None); XSelectInput(xdisplay, w, StructureNotifyMask | FocusChangeMask | PropertyChangeMask); XSetWindowBorderWidth(xdisplay, w, 0); XAddToSaveSet(xdisplay, w); } static int parent_eq(Window wnd, Window might_be_parent) { Window root, parent; Window* children; unsigned int nchildren; XQueryTree(xdisplay, wnd, &root, &parent, &children, &nchildren); if(children != NULL) XFree(children); if(parent == might_be_parent) return 1; if(parent != root) return parent_eq(parent, might_be_parent); return 0; } static void set_stuff(Window wnd) { Atom type; int format; unsigned long nitem, after; unsigned char* buf = NULL; Atom atom_name = XInternAtom(xdisplay, "WM_NAME", False); Atom atom_icon = XInternAtom(xdisplay, "_NET_WM_ICON", False); Atom utf8 = XInternAtom(xdisplay, "UTF8_STRING", False); int i; for(i = 0; i < arrlen(windows); i++) { if(windows[i].client == wnd) { XClassHint* ch = XAllocClassHint(); int def = 1; int l = 0; int wid = 2048, hei = 2048; unsigned char* data = NULL; while(1) { l += 2; if(def && XGetWindowProperty(xdisplay, wnd, atom_icon, l - 2, 2, False, XA_CARDINAL, &type, &format, &nitem, &after, &buf) == Success && buf != NULL) { if(nitem == 2) { long* b = (long*)buf; int w = b[0], h = b[1]; l += w * h; XFree(buf); if(w <= 1024 && h <= 1024) { if(w == 16 || h == 16 || w < wid || h < hei) { if(data != NULL) free(data); data = malloc(4 * w * h); if(XGetWindowProperty(xdisplay, wnd, atom_icon, l - w * h, w * h, False, XA_CARDINAL, &type, &format, &nitem, &after, &buf) == Success && buf != NULL) { if(nitem == w * h) { int i; b = (long*)buf; for(i = 0; i < w * h; i++) { data[4 * i + 0] = (b[i] >> 16) & 0xff; data[4 * i + 1] = (b[i] >> 8) & 0xff; data[4 * i + 2] = (b[i] >> 0) & 0xff; data[4 * i + 3] = (b[i] >> 24) & 0xff; } } XFree(buf); } wid = w; hei = h; } } } else { XFree(buf); break; } } else { break; } } if(def && data != NULL) { wm_set_icon(windows[i].frame, data, wid, hei); free(data); def = 0; } if(def && XGetClassHint(xdisplay, wnd, ch)) { if(wm_set_icon_by_name(windows[i].frame, NULL, ch->res_name)) def = 0; } XFree(ch); if(def) { wm_set_icon_by_name(windows[i].frame, NULL, "unknown"); } if(XGetWindowProperty(xdisplay, wnd, atom_name, 0, 1024, False, AnyPropertyType, &type, &format, &nitem, &after, &buf) == Success && buf != NULL) { if(type == XA_STRING || type == utf8) wm_set_name(windows[i].frame, buf); XFree(buf); } } } } static int is_no_border(Window window) { Atom atom = XInternAtom(xdisplay, "_MOTIF_WM_HINTS", False); Atom type; int format; unsigned long nitem, after; unsigned char* buf = NULL; mwm_hints_t* mwm; if(XGetWindowProperty(xdisplay, window, atom, 0, sizeof(*mwm) / sizeof(long), False, AnyPropertyType, &type, &format, &nitem, &after, &buf) == Success && buf != NULL) { if(type == atom && nitem == (sizeof(*mwm) / sizeof(long))) { mwm = (mwm_hints_t*)buf; if(mwm->flags & MWM_HINTS_DECORATIONS) { if(mwm->decorations == 0) { XFree(buf); return 1; } } else { XFree(buf); return 1; } } XFree(buf); } return 0; } static void bring_up(void) { int i; for(i = 0; i < arrlen(wnd); i++) { XWindowAttributes xwa; XGetWindowAttributes(xdisplay, wnd[i], &xwa); if(xwa.override_redirect) XRaiseWindow(xdisplay, wnd[i]); } } void loop_x(void) { XEvent ev; Window root, parent; Window* children; unsigned int nchildren; int i; begin_error(); XQueryTree(xdisplay, DefaultRootWindow(xdisplay), &root, &parent, &children, &nchildren); if(children != NULL) { for(i = 0; i < nchildren; i++) { window_t w; XWindowAttributes xwa; if(XGetWindowAttributes(xdisplay, children[i], &xwa)) { if(xwa.override_redirect || xwa.map_state != IsViewable || is_no_border(children[i])) continue; w.frame = wm_frame(xwa.width, xwa.height); w.client = children[i]; XUnmapWindow(xdisplay, children[i]); save(w.client); arrput(windows, w); set_stuff(w.client); } } XFree(children); } while(1) { XNextEvent(xdisplay, &ev); if((ev.type == ButtonPress || ev.type == ButtonRelease) && ev.xbutton.button == Button1) { Window w = None, t = ev.xbutton.window; if(ev.xbutton.subwindow != None) t = ev.xbutton.subwindow; if(ev.type == ButtonPress) { for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame != NULL && (windows[i].frame->lowlevel->x11.window == t || parent_eq(t, windows[i].frame->lowlevel->x11.window))) { set_focus(windows[i].client); w = windows[i].frame->lowlevel->x11.window; break; } } } if(w == None) { XRaiseWindow(xdisplay, t); set_focus(t); } else { XRaiseWindow(xdisplay, w); } bring_up(); XAllowEvents(xdisplay, ReplayPointer, CurrentTime); } else if(ev.type == MotionNotify && ev.xmotion.subwindow != None) { Window w = None, t = ev.xmotion.window; if(ev.xmotion.subwindow != None) t = ev.xmotion.subwindow; for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame->lowlevel->x11.window == t || parent_eq(t, windows[i].frame->lowlevel->x11.window)) { w = windows[i].frame->lowlevel->x11.window; break; } } if(w != None) { XRaiseWindow(xdisplay, w); bring_up(); } XAllowEvents(xdisplay, ReplayPointer, CurrentTime); } else if(ev.type == FocusIn && ev.xfocus.window != focus) { set_focus(focus); } else if(ev.type == ConfigureRequest) { XWindowChanges xwc; int i; Window w = ev.xconfigurerequest.window; for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame != NULL && windows[i].frame->lowlevel->x11.window == w) { XConfigureEvent ev2; xwc.x = wm_content_x(); xwc.y = wm_content_y(); xwc.width = wm_content_width(ev.xconfigurerequest.width); xwc.height = wm_content_height(ev.xconfigurerequest.height); XConfigureWindow(xdisplay, windows[i].client, CWX | CWY | CWWidth | CWHeight, &xwc); ev2.type = ConfigureNotify; ev2.display = xdisplay; ev2.event = windows[i].client; ev2.x = ev.xconfigurerequest.x + wm_content_x(); ev2.y = ev.xconfigurerequest.y + wm_content_y(); ev2.width = xwc.width; ev2.height = xwc.height; XSendEvent(xdisplay, windows[i].client, False, StructureNotifyMask, (XEvent*)&ev2); break; } } xwc.x = ev.xconfigurerequest.x; xwc.y = ev.xconfigurerequest.y; xwc.width = ev.xconfigurerequest.width; xwc.height = ev.xconfigurerequest.height; XConfigureWindow(xdisplay, w, ev.xconfigurerequest.value_mask, &xwc); } else if(ev.type == ConfigureNotify) { int i; for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame != NULL && windows[i].client == ev.xconfigure.window) { XWindowChanges xwc; XWindowAttributes xwa; xwc.x = ev.xconfigure.x; xwc.y = ev.xconfigure.y; xwc.width = wm_entire_width(ev.xconfigure.width); xwc.height = wm_entire_height(ev.xconfigure.height); XGetWindowAttributes(xdisplay, windows[i].frame->lowlevel->x11.window, &xwa); if(xwc.x != wm_content_x() || xwc.y != wm_content_y()) { XConfigureWindow(xdisplay, windows[i].frame->lowlevel->x11.window, CWX | CWY | CWWidth | CWHeight, &xwc); xwc.x = wm_content_x(); xwc.y = wm_content_y(); XConfigureWindow(xdisplay, windows[i].client, CWX | CWY, &xwc); } else if(xwa.width != xwc.width || xwa.height != xwc.height) { XConfigureWindow(xdisplay, windows[i].frame->lowlevel->x11.window, CWWidth | CWHeight, &xwc); } break; } } } else if(ev.type == CreateNotify) { XWindowAttributes xwa; if(!ev.xcreatewindow.override_redirect) { XGetWindowAttributes(xdisplay, DefaultRootWindow(xdisplay), &xwa); XMoveWindow(xdisplay, ev.xcreatewindow.window, rand() % (xwa.width - wm_entire_width(ev.xcreatewindow.width)), rand() % (xwa.height - wm_entire_height(ev.xcreatewindow.height))); } } else if(ev.type == DestroyNotify) { for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame != NULL && windows[i].client == ev.xdestroywindow.window) { wm_destroy(windows[i].frame); arrdel(windows, i); break; } } for(i = 0; i < arrlen(wnd); i++) { if(wnd[i] == ev.xdestroywindow.window) { arrdel(wnd, i); break; } } } else if(ev.type == MapRequest) { window_t w; int i; int ret = 0; XWindowAttributes xwa, xwar; XConfigureEvent ev2; int mx, my; if(!XGetWindowAttributes(xdisplay, ev.xmaprequest.window, &xwa)) continue; /* ?? ? ? ???? ?? ? ? */ if(xwa.override_redirect || is_no_border(ev.xmaprequest.window)) { w.frame = NULL; w.client = ev.xmaprequest.window; w.working = 1; arrput(windows, w); XMapWindow(xdisplay, ev.xmaprequest.window); if(xwa.override_redirect) { arrput(wnd, ev.xmaprequest.window); save(ev.xmaprequest.window); } continue; } for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame != NULL && windows[i].frame->lowlevel->x11.window == ev.xmaprequest.window) break; if(windows[i].client == ev.xmaprequest.window) { /* what? */ ret = 1; break; } } if(ret) continue; if(i != arrlen(windows)) { XWindowAttributes xwa; int rev; Window cfocus; if(!XGetWindowAttributes(xdisplay, windows[i].client, &xwa)) { wm_destroy(windows[i].frame); arrdel(windows, i); continue; } if(xwa.override_redirect) { wm_destroy(windows[i].frame); arrdel(windows, i); continue; } ev2.type = ConfigureNotify; ev2.display = xdisplay; ev2.event = windows[i].client; ev2.x = xwa.x + wm_content_x(); ev2.y = xwa.y + wm_content_y(); ev2.width = xwa.width; ev2.height = xwa.height; XSendEvent(xdisplay, windows[i].client, False, StructureNotifyMask, (XEvent*)&ev2); XMoveWindow(xdisplay, ev.xmaprequest.window, xwa.x, xwa.y); XMapWindow(xdisplay, ev.xmaprequest.window); do { XFlush(xdisplay); XGetWindowAttributes(xdisplay, ev.xmaprequest.window, &xwa); } while(xwa.map_state != IsViewable); XGrabServer(xdisplay); if(!XReparentWindow(xdisplay, windows[i].client, wm_get_inside(windows[i].frame)->lowlevel->x11.window, wm_content_x(), wm_content_y())) { wm_destroy(windows[i].frame); arrdel(windows, i); continue; } XUngrabServer(xdisplay); XMapWindow(xdisplay, windows[i].client); set_focus(windows[i].client); continue; } w.frame = wm_frame(xwa.width, xwa.height); w.client = ev.xmaprequest.window; w.working = 1; save(w.client); if(ret) { wm_destroy(w.frame); } else { arrput(windows, w); set_stuff(w.client); } XGetWindowAttributes(xdisplay, DefaultRootWindow(xdisplay), &xwar); mx = (xwar.width - wm_entire_width(xwa.width)); my = (xwar.height - wm_entire_height(xwa.height) - 46); for(i = 0; i < arrlen(wnd); i++) { if(wnd[i] == w.client) break; } if(i == arrlen(wnd)) { XMoveWindow(xdisplay, w.client, mx == 0 ? 0 : (rand() % mx), my == 0 ? 0 : (rand() % my)); arrput(wnd, w.client); } } else if(ev.type == PropertyNotify) { int i; for(i = 0; i < arrlen(windows); i++) { if(windows[i].client == ev.xproperty.window) { break; } } if(arrlen(windows) == i) continue; if(ev.xproperty.atom == XInternAtom(xdisplay, "WM_NAME", False) || ev.xproperty.atom == XInternAtom(xdisplay, "WM_CLASS", False) || ev.xproperty.atom == XInternAtom(xdisplay, "_NET_WM_ICON", False)) { set_stuff(windows[i].client); } } else if(ev.type == MapNotify) { int i; XWindowAttributes xwa; if(!XGetWindowAttributes(xdisplay, ev.xmap.window, &xwa)) continue; if(xwa.override_redirect) { for(i = 0; i < arrlen(wnd); i++) { if(wnd[i] == ev.xmap.window) { break; } } if(i == arrlen(wnd)) arrput(wnd, ev.xmap.window); continue; } for(i = 0; i < arrlen(windows); i++) { if(windows[i].client == ev.xmap.window) { windows[i].working = 0; break; } else if(windows[i].frame != NULL && (windows[i].frame->lowlevel->x11.window == ev.xmap.window || nofocus == ev.xmap.window)) { break; } } if(i == arrlen(windows)) { window_t w; XWindowAttributes xwa; XGetWindowAttributes(xdisplay, ev.xmap.window, &xwa); w.frame = wm_frame(xwa.width, xwa.height); w.client = ev.xmap.window; w.working = 1; save(w.client); arrput(windows, w); } } else if(ev.type == UnmapNotify) { int i; int nvm = 0; for(i = 0; i < arrlen(windows); i++) { if(windows[i].client == ev.xunmap.window) { XWindowAttributes xwa; int x, y; if(XGetWindowAttributes(xdisplay, windows[i].client, &xwa)) { if(xwa.map_state == IsViewable) { nvm = 1; break; } } else if(windows[i].working) { nvm = 1; break; } x = xwa.x; y = xwa.y; if(windows[i].frame != NULL) { if(XGetWindowAttributes(xdisplay, windows[i].frame->lowlevel->x11.window, &xwa)) { XReparentWindow(xdisplay, windows[i].client, DefaultRootWindow(xdisplay), xwa.x + x, xwa.y + y); } wm_destroy(windows[i].frame); } arrdel(windows, i); break; } } if(nvm) continue; if(focus == ev.xunmap.window) { Window w = None; int i; for(i = arrlen(windows) - 1; i >= 0; i--) { XWindowAttributes xwa; XGetWindowAttributes(xdisplay, windows[i].client, &xwa); if(xwa.map_state == IsViewable) { w = windows[i].client; } } set_focus(w); } } else if(ev.type == ClientMessage) { if(ev.xclient.message_type == XInternAtom(xdisplay, "WM_PROTOCOLS", False) && ev.xclient.data.l[0] == milkwm_set_focus) { set_focus(ev.xclient.data.l[1]); } } } end_error(); arrfree(wnd); } void set_focus_x(MwWidget widget) { int i; for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame == widget) { XEvent ev; ev.type = ClientMessage; ev.xclient.window = nofocus; ev.xclient.message_type = XInternAtom(xdisplay, "WM_PROTOCOLS", False); ev.xclient.format = 32; ev.xclient.data.l[0] = milkwm_set_focus; ev.xclient.data.l[1] = windows[i].client; XSendEvent(xdisplay, nofocus, False, 0, &ev); XRaiseWindow(xdisplay, windows[i].frame->lowlevel->x11.window); bring_up(); break; } } } void delete_x(MwWidget widget) { int i; for(i = 0; i < arrlen(windows); i++) { if(windows[i].frame == widget) { XEvent ev; ev.type = ClientMessage; ev.xclient.window = windows[i].client; ev.xclient.message_type = XInternAtom(xdisplay, "WM_PROTOCOLS", False); ev.xclient.format = 32; ev.xclient.data.l[0] = XInternAtom(xdisplay, "WM_DELETE_WINDOW", False); ev.xclient.data.l[1] = CurrentTime; XSendEvent(xdisplay, windows[i].client, False, 0, &ev); break; } } }