mdebase/milkwm/xserver.c
2026-04-12 08:03:59 +09:00

887 lines
23 KiB
C

#define _MILSKO
#define USE_X11
#include "milkwm.h"
#include <stb_ds.h>
#include <stb_image.h>
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;
}
}
}