#include "render/render.h" #include "render/theme.h" #include #include #include #ifdef HAVE_SIGNAL_H # include #endif #ifdef HAVE_SYS_SELECT_H # include #endif #define TITLE_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \ ButtonMotionMask) #define ROOT_EVENT_MASK (PropertyChangeMask | StructureNotifyMask | \ SubstructureNotifyMask) #define SLITAPP_EVENT_MASK (StructureNotifyMask) Display *ob_display; Window ob_root; int ob_screen; static struct Slit { Window frame; Window title; /* user-requested position stuff */ int gravity; int user_x, user_y; /* actual position (when not auto-hidden) */ int x, y; int w, h; gboolean horz; Appearance *a_frame; Appearance *a_title; GList *slit_apps; } *slit; static int nslits; struct SlitApp { Window icon_win; Window win; int x; int y; int w; int h; }; static Atom atom_atom; static Atom atom_card; static Atom atom_theme; static Atom atom_type; static Atom atom_type_dock; static Atom atom_desktop; static Atom atom_state; static Atom atom_strut; static gboolean quit = FALSE; static gboolean reconfig = FALSE; void slit_read_theme(); void slit_configure(); void event_handle(XEvent *e); void slit_add_existing(); void slit_add_app(Window win); void slit_remove_app(struct Slit *slit, struct SlitApp *app,gboolean reparent); void sighandler(int signal) { if (signal == SIGUSR1) reconfig =TRUE; else quit = TRUE; } int xerrorhandler(Display *d, XErrorEvent *e) { char errtxt[128]; XGetErrorText(d, e->error_code, errtxt, 127); g_error("X Error: %s", errtxt); return 0; } int main() { int i; guint desk = 0xffffffff; XEvent e; XSetWindowAttributes attrib; struct sigaction action; sigset_t sigset; int xfd; fd_set selset; /* set up signal handler */ sigemptyset(&sigset); action.sa_handler = sighandler; action.sa_mask = sigset; action.sa_flags = SA_NOCLDSTOP; sigaction(SIGUSR1, &action, (struct sigaction *) NULL); sigaction(SIGPIPE, &action, (struct sigaction *) NULL); sigaction(SIGSEGV, &action, (struct sigaction *) NULL); sigaction(SIGFPE, &action, (struct sigaction *) NULL); sigaction(SIGTERM, &action, (struct sigaction *) NULL); sigaction(SIGINT, &action, (struct sigaction *) NULL); sigaction(SIGHUP, &action, (struct sigaction *) NULL); ob_display = XOpenDisplay(NULL); ob_screen = DefaultScreen(ob_display); ob_root = RootWindow(ob_display, ob_screen); XSetErrorHandler(xerrorhandler); render_startup(); theme_startup(); atom_atom = XInternAtom(ob_display, "ATOM", False); atom_card = XInternAtom(ob_display, "CARDINAL", False); atom_theme = XInternAtom(ob_display, "_OPENBOX_THEME", False); atom_type = XInternAtom(ob_display, "_NET_WM_WINDOW_TYPE", False); atom_type_dock = XInternAtom(ob_display, "_NET_WM_WINDOW_TYPE_DOCK",False); atom_desktop =XInternAtom(ob_display, "_NET_WM_DESKTOP", False); atom_state = XInternAtom(ob_display, "WM_STATE", False); atom_strut = XInternAtom(ob_display, "_NET_WM_STRUT", False); nslits = 1; slit = g_new0(struct Slit, nslits); for (i = 0; i < nslits; ++i) { slit[i].horz = TRUE; slit[i].frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0, render_depth, InputOutput, render_visual, 0, NULL); attrib.event_mask = TITLE_EVENT_MASK; slit[i].title = XCreateWindow(ob_display, slit[i].frame, 0, 0, 1, 1, 0, render_depth, InputOutput, render_visual, CWEventMask, &attrib); XMapWindow(ob_display, slit[i].title); XChangeProperty(ob_display, slit[i].frame, atom_type, atom_atom, 32, PropModeReplace, (guchar*)&atom_type_dock, 1); XChangeProperty(ob_display, slit[i].frame, atom_desktop, atom_card, 32, PropModeReplace, (guchar*)&desk, 1); } slit_read_theme(); XSelectInput(ob_display, ob_root, ROOT_EVENT_MASK); slit_add_existing(); xfd = ConnectionNumber(ob_display); FD_ZERO(&selset); FD_SET(xfd, &selset); while (!quit) { gboolean hadevent = FALSE; while (XPending(ob_display)) { XNextEvent(ob_display, &e); event_handle(&e); hadevent = TRUE; } if (!hadevent) { if (reconfig) slit_read_theme(); if (!quit) select(xfd + 1, &selset, NULL, NULL, NULL); } } for (i = 0; i < nslits; ++i) { while (slit[i].slit_apps) slit_remove_app(&slit[i], slit[i].slit_apps->data, TRUE); XDestroyWindow(ob_display, slit[i].title); XDestroyWindow(ob_display, slit[i].frame); appearance_free(slit[i].a_frame); appearance_free(slit[i].a_title); } theme_shutdown(); render_shutdown(); XCloseDisplay(ob_display); return 0; } Window find_client(Window win) { Window r, *children; unsigned int n, i; Atom ret_type; int ret_format; unsigned long ret_items, ret_bytesleft; unsigned long *prop_return; XQueryTree(ob_display, win, &r, &r, &children, &n); for (i = 0; i < n; ++i) { Window w = find_client(children[i]); if (w) return w; } /* try me */ XGetWindowProperty(ob_display, win, atom_state, 0, 1, False, atom_state, &ret_type, &ret_format, &ret_items, &ret_bytesleft, (unsigned char**) &prop_return); if (ret_type == None || ret_items < 1) return None; return win; /* found it! */ } void event_handle(XEvent *e) { int i; Window win; static guint button = 0; int sw, sh; int xpos, ypos; int x, y, g; switch (e->type) { case ButtonPress: if (!button) { button = e->xbutton.button; } break; case ButtonRelease: if (button == e->xbutton.button) button = 0; break; case MotionNotify: if (button == 1) { for (i = 0; i < nslits; ++i) if (slit[i].title == e->xmotion.window) { /* pick a corner and move it */ sw = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen)); sh = HeightOfScreen(ScreenOfDisplay(ob_display,ob_screen)); if (e->xmotion.x_root < sw / 3) /* left edge */ xpos = 0; else if (e->xmotion.x_root < sw / 3 * 2) /* middle */ xpos = 1; else /* right edge */ xpos = 2; if (e->xmotion.y_root < sh / 3) /* top edge */ ypos = 0; else if (e->xmotion.y_root < sh / 3 * 2) /* middle */ ypos = 1; else /* bottom edge */ ypos = 2; if (xpos == 1 && ypos == 1) return; /* cant go in middle middle */ if (xpos == 0) { if (ypos == 0) { x = 0; y = 0; g = NorthWestGravity; } else if (ypos == 1) { x = 0; y = sh / 2; g = WestGravity; } else { x = 0; y = sh; g = SouthWestGravity; } } else if (xpos == 1) { if (ypos == 0) { x = sw / 2; y = 0; g = NorthGravity; } else { x = sw / 2; y = sh; g = SouthGravity; } } else { if (ypos == 0) { x = sw; y = 0; g = NorthEastGravity; } else if (ypos == 1) { x = sw; y = sh / 2; g = EastGravity; } else { x = sw; y = sh; g = SouthEastGravity; } } if (x != slit[i].x || y != slit[i].y || g != slit[i].gravity) { slit[i].user_x = x; slit[i].user_y = y; slit[i].gravity = g; slit_configure(); } } } break; case PropertyNotify: g_message("PropertyNotify on 0x%lx", e->xproperty.window); if (e->xproperty.window == ob_root) { if (e->xproperty.atom == atom_theme) slit_read_theme(); } break; case ConfigureNotify: g_message("ConfigureNotify on 0x%lx", e->xconfigure.window); if (e->xconfigure.window == ob_root) { slit_configure(); return; } /* an owned slitapp? */ for (i = 0; i < nslits; ++i) { GList *it; for (it = slit[i].slit_apps; it; it = it->next) { struct SlitApp *app = it->data; if (e->xconfigure.window == app->icon_win) { if (app->w != e->xconfigure.width || app->h != e->xconfigure.height) { g_message("w %d h %d w %d h %d", app->w, e->xconfigure.width, app->h, e->xconfigure.height); app->w = e->xconfigure.width; app->h = e->xconfigure.height; slit_configure(); } return; } } } break; case MapNotify: g_message("MapNotify on 0x%lx", e->xmap.window); win = find_client(e->xmap.window); if (!win) return; for (i = 0; i < nslits; ++i) if (win == slit[i].frame) return; slit_add_app(win); break; case UnmapNotify: g_message("UnmapNotify on 0x%lx", e->xunmap.window); for (i = 0; i < nslits; ++i) { GList *it; for (it = slit[i].slit_apps; it; it = it->next) { struct SlitApp *app = it->data; if (e->xunmap.window == app->icon_win) { gboolean r; XEvent e; r = !XCheckTypedWindowEvent(ob_display, app->icon_win, DestroyNotify, &e); if (r) { if (XCheckTypedWindowEvent(ob_display, app->icon_win, ReparentNotify, &e)) { XPutBackEvent(ob_display, &e); r = FALSE; } } slit_remove_app(&slit[i], app, r); break; } } } break; case ReparentNotify: g_message("ReparentNotify on 0x%lx", e->xdestroywindow.window); for (i = 0; i < nslits; ++i) { GList *it; for (it = slit[i].slit_apps; it; it = it->next) { struct SlitApp *app = it->data; if (e->xdestroywindow.window == app->icon_win) { slit_remove_app(&slit[i], app, FALSE); break; } } } case DestroyNotify: g_message("DestroyNotify on 0x%lx", e->xdestroywindow.window); for (i = 0; i < nslits; ++i) { GList *it; for (it = slit[i].slit_apps; it; it = it->next) { struct SlitApp *app = it->data; if (e->xdestroywindow.window == app->icon_win) { slit_remove_app(&slit[i], app, FALSE); break; } } } break; } } void slit_add_existing() { unsigned int i, nchild; int j; Window w, *children; XWindowAttributes attrib; XQueryTree(ob_display, ob_root, &w, &w, &children, &nchild); for (i = 0; i < nchild; ++i) { for (j = 0; j < nslits; ++j) if (children[i] == slit[j].frame) continue; if (children[i] == None) continue; if ((children[i] = find_client(children[i])) == None) continue; if (XGetWindowAttributes(ob_display, children[i], &attrib)) { if (attrib.override_redirect) continue; slit_add_app(children[i]); } } XFree(children); } void slit_add_app(Window win) { int i; XWMHints *h; XWindowAttributes attrib; struct Slit *s; s = &slit[0]; if ((h = XGetWMHints(ob_display, win))) { if (h->flags & StateHint && h->initial_state == WithdrawnState) { struct SlitApp *app = g_new(struct SlitApp, 1); app->win = win; app->icon_win = (h->flags & IconWindowHint) ? h->icon_window : win; XFree(h); for (i = 0; i < nslits; ++i) { GList *it; for (it = slit[i].slit_apps; it; it = it->next) if (app->icon_win == ((struct SlitApp*)it->data)->icon_win) /* already managed! */ return; } if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) { app->w = attrib.width; app->h = attrib.height; } else { g_free(app); app = NULL; } if (app) { s->slit_apps = g_list_append(s->slit_apps, app); slit_configure(); XReparentWindow(ob_display, app->icon_win, s->frame, app->x, app->y); /* if (app->win != app->icon_win) XUnmapWindow(ob_display, app->win);*/ XSync(ob_display, False); XSelectInput(ob_display, app->icon_win, SLITAPP_EVENT_MASK); } g_message("Managed: 0x%lx", app->icon_win); } else XFree(h); } } void slit_remove_app(struct Slit *slit, struct SlitApp *app, gboolean reparent) { XSelectInput(ob_display, app->icon_win, NoEventMask); XSync(ob_display, False); if (reparent) { g_message("reparenting"); /* if (app->win != app->icon_win) XMapWindow(ob_display, app->win);*/ XReparentWindow(ob_display, app->icon_win, ob_root, 0, 0); } g_free(app); slit->slit_apps = g_list_remove(slit->slit_apps, app); slit_configure(); } void slit_read_theme() { XTextProperty prop; int i; char *theme = NULL; if (XGetTextProperty(ob_display, ob_root, &prop, XInternAtom(ob_display, "_OPENBOX_THEME", False))) { theme = theme_load((char*)prop.value); XFree(prop.value); } else theme = theme_load(NULL); g_free(theme); if (!theme) exit(EXIT_FAILURE); for (i = 0; i < nslits; ++i) { appearance_free(slit[i].a_frame); appearance_free(slit[i].a_title); slit[i].a_frame = appearance_copy(theme_a_unfocused_title); slit[i].a_title = appearance_copy(theme_a_unfocused_title); XSetWindowBorder(ob_display, slit[i].frame, theme_b_color->pixel); XSetWindowBorderWidth(ob_display, slit[i].frame, theme_bwidth); XSetWindowBorder(ob_display, slit[i].frame, BlackPixel(ob_display, ob_screen)); XSetWindowBorderWidth(ob_display, slit[i].frame, 30); } slit_configure(); } void slit_configure() { int i; int titleh; GList *it; int spot; titleh = 4; for (i = 0; i < nslits; ++i) { if (slit[i].horz) { slit[i].w = titleh; slit[i].h = 0; } else { slit[i].w = 0; slit[i].h = titleh; } spot = titleh; for (it = slit[i].slit_apps; it; it = it->next) { struct SlitApp *app = it->data; if (slit[i].horz) { g_message("%d", spot); app->x = spot; app->y = 0; slit[i].w += app->w; slit[i].h = MAX(slit[i].h, app->h); spot += app->w; } else { app->x = 0; app->y = spot; slit[i].w = MAX(slit[i].h, app->w); slit[i].h += app->h; spot += app->h; } XMoveWindow(ob_display, app->icon_win, app->x, app->y); } /* calculate position */ slit[i].x = slit[i].user_x; slit[i].y = slit[i].user_y; switch(slit[i].gravity) { case NorthGravity: case CenterGravity: case SouthGravity: slit[i].x -= slit[i].w / 2; break; case NorthEastGravity: case EastGravity: case SouthEastGravity: slit[i].x -= slit[i].w; break; } switch(slit[i].gravity) { case WestGravity: case CenterGravity: case EastGravity: slit[i].y -= slit[i].h / 2; break; case SouthWestGravity: case SouthGravity: case SouthEastGravity: slit[i].y -= slit[i].h; break; } if (slit[i].w > 0 && slit[i].h > 0) { RECT_SET(slit[i].a_frame->area, 0, 0, slit[i].w, slit[i].h); XMoveResizeWindow(ob_display, slit[i].frame, slit[i].x - theme_bwidth, slit[i].y - theme_bwidth, slit[i].w, slit[i].h); if (slit[i].horz) { RECT_SET(slit[i].a_title->area, 0, 0, titleh, slit[i].h); XMoveResizeWindow(ob_display, slit[i].title, 0, 0, titleh, slit[i].h); } else { RECT_SET(slit[i].a_title->area, 0, 0, slit[i].w, titleh); XMoveResizeWindow(ob_display, slit[i].title, 0, 0, slit[i].w, titleh); } paint(slit[i].frame, slit[i].a_frame); paint(slit[i].title, slit[i].a_title); XMapWindow(ob_display, slit[i].frame); } else XUnmapWindow(ob_display, slit[i].frame); } }