887 lines
23 KiB
C
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;
|
|
}
|
|
}
|
|
}
|