/** Show a hidden client. This will not update transients. */ void ShowClient(ClientNode *np) { if(np->state.status & STAT_HIDDEN) { np->state.status &= ~STAT_HIDDEN; if(np->state.status & (STAT_MAPPED | STAT_SHADED)) { if(!(np->state.status & STAT_MINIMIZED)) { if(np->parent != None) { JXMapWindow(display, np->parent); } else { JXMapWindow(display, np->window); } if(np->state.status & STAT_ACTIVE) { FocusClient(np); } } } } }
/** Restore a window with its transients (helper method). */ void RestoreTransients(ClientNode *np, char raise) { ClientNode *tp; int x; Assert(np); /* Make sure this window is on the current desktop. */ SetClientDesktop(np, currentDesktop); /* Restore this window. */ if(!(np->state.status & STAT_MAPPED)) { if(np->state.status & STAT_SHADED) { if(np->parent != None) { JXMapWindow(display, np->parent); } } else { JXMapWindow(display, np->window); if(np->parent != None) { JXMapWindow(display, np->parent); } np->state.status |= STAT_MAPPED; } } np->state.status &= ~STAT_MINIMIZED; np->state.status &= ~STAT_SDESKTOP; /* Restore transient windows. */ for(x = 0; x < LAYER_COUNT; x++) { for(tp = nodes[x]; tp; tp = tp->next) { if(tp->owner == np->window && (tp->state.status & STAT_MINIMIZED)) { RestoreTransients(tp, raise); } } } if(raise) { FocusClient(np); RaiseClient(np); } WriteState(np); }
/** Handle a map request. */ void HandleMapRequest(const XMapEvent *event) { ClientNode *np; Assert(event); if(CheckSwallowMap(event)) { return; } np = FindClientByWindow(event->window); if(!np) { JXSync(display, False); JXGrabServer(display); np = AddClientWindow(event->window, 0, 1); if(np) { if(focusModel == FOCUS_CLICK && !(np->state.status & STAT_NOFOCUS)) { FocusClient(np); } } else { JXMapWindow(display, event->window); } JXSync(display, False); JXUngrabServer(display); } else { if(!(np->state.status & STAT_MAPPED)) { np->state = ReadWindowState(np->window); np->state.status |= STAT_MAPPED; if(!(np->state.status & STAT_STICKY)) { np->state.desktop = currentDesktop; } JXMapWindow(display, np->window); JXMapWindow(display, np->parent); if(!(np->state.status & STAT_NOFOCUS)) { RaiseClient(np); FocusClient(np); } UpdateTaskBar(); UpdatePager(); } } RestackClients(); }
/** Load the background for the specified desktop. */ void LoadBackground(int desktop) { XSetWindowAttributes attr; long attrValues; BackgroundNode *bp; /* Determine the background to load. */ for(bp = backgrounds; bp; bp = bp->next) { if(bp->desktop == desktop) { break; } } if(!bp) { bp = defaultBackground; } /* If there is no background specified for this desktop, just return. */ if(!bp || !bp->value) { return; } /* If the background isn't changing, don't do anything. */ if( lastBackground && bp->type == lastBackground->type && !strcmp(bp->value, lastBackground->value)) { return; } if(lastBackground && lastBackground->window) { JXUnmapWindow(display, lastBackground->window); } lastBackground = bp; /* Load the background based on type. */ switch(bp->type) { case BACKGROUND_COMMAND: RunCommand(bp->value); return; default: attrValues = CWBackPixmap; attr.background_pixmap = bp->pixmap; break; } JXChangeWindowAttributes(display, bp->window, attrValues, &attr); JXClearWindow(display, bp->window); JXMapWindow(display, bp->window); SetPixmapAtom(rootWindow, ATOM_XSETROOT_ID, bp->window); }
/** Handle a map request. */ void HandleMapRequest(const XMapEvent *event) { ClientNode *np; Assert(event); if(CheckSwallowMap(event->window)) { return; } np = FindClientByWindow(event->window); if(!np) { GrabServer(); np = AddClientWindow(event->window, 0, 1); if(np) { if(!(np->state.status & STAT_NOFOCUS)) { FocusClient(np); } } else { JXMapWindow(display, event->window); } UngrabServer(); } else { if(!(np->state.status & STAT_MAPPED)) { UpdateState(np); np->state.status |= STAT_MAPPED; XMapWindow(display, np->window); if(np->parent != None) { XMapWindow(display, np->parent); } if(!(np->state.status & STAT_STICKY)) { np->state.desktop = currentDesktop; } if(!(np->state.status & STAT_NOFOCUS)) { FocusClient(np); RaiseClient(np); } WriteState(np); RequireTaskUpdate(); RequirePagerUpdate(); } } RequireRestack(); }
/** Unshade a client. */ void UnshadeClient(ClientNode *np) { Assert(np); if(!(np->state.status & STAT_SHADED)) { return; } if(!(np->state.status & (STAT_MINIMIZED | STAT_SDESKTOP))) { JXMapWindow(display, np->window); np->state.status |= STAT_MAPPED; } np->state.status &= ~STAT_SHADED; WriteState(np); ResetBorder(np); RefocusClient(); RequirePagerUpdate(); }
/** 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(); }
/** Remove a client window from management. */ void RemoveClient(ClientNode *np) { ColormapNode *cp; Assert(np); Assert(np->window != None); /* Remove this client from the client list */ if(np->next) { np->next->prev = np->prev; } else { nodeTail[np->state.layer] = np->prev; } if(np->prev) { np->prev->next = np->next; } else { nodes[np->state.layer] = np->next; } clientCount -= 1; XDeleteContext(display, np->window, clientContext); if(np->parent != None) { XDeleteContext(display, np->parent, frameContext); } if(np->state.status & STAT_URGENT) { UnregisterCallback(SignalUrgent, np); } /* Make sure this client isn't active */ if(activeClient == np && !shouldExit) { FocusNextStacked(np); } if(activeClient == np) { /* Must be the last client. */ SetWindowAtom(rootWindow, ATOM_NET_ACTIVE_WINDOW, None); activeClient = NULL; JXSetInputFocus(display, rootWindow, RevertToParent, eventTime); } /* If the window manager is exiting (ie, not the client), then * reparent etc. */ if(shouldExit && !(np->state.status & STAT_WMDIALOG)) { if(np->state.maxFlags) { np->x = np->oldx; np->y = np->oldy; np->width = np->oldWidth; np->height = np->oldHeight; JXMoveResizeWindow(display, np->window, np->x, np->y, np->width, np->height); } GravitateClient(np, 1); if(!(np->state.status & STAT_MAPPED) && (np->state.status & (STAT_MINIMIZED | STAT_SHADED))) { JXMapWindow(display, np->window); } JXUngrabButton(display, AnyButton, AnyModifier, np->window); JXReparentWindow(display, np->window, rootWindow, np->x, np->y); JXRemoveFromSaveSet(display, np->window); } /* Destroy the parent */ if(np->parent) { JXDestroyWindow(display, np->parent); } if(np->name) { Release(np->name); } if(np->instanceName) { JXFree(np->instanceName); } if(np->className) { JXFree(np->className); } RemoveClientFromTaskBar(np); RemoveClientStrut(np); while(np->colormaps) { cp = np->colormaps->next; Release(np->colormaps); np->colormaps = cp; } DestroyIcon(np->icon); Release(np); RequireRestack(); }
/** Add a window to management. */ ClientNode *AddClientWindow(Window w, char alreadyMapped, char notOwner) { XWindowAttributes attr; ClientNode *np; Assert(w != None); /* Get window attributes. */ if(JXGetWindowAttributes(display, w, &attr) == 0) { return NULL; } /* Determine if we should care about this window. */ if(attr.override_redirect == True) { return NULL; } if(attr.class == InputOnly) { return NULL; } /* Prepare a client node for this window. */ np = Allocate(sizeof(ClientNode)); memset(np, 0, sizeof(ClientNode)); np->window = w; np->owner = None; np->state.desktop = currentDesktop; np->x = attr.x; np->y = attr.y; np->width = attr.width; np->height = attr.height; np->cmap = attr.colormap; np->state.status = STAT_NONE; np->state.maxFlags = MAX_NONE; np->state.layer = LAYER_NORMAL; np->state.defaultLayer = LAYER_NORMAL; np->state.border = BORDER_DEFAULT; np->borderAction = BA_NONE; ReadClientInfo(np, alreadyMapped); if(!notOwner) { np->state.border = BORDER_OUTLINE | BORDER_TITLE | BORDER_MOVE; np->state.status |= STAT_WMDIALOG | STAT_STICKY; np->state.layer = LAYER_ABOVE; np->state.defaultLayer = LAYER_ABOVE; } ApplyGroups(np); if(np->icon == NULL) { LoadIcon(np); } /* We now know the layer, so insert */ np->prev = NULL; np->next = nodes[np->state.layer]; if(np->next) { np->next->prev = np; } else { nodeTail[np->state.layer] = np; } nodes[np->state.layer] = np; SetDefaultCursor(np->window); ReparentClient(np, notOwner); PlaceClient(np, alreadyMapped); if(!((np->state.status & STAT_FULLSCREEN) || np->state.maxFlags)) { int north, south, east, west; GetBorderSize(&np->state, &north, &south, &east, &west); if(np->parent != None) { JXMoveResizeWindow(display, np->parent, np->x - west, np->y - north, np->width + east + west, np->height + north + south); JXMoveResizeWindow(display, np->window, west, north, np->width, np->height); } else { JXMoveResizeWindow(display, np->window, np->x, np->y, np->width, np->height); } } /* If one of these fails we are SOL, so who cares. */ XSaveContext(display, np->window, clientContext, (void*)np); if(np->parent != None) { XSaveContext(display, np->parent, frameContext, (void*)np); } if(np->state.status & STAT_MAPPED) { JXMapWindow(display, np->window); if(np->parent != None) { JXMapWindow(display, np->parent); } } clientCount += 1; if(!alreadyMapped) { RaiseClient(np); } if(np->state.status & STAT_OPACITY) { SetOpacity(np, np->state.opacity, 1); } else { SetOpacity(np, settings.inactiveClientOpacity, 1); } if(np->state.status & STAT_STICKY) { SetCardinalAtom(np->window, ATOM_NET_WM_DESKTOP, ~0UL); } else { SetCardinalAtom(np->window, ATOM_NET_WM_DESKTOP, np->state.desktop); } /* Shade the client if requested. */ if(np->state.status & STAT_SHADED) { np->state.status &= ~STAT_SHADED; ShadeClient(np); } /* Minimize the client if requested. */ if(np->state.status & STAT_MINIMIZED) { np->state.status &= ~STAT_MINIMIZED; MinimizeClient(np, 0); } /* Maximize the client if requested. */ if(np->state.maxFlags) { const MaxFlags flags = np->state.maxFlags; np->state.maxFlags = MAX_NONE; MaximizeClient(np, flags); } if(np->state.status & STAT_URGENT) { RegisterCallback(URGENCY_DELAY, SignalUrgent, np); } /* Update task bars. */ AddClientToTaskBar(np); /* Make sure we're still in sync */ WriteState(np); SendConfigureEvent(np); /* Hide the client if we're not on the right desktop. */ if(np->state.desktop != currentDesktop && !(np->state.status & STAT_STICKY)) { HideClient(np); } ReadClientStrut(np); /* Focus transients if their parent has focus. */ if(np->owner != None) { if(activeClient && np->owner == activeClient->window) { FocusClient(np); } } /* Make the client fullscreen if requested. */ if(np->state.status & STAT_FULLSCREEN) { np->state.status &= ~STAT_FULLSCREEN; SetClientFullScreen(np, 1); } ResetBorder(np); return np; }
/** 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(); }