/** Set _NET_WORKAREA. */ void SetWorkarea(void) { BoundingBox box; unsigned long *array; unsigned int count; int x; count = 4 * settings.desktopCount * sizeof(unsigned long); array = (unsigned long*)AllocateStack(count); box.x = 0; box.y = 0; box.width = rootWidth; box.height = rootHeight; SubtractTrayBounds(GetTrays(), &box, LAYER_NORMAL); SubtractStrutBounds(&box, NULL); for(x = 0; x < settings.desktopCount; x++) { array[x * 4 + 0] = box.x; array[x * 4 + 1] = box.y; array[x * 4 + 2] = box.width; array[x * 4 + 3] = box.height; } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_WORKAREA], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, settings.desktopCount * 4); ReleaseStack(array); }
/** Write the window state hint for a client. */ void WriteState(ClientNode *np) { unsigned long data[2]; if(np->state.status & STAT_MAPPED) { data[0] = NormalState; } else if(np->state.status & STAT_MINIMIZED) { data[0] = IconicState; } else if(np->state.status & STAT_SHADED) { data[0] = NormalState; } else { data[0] = WithdrawnState; } data[1] = None; if(data[0] == WithdrawnState) { JXDeleteProperty(display, np->window, atoms[ATOM_WM_STATE]); } else { JXChangeProperty(display, np->window, atoms[ATOM_WM_STATE], atoms[ATOM_WM_STATE], 32, PropModeReplace, (unsigned char*)data, 2); } WriteNetState(np); WriteNetAllowed(np); }
/** Maintain the _NET_CLIENT_LIST[_STACKING] properties on the root. */ void UpdateNetClientList(void) { TaskEntry *tp; ClientNode *client; Window *windows; unsigned int count; int layer; /* Determine how much we need to allocate. */ if(clientCount == 0) { windows = NULL; } else { windows = AllocateStack(clientCount * sizeof(Window)); } /* Set _NET_CLIENT_LIST */ count = 0; for(tp = taskEntries; tp; tp = tp->next) { ClientEntry *cp; for(cp = tp->clients; cp; cp = cp->next) { windows[count] = cp->client->window; count += 1; } } Assert(count <= clientCount); JXChangeProperty(display, rootWindow, atoms[ATOM_NET_CLIENT_LIST], XA_WINDOW, 32, PropModeReplace, (unsigned char*)windows, count); /* Set _NET_CLIENT_LIST_STACKING */ count = 0; for(layer = FIRST_LAYER; layer <= LAST_LAYER; layer++) { for(client = nodes[layer]; client; client = client->next) { windows[count] = client->window; count += 1; } } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_CLIENT_LIST_STACKING], XA_WINDOW, 32, PropModeReplace, (unsigned char*)windows, count); if(windows != NULL) { ReleaseStack(windows); } }
/** Write the allowed action property. */ void WriteNetAllowed(ClientNode *np) { unsigned long values[12]; unsigned int index; Assert(np); index = 0; if(np->state.border & BORDER_SHADE) { values[index++] = atoms[ATOM_NET_WM_ACTION_SHADE]; } if(np->state.border & BORDER_MIN) { values[index++] = atoms[ATOM_NET_WM_ACTION_MINIMIZE]; } if(np->state.border & BORDER_MAX) { values[index++] = atoms[ATOM_NET_WM_ACTION_MAXIMIZE_HORZ]; values[index++] = atoms[ATOM_NET_WM_ACTION_MAXIMIZE_VERT]; values[index++] = atoms[ATOM_NET_WM_ACTION_FULLSCREEN]; } if(np->state.border & BORDER_CLOSE) { values[index++] = atoms[ATOM_NET_WM_ACTION_CLOSE]; } if(np->state.border & BORDER_RESIZE) { values[index++] = atoms[ATOM_NET_WM_ACTION_RESIZE]; } if(np->state.border & BORDER_MOVE) { values[index++] = atoms[ATOM_NET_WM_ACTION_MOVE]; } if(!(np->state.status & STAT_STICKY)) { values[index++] = atoms[ATOM_NET_WM_ACTION_CHANGE_DESKTOP]; } values[index++] = atoms[ATOM_NET_WM_ACTION_STICK]; values[index++] = atoms[ATOM_NET_WM_ACTION_BELOW]; values[index++] = atoms[ATOM_NET_WM_ACTION_ABOVE]; JXChangeProperty(display, np->window, atoms[ATOM_NET_WM_ALLOWED_ACTIONS], XA_ATOM, 32, PropModeReplace, (unsigned char*)values, index); }
/** Set _NET_FRAME_EXTENTS. */ void WriteFrameExtents(Window win, const ClientState *state) { unsigned long values[4]; int north, south, east, west; GetBorderSize(state, &north, &south, &east, &west); /* left, right, top, bottom */ values[0] = west; values[1] = east; values[2] = north; values[3] = south; JXChangeProperty(display, win, atoms[ATOM_NET_FRAME_EXTENTS], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)values, 4); }
/** Write the net state hint for a client. */ void WriteNetState(ClientNode *np) { unsigned long values[16]; int index; Assert(np); /* We remove the _NET_WM_STATE and _NET_WM_DESKTOP for withdrawn windows. */ if(!(np->state.status & (STAT_MAPPED | STAT_MINIMIZED | STAT_SHADED))) { JXDeleteProperty(display, np->window, atoms[ATOM_NET_WM_STATE]); JXDeleteProperty(display, np->window, atoms[ATOM_NET_WM_DESKTOP]); return; } index = 0; if(np->state.status & STAT_MINIMIZED) { values[index++] = atoms[ATOM_NET_WM_STATE_HIDDEN]; } if(np->state.maxFlags & MAX_HORIZ) { values[index++] = atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ]; } if(np->state.maxFlags & MAX_VERT) { values[index++] = atoms[ATOM_NET_WM_STATE_MAXIMIZED_VERT]; } if(np->state.maxFlags & MAX_TOP) { values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_TOP]; } if(np->state.maxFlags & MAX_BOTTOM) { values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_BOTTOM]; } if(np->state.maxFlags & MAX_LEFT) { values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_LEFT]; } if(np->state.maxFlags & MAX_RIGHT) { values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_RIGHT]; } if(np->state.status & STAT_SHADED) { values[index++] = atoms[ATOM_NET_WM_STATE_SHADED]; } if(np->state.status & STAT_STICKY) { values[index++] = atoms[ATOM_NET_WM_STATE_STICKY]; } if(np->state.status & STAT_FULLSCREEN) { values[index++] = atoms[ATOM_NET_WM_STATE_FULLSCREEN]; } if(np->state.status & STAT_NOLIST) { values[index++] = atoms[ATOM_NET_WM_STATE_SKIP_TASKBAR]; } if(np->state.status & STAT_NOPAGER) { values[index++] = atoms[ATOM_NET_WM_STATE_SKIP_PAGER]; } if(np->state.layer != np->state.defaultLayer) { if(np->state.layer == LAYER_BELOW) { values[index++] = atoms[ATOM_NET_WM_STATE_BELOW]; } else if(np->state.layer == LAYER_ABOVE) { values[index++] = atoms[ATOM_NET_WM_STATE_ABOVE]; } } if(np->state.status & STAT_URGENT) { values[index++] = atoms[ATOM_NET_WM_STATE_DEMANDS_ATTENTION]; } JXChangeProperty(display, np->window, atoms[ATOM_NET_WM_STATE], XA_ATOM, 32, PropModeReplace, (unsigned char*)values, index); WriteFrameExtents(np->window, &np->state); }
/** Set root hints and intern atoms. */ void StartupHints(void) { unsigned long *array; char *data; Atom *supported; Window win; unsigned int x; unsigned int count; /* Determine how much space we will need on the stack and allocate it. */ count = 0; for(x = 0; x < settings.desktopCount; x++) { count += strlen(GetDesktopName(x)) + 1; } if(count < 2 * sizeof(unsigned long)) { count = 2 * sizeof(unsigned long); } if(count < ATOM_COUNT * sizeof(Atom)) { count = ATOM_COUNT * sizeof(Atom); } data = AllocateStack(count); array = (unsigned long*)data; supported = (Atom*)data; /* Intern the atoms */ for(x = 0; x < ATOM_COUNT; x++) { *atomList[x].atom = JXInternAtom(display, atomList[x].name, False); } /* _NET_SUPPORTED */ for(x = FIRST_NET_ATOM; x <= LAST_NET_ATOM; x++) { supported[x - FIRST_NET_ATOM] = atoms[x]; } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_SUPPORTED], XA_ATOM, 32, PropModeReplace, (unsigned char*)supported, LAST_NET_ATOM - FIRST_NET_ATOM + 1); /* _NET_NUMBER_OF_DESKTOPS */ SetCardinalAtom(rootWindow, ATOM_NET_NUMBER_OF_DESKTOPS, settings.desktopCount); /* _NET_DESKTOP_NAMES */ count = 0; for(x = 0; x < settings.desktopCount; x++) { const char *name = GetDesktopName(x); const unsigned len = strlen(name); memcpy(&data[count], name, len + 1); count += len + 1; } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_NAMES], atoms[ATOM_UTF8_STRING], 8, PropModeReplace, (unsigned char*)data, count); /* _NET_DESKTOP_GEOMETRY */ array[0] = rootWidth; array[1] = rootHeight; JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_GEOMETRY], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, 2); /* _NET_DESKTOP_VIEWPORT */ array[0] = 0; array[1] = 0; JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_VIEWPORT], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, 2); /* _NET_WM_NAME */ win = supportingWindow; JXChangeProperty(display, win, atoms[ATOM_NET_WM_NAME], atoms[ATOM_UTF8_STRING], 8, PropModeReplace, (unsigned char*)"JWM", 3); /* _NET_WM_PID */ array[0] = getpid(); JXChangeProperty(display, win, atoms[ATOM_NET_WM_PID], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, 1); /* _NET_SUPPORTING_WM_CHECK */ SetWindowAtom(rootWindow, ATOM_NET_SUPPORTING_WM_CHECK, win); SetWindowAtom(win, ATOM_NET_SUPPORTING_WM_CHECK, win); ReleaseStack(data); }
/** Set an atom atom. */ void SetAtomAtom(Window window, AtomType atom, AtomType value) { Assert(window != None); JXChangeProperty(display, window, atoms[atom], XA_ATOM, 32, PropModeReplace, (unsigned char*)&atoms[value], 1); }
/** Set a pixmap atom. */ void SetPixmapAtom(Window window, AtomType atom, Pixmap value) { Assert(window != None); JXChangeProperty(display, window, atoms[atom], XA_PIXMAP, 32, PropModeReplace, (unsigned char*)&value, 1); }
/** Set a window atom. */ void SetWindowAtom(Window window, AtomType atom, unsigned long value) { Assert(window != None); JXChangeProperty(display, window, atoms[atom], XA_WINDOW, 32, PropModeReplace, (unsigned char*)&value, 1); }
/** Startup trays. */ void StartupTray(void) { XSetWindowAttributes attr; Atom atom; unsigned long attrMask; TrayType *tp; TrayComponentType *cp; int variableSize; int variableRemainder; int width, height; int xoffset, yoffset; for(tp = trays; tp; tp = tp->next) { LayoutTray(tp, &variableSize, &variableRemainder); /* Create the tray window. */ /* The window is created larger for a border. */ attrMask = CWOverrideRedirect; attr.override_redirect = True; /* We can't use PointerMotionHintMask since the exact position * of the mouse on the tray is important for popups. */ attrMask |= CWEventMask; attr.event_mask = ButtonPressMask | ButtonReleaseMask | SubstructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | PointerMotionMask; attrMask |= CWBackPixel; attr.background_pixel = colors[COLOR_TRAY_BG2]; attrMask |= CWBorderPixel; attr.border_pixel = colors[COLOR_TRAY_OUTLINE]; Assert(tp->width > 0); Assert(tp->height > 0); tp->window = JXCreateWindow(display, rootWindow, tp->x, tp->y, tp->width, tp->height, TRAY_BORDER_SIZE, rootVisual.depth, InputOutput, rootVisual.visual, attrMask, &attr); if(settings.trayOpacity < UINT_MAX) { /* Can't use atoms yet as it hasn't been initialized. */ atom = JXInternAtom(display, opacityAtom, False); JXChangeProperty(display, tp->window, atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&settings.trayOpacity, 1); } SetDefaultCursor(tp->window); /* Create and layout items on the tray. */ xoffset = 0; yoffset = 0; for(cp = tp->components; cp; cp = cp->next) { if(cp->Create) { if(tp->layout == LAYOUT_HORIZONTAL) { height = tp->height; width = cp->width; if(width == 0) { width = variableSize; if(variableRemainder) { width += 1; variableRemainder -= 1; } } } else { width = tp->width; height = cp->height; if(height == 0) { height = variableSize; if(variableRemainder) { height += 1; variableRemainder -= 1; } } } cp->width = Max(1, width); cp->height = Max(1, height); (cp->Create)(cp); } cp->x = xoffset; cp->y = yoffset; cp->screenx = tp->x + xoffset; cp->screeny = tp->y + yoffset; if(cp->window != None) { JXReparentWindow(display, cp->window, tp->window, xoffset, yoffset); } if(tp->layout == LAYOUT_HORIZONTAL) { xoffset += cp->width; } else { yoffset += cp->height; } } /* Show the tray. */ JXMapWindow(display, tp->window); trayCount += 1; } RequirePagerUpdate(); RequireTaskUpdate(); }
/** Startup trays. */ void StartupTray() { XSetWindowAttributes attr; Atom opacityAtom; unsigned long attrMask; TrayType *tp; TrayComponentType *cp; int variableSize; int variableRemainder; int width, height; int xoffset, yoffset; for(tp = trays; tp; tp = tp->next) { LayoutTray(tp, &variableSize, &variableRemainder); /* Create the tray window. */ /* The window is created larger for a border. */ attrMask = CWOverrideRedirect; attr.override_redirect = True; /* We can't use PointerMotionHintMask since the exact position * of the mouse on the tray is important for popups. */ attrMask |= CWEventMask; attr.event_mask = ButtonPressMask | ButtonReleaseMask | SubstructureNotifyMask | ExposureMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | PointerMotionMask; attrMask |= CWBackPixel; attr.background_pixel = colors[COLOR_TRAY_BG]; tp->window = JXCreateWindow(display, rootWindow, tp->x, tp->y, tp->width, tp->height, 0, rootDepth, InputOutput, rootVisual, attrMask, &attr); if(trayOpacity < UINT_MAX) { /* Can't use atoms yet as it hasn't been initialized. */ opacityAtom = JXInternAtom(display, "_NET_WM_WINDOW_OPACITY", False); JXChangeProperty(display, tp->window, opacityAtom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&trayOpacity, 1); JXSync(display, False); } SetDefaultCursor(tp->window); /* Create and layout items on the tray. */ xoffset = tp->border; yoffset = tp->border; for(cp = tp->components; cp; cp = cp->next) { if(cp->Create) { if(tp->layout == LAYOUT_HORIZONTAL) { height = tp->height - 2 * tp->border; width = cp->width; if(width == 0) { width = variableSize; if(variableRemainder) { ++width; --variableRemainder; } } } else { width = tp->width - 2 * tp->border; height = cp->height; if(height == 0) { height = variableSize; if(variableRemainder) { ++height; --variableRemainder; } } } cp->width = width; cp->height = height; (cp->Create)(cp); } cp->x = xoffset; cp->y = yoffset; cp->screenx = tp->x + xoffset; cp->screeny = tp->y + yoffset; if(cp->window != None) { JXReparentWindow(display, cp->window, tp->window, xoffset, yoffset); } if(tp->layout == LAYOUT_HORIZONTAL) { xoffset += cp->width; } else { yoffset += cp->height; } } /* Show the tray. */ JXMapWindow(display, tp->window); ++trayCount; } UpdatePager(); UpdateTaskBar(); }