SubPanel * subPanelNew(int type) { SubPanel *p = NULL; /* Create new panel */ p = PANEL(subSharedMemoryAlloc(1, sizeof(SubPanel))); p->flags = (SUB_TYPE_PANEL|type); /* Handle panel item type */ switch(p->flags & (SUB_PANEL_ICON|SUB_PANEL_SUBLET|SUB_PANEL_VIEWS)) { case SUB_PANEL_ICON: /* {{{ */ p->icon = ICON(subSharedMemoryAlloc(1, sizeof(SubIcon))); break; /* }}} */ case SUB_PANEL_SUBLET: /* {{{ */ p->sublet = SUBLET(subSharedMemoryAlloc(1, sizeof(SubSublet))); /* Sublet specific */ p->sublet->time = subSubtleTime(); p->sublet->text = subSharedTextNew(); p->sublet->style = -1; break; /* }}} */ case SUB_PANEL_VIEWS: /* {{{ */ p->flags |= SUB_PANEL_DOWN; break; /* }}} */ } subSharedLogDebugSubtle("new=panel, type=%s\n", SUB_PANEL_VIEWS == type ? "views" : "title"); return p; } /* }}} */
/* ScreenPublish {{{ */ static void ScreenPublish(void) { int i; long *workareas = NULL, *panels = NULL, *viewports = NULL; assert(subtle); /* EWMH: Workarea and screen panels */ workareas = (long *)subSharedMemoryAlloc(4 * subtle->screens->ndata, sizeof(long)); panels = (long *)subSharedMemoryAlloc(2 * subtle->screens->ndata, sizeof(long)); /* Collect data*/ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); /* Set workareas */ workareas[i * 4 + 0] = s->geom.x; workareas[i * 4 + 1] = s->geom.y; workareas[i * 4 + 2] = s->geom.width; workareas[i * 4 + 3] = s->geom.height; /* Set panels */ panels[i * 2 + 0] = s->flags & SUB_SCREEN_PANEL1 ? subtle->ph : 0; panels[i * 2 + 1] = s->flags & SUB_SCREEN_PANEL2 ? subtle->ph : 0; } subEwmhSetCardinals(ROOT, SUB_EWMH_NET_WORKAREA, workareas, 4 * subtle->screens->ndata); subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_SCREEN_PANELS, panels, 2 * subtle->screens->ndata); /* EWMH: Desktop viewport */ viewports = (long *)subSharedMemoryAlloc(2 * subtle->screens->ndata, sizeof(long)); ///< Calloc inits with zero - great subEwmhSetCardinals(ROOT, SUB_EWMH_NET_DESKTOP_VIEWPORT, viewports, 2 * subtle->screens->ndata); free(workareas); free(panels); free(viewports); XSync(subtle->dpy, False); ///< Sync all changes subSharedLogDebugSubtle("publish=screen, screens=%d\n", subtle->screens->ndata); } /* }}} */
void subScreenPublish(void) { int i; long *views = NULL; assert(subtle); /* EWMH: Views per screen */ views = (long *)subSharedMemoryAlloc(subtle->screens->ndata, sizeof(long)); /* Collect views */ for(i = 0; i < subtle->screens->ndata; i++) views[i] = SCREEN(subtle->screens->data[i])->vid; subEwmhSetCardinals(ROOT, SUB_EWMH_SUBTLE_SCREEN_VIEWS, views, subtle->screens->ndata); free(views); XSync(subtle->dpy, False); ///< Sync all changes subSharedLogDebugSubtle("publish=screen, screens=%d\n", subtle->screens->ndata); } /* }}} */
SubTray * subTrayNew(Window win) { SubTray *t = NULL; assert(win); /* Create new tray */ t = TRAY(subSharedMemoryAlloc(1, sizeof(SubTray))); t->flags = SUB_TYPE_TRAY; t->win = win; t->width = subtle->ph; ///< Default width /* Update tray properties */ subSharedPropertyName(subtle->dpy, win, &t->name, PKG_NAME); subEwmhSetWMState(t->win, WithdrawnState); XSelectInput(subtle->dpy, t->win, TRAYMASK); XReparentWindow(subtle->dpy, t->win, subtle->windows.tray, 0, 0); XAddToSaveSet(subtle->dpy, t->win); XSaveContext(subtle->dpy, t->win, TRAYID, (void *)t); /* Start embedding life cycle */ subEwmhMessage(t->win, SUB_EWMH_XEMBED, 0xFFFFFF, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, subtle->windows.tray, 0); subSharedLogDebugSubtle("new=tray, name=%s, win=%#lx\n", t->name, win); return t; } /* }}} */
VALUE subIconAlloc(VALUE self) { SubtlextIcon *i = NULL; /* Create icon */ i = (SubtlextIcon *)subSharedMemoryAlloc(1, sizeof(SubtlextIcon)); i->instance = Data_Wrap_Struct(self, IconMark, IconSweep, (void *)i); return i->instance; } /* }}} */
/* GravityFindId {{{ */ static int GravityFindId(char *match, char **name, XRectangle *geometry) { int ret = -1, ngravities = 0; char **gravities = NULL; regex_t *preg = NULL; assert(match); /* Find gravity id */ if((preg = subSharedRegexNew(match)) && (gravities = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "SUBTLE_GRAVITY_LIST", False), &ngravities))) { int i; XRectangle geom = { 0 }; char buf[30] = { 0 }; for(i = 0; i < ngravities; i++) { sscanf(gravities[i], "%hdx%hd+%hd+%hd#%s", &geom.x, &geom.y, &geom.width, &geom.height, buf); /* Check id and name */ if((isdigit(match[0]) && atoi(match) == i) || (!isdigit(match[0]) && subSharedRegexMatch(preg, buf))) { subSharedLogDebugSubtlext("Found: type=gravity, name=%s, id=%d\n", buf, i); if(geometry) *geometry = geom; if(name) { *name = (char *)subSharedMemoryAlloc(strlen(buf) + 1, sizeof(char)); strncpy(*name, buf, strlen(buf)); } ret = i; break; } } } else subSharedLogDebugSubtlext("Failed finding gravity `%s'\n", name); if(preg) subSharedRegexKill(preg); if(gravities) XFreeStringList(gravities); return ret; } /* }}} */
SubStyle * subStyleNew(void) { SubStyle *s = NULL; /* Create new style */ s = STYLE(subSharedMemoryAlloc(1, sizeof(SubStyle))); s->flags |= SUB_TYPE_STYLE; /* Init style values */ subStyleReset(s, -1); subSharedLogDebugSubtle("new=style\n"); return s; } /* }}} */
SubHook * subHookNew(int type, unsigned long proc) { SubHook *h = NULL; assert(proc); /* Create new hook */ h = HOOK(subSharedMemoryAlloc(1, sizeof(SubHook))); h->flags = (SUB_TYPE_HOOK|type); h->proc = proc; subSubtleLogDebugSubtle("new=hook, type=%d, proc=%ld\n", type, proc); return h; } /* }}} */
void subTrayPublish(void) { int i; Window *wins = (Window *)subSharedMemoryAlloc(subtle->trays->ndata, sizeof(Window)); for(i = 0; i < subtle->trays->ndata; i++) wins[i] = TRAY(subtle->trays->data[i])->win; /* EWMH: Client list and client list stacking */ subEwmhSetWindows(ROOT, SUB_EWMH_SUBTLE_TRAY_LIST, wins, subtle->trays->ndata); XSync(subtle->dpy, False); ///< Sync all changes free(wins); subSharedLogDebugSubtle("publish=tray, trays=%d\n", subtle->trays->ndata); } /* }}} */
SubScreen * subScreenNew(int x, int y, unsigned int width, unsigned int height) { SubScreen *s = NULL; XSetWindowAttributes sattrs; unsigned long mask = 0; /* Create screen */ s = SCREEN(subSharedMemoryAlloc(1, sizeof(SubScreen))); s->flags = SUB_TYPE_SCREEN; s->geom.x = x; s->geom.y = y; s->geom.width = width; s->geom.height = height; s->base = s->geom; ///< Backup size s->vid = subtle->screens->ndata; ///< Init /* Create panel windows */ sattrs.event_mask = ButtonPressMask|EnterWindowMask| LeaveWindowMask|ExposureMask; sattrs.override_redirect = True; sattrs.background_pixmap = ParentRelative; mask = CWEventMask|CWOverrideRedirect|CWBackPixmap; s->panel1 = XCreateWindow(subtle->dpy, ROOT, 0, 1, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, mask, &sattrs); s->panel2 = XCreateWindow(subtle->dpy, ROOT, 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, mask, &sattrs); XSaveContext(subtle->dpy, s->panel1, SCREENID, (void *)s); XSaveContext(subtle->dpy, s->panel2, SCREENID, (void *)s); subSharedLogDebugSubtle("new=screen, x=%d, y=%d, width=%u, height=%u\n", s->geom.x, s->geom.y, s->geom.width, s->geom.height); return s; } /* }}} */
void subPanelPublish(void) { int i = 0, j = 0, idx = 0; char **names = NULL; /* Alloc space */ names = (char **)subSharedMemoryAlloc(subtle->sublets->ndata, sizeof(char *)); /* Find sublet in panels */ for(i = 0; i < subtle->screens->ndata; i++) { SubScreen *s = SCREEN(subtle->screens->data[i]); if(s->panels) { for(j = 0; j < s->panels->ndata; j++) { SubPanel *p = PANEL(s->panels->data[j]); /* Include sublets, exclude shallow copies */ if(p->flags & SUB_PANEL_SUBLET && !(p->flags & SUB_PANEL_COPY)) names[idx++] = p->sublet->name; } } } /* EWMH: Sublet list and windows */ subSharedPropertySetStrings(subtle->dpy, ROOT, subEwmhGet(SUB_EWMH_SUBTLE_SUBLET_LIST), names, subtle->sublets->ndata); subSharedLogDebugSubtle("publish=panel, n=%d\n", subtle->sublets->ndata); XSync(subtle->dpy, False); ///< Sync all changes free(names); } /* }}} */
/* main {{{ */ int main(int argc, char *argv[]) { int c; char *display = NULL; struct sigaction sa; const struct option long_options[] = { { "config", required_argument, 0, 'c' }, { "display", required_argument, 0, 'd' }, { "help", no_argument, 0, 'h' }, { "check", no_argument, 0, 'k' }, { "no-randr", no_argument, 0, 'n' }, { "replace", no_argument, 0, 'r' }, { "sublets", required_argument, 0, 's' }, { "version", no_argument, 0, 'v' }, #ifdef DEBUG { "level", required_argument, 0, 'l' }, { "debug", no_argument, 0, 'D' }, #endif /* DEBUG */ { 0, 0, 0, 0} }; /* Create subtle */ subtle = (SubSubtle *)(subSharedMemoryAlloc(1, sizeof(SubSubtle))); subtle->flags |= (SUB_SUBTLE_XRANDR|SUB_SUBTLE_XINERAMA); /* Parse arguments */ while(-1 != (c = getopt_long(argc, argv, "c:d:hknrs:vl:D", long_options, NULL))) { switch(c) { case 'c': subtle->paths.config = optarg; break; case 'd': display = optarg; break; case 'h': SubtleUsage(); return 0; case 'k': subtle->flags |= SUB_SUBTLE_CHECK; break; case 'n': subtle->flags &= ~SUB_SUBTLE_XRANDR; break; case 'r': subtle->flags |= SUB_SUBTLE_REPLACE; break; case 's': subtle->paths.sublets = optarg; break; case 'v': SubtleVersion(); return 0; #ifdef DEBUG case 'l': subSharedLogLevel(SubtleLevel(optarg)); break; case 'D': subtle->flags |= SUB_SUBTLE_DEBUG; subSharedLogLevel(DEFAULT_LOGLEVEL|DEBUG_LOGLEVEL); break; #else /* DEBUG */ case 'l': case 'D': printf("Please recompile %s with `debug=yes'\n", PKG_NAME); return 0; #endif /* DEBUG */ case '?': printf("Try `%s --help' for more information\n", PKG_NAME); return -1; } } /* Signal handler */ sa.sa_handler = SubtleSignal; sa.sa_flags = 0; memset(&sa.sa_mask, 0, sizeof(sigset_t)); ///< Avoid uninitialized values sigemptyset(&sa.sa_mask); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGSEGV, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); /* Load and check config only */ if(subtle->flags & SUB_SUBTLE_CHECK) { int ret = 0; subRubyInit(); if((ret = subRubyLoadConfig())) printf("Syntax OK\n"); subRubyFinish(); free(subtle); return !ret; } /* Alloc arrays */ subtle->clients = subArrayNew(); subtle->grabs = subArrayNew(); subtle->gravities = subArrayNew(); subtle->hooks = subArrayNew(); subtle->screens = subArrayNew(); subtle->sublets = subArrayNew(); subtle->tags = subArrayNew(); subtle->trays = subArrayNew(); subtle->views = subArrayNew(); /* Init */ SubtleVersion(); subDisplayInit(display); subEwmhInit(); subScreenInit(); subRubyInit(); subGrabInit(); /* Load */ subRubyLoadConfig(); subRubyLoadSublets(); subRubyLoadPanels(); /* Display */ subDisplayConfigure(); subDisplayScan(); subEventLoop(); /* Restart if necessary */ if(subtle->flags & SUB_SUBTLE_RESTART) { subSubtleFinish(); printf("Restarting\n"); execvp(argv[0], argv); } else subSubtleFinish(); printf("Exit\n"); return 0; } /* }}} */
void subEwmhInit(void) { int len = 0; long data[2] = { 0, 0 }, pid = (long)getpid(); char *selection = NULL, *names[] = { /* ICCCM */ "WM_NAME", "WM_CLASS", "WM_STATE", "WM_PROTOCOLS", "WM_TAKE_FOCUS", "WM_DELETE_WINDOW", "WM_NORMAL_HINTS", "WM_SIZE_HINTS", "WM_HINTS", "WM_WINDOW_ROLE", "WM_CLIENT_LEADER", /* EWMH */ "_NET_SUPPORTED", "_NET_CLIENT_LIST", "_NET_CLIENT_LIST_STACKING", "_NET_NUMBER_OF_DESKTOPS", "_NET_DESKTOP_NAMES", "_NET_DESKTOP_GEOMETRY", "_NET_DESKTOP_VIEWPORT", "_NET_CURRENT_DESKTOP", "_NET_ACTIVE_WINDOW", "_NET_WORKAREA", "_NET_SUPPORTING_WM_CHECK", "_NET_WM_FULL_PLACEMENT", "_NET_FRAME_EXTENTS", /* Client */ "_NET_CLOSE_WINDOW", "_NET_RESTACK_WINDOW", "_NET_MOVERESIZE_WINDOW", "_NET_WM_NAME", "_NET_WM_PID", "_NET_WM_DESKTOP", "_NET_WM_STRUT", /* Types */ "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_DOCK", "_NET_WM_WINDOW_TYPE_DESKTOP", "_NET_WM_WINDOW_TYPE_TOOLBAR", "_NET_WM_WINDOW_TYPE_SPLASH", "_NET_WM_WINDOW_TYPE_DIALOG", /* States */ "_NET_WM_STATE", "_NET_WM_STATE_FULLSCREEN", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_STICKY", "_NET_WM_STATE_DEMANDS_ATTENTION", /* Tray */ "_NET_SYSTEM_TRAY_OPCODE", "_NET_SYSTEM_TRAY_MESSAGE_DATA", "_NET_SYSTEM_TRAY_S", /* Misc */ "UTF8_STRING", "MANAGER", "_MOTIF_WM_HINTS", /* XEmbed */ "_XEMBED", "_XEMBED_INFO", /* subtle */ "SUBTLE_CLIENT_TAGS", "SUBTLE_CLIENT_RETAG", "SUBTLE_CLIENT_GRAVITY", "SUBTLE_CLIENT_SCREEN", "SUBTLE_CLIENT_FLAGS", "SUBTLE_GRAVITY_NEW", "SUBTLE_GRAVITY_LIST", "SUBTLE_GRAVITY_KILL", "SUBTLE_TAG_NEW", "SUBTLE_TAG_LIST", "SUBTLE_TAG_KILL", "SUBTLE_TRAY_LIST", "SUBTLE_VIEW_NEW", "SUBTLE_VIEW_TAGS", "SUBTLE_VIEW_STYLE", "SUBTLE_VIEW_ICONS", "SUBTLE_VIEW_KILL", "SUBTLE_SUBLET_NEW", "SUBTLE_SUBLET_UPDATE", "SUBTLE_SUBLET_DATA", "SUBTLE_SUBLET_STYLE", "SUBTLE_SUBLET_FLAGS", "SUBTLE_SUBLET_LIST", "SUBTLE_SUBLET_KILL", "SUBTLE_SCREEN_PANELS", "SUBTLE_SCREEN_VIEWS", "SUBTLE_SCREEN_JUMP", "SUBTLE_VISIBLE_TAGS", "SUBTLE_VISIBLE_VIEWS", "SUBTLE_RENDER", "SUBTLE_RELOAD", "SUBTLE_RESTART", "SUBTLE_QUIT", "SUBTLE_COLORS", "SUBTLE_FONT", "SUBTLE_DATA" }; assert(SUB_EWMH_TOTAL == LENGTH(names)); /* Update tray selection name for current screen */ len = strlen(names[SUB_EWMH_NET_SYSTEM_TRAY_SELECTION]) + 5; ///< For high screen counts selection = (char *)subSharedMemoryAlloc(len, sizeof(char)); snprintf(selection, len, "%s%u", names[SUB_EWMH_NET_SYSTEM_TRAY_SELECTION], SCRN); subSharedLogDebug("Selection: len=%d, name=%s\n", len, selection); names[SUB_EWMH_NET_SYSTEM_TRAY_SELECTION] = selection; /* Register atoms */ XInternAtoms(subtle->dpy, names, SUB_EWMH_TOTAL, 0, atoms); subtle->flags |= SUB_SUBTLE_EWMH; ///< Set EWMH flag free(selection); /* EWMH: Supported hints */ XChangeProperty(subtle->dpy, ROOT, atoms[SUB_EWMH_NET_SUPPORTED], XA_ATOM, 32, PropModeReplace, (unsigned char *)&atoms, SUB_EWMH_TOTAL); /* EWMH: Window manager information */ subEwmhSetWindows(ROOT, SUB_EWMH_NET_SUPPORTING_WM_CHECK, &subtle->windows.support, 1); subEwmhSetString(subtle->windows.support, SUB_EWMH_NET_WM_NAME, PKG_NAME); subEwmhSetString(subtle->windows.support, SUB_EWMH_WM_CLASS, PKG_NAME); subEwmhSetCardinals(subtle->windows.support, SUB_EWMH_NET_WM_PID, &pid, 1); /* EWMH: Desktop geometry */ data[0] = subtle->width; data[1] = subtle->height; subEwmhSetCardinals(ROOT, SUB_EWMH_NET_DESKTOP_GEOMETRY, (long *)&data, 2); /* EWMH: Client list and client list stacking */ subEwmhSetWindows(ROOT, SUB_EWMH_NET_CLIENT_LIST, NULL, 0); subEwmhSetWindows(ROOT, SUB_EWMH_NET_CLIENT_LIST_STACKING, NULL, 0); subSharedLogDebugSubtle("init=ewmh\n"); } /* }}} */