/** Shutdown the dock. */ void ShutdownDock(void) { DockNode *np; if(dock) { /* Release memory used by the dock list. */ while(dock->nodes) { np = dock->nodes->next; JXReparentWindow(display, dock->nodes->window, rootWindow, 0, 0); Release(dock->nodes); dock->nodes = np; } /* Release the selection. */ if(owner) { JXSetSelectionOwner(display, dockAtom, None, CurrentTime); } /* Destroy the dock window. */ JXDestroyWindow(display, dock->window); } }
/** Determine if this is a window to be swallowed, if it is, swallow it. */ int CheckSwallowMap(const XMapEvent *event) { SwallowNode *np; XClassHint hint; XWindowAttributes attr; for(np = swallowNodes; np; np = np->next) { if(np->cp->window != None) { continue; } Assert(np->cp->tray->window != None); if(JXGetClassHint(display, event->window, &hint)) { if(!strcmp(hint.res_name, np->name)) { /* Swallow the window. */ JXSelectInput(display, event->window, StructureNotifyMask | ResizeRedirectMask); JXAddToSaveSet(display, event->window); JXSetWindowBorder(display, event->window, colors[COLOR_TRAY_BG]); JXReparentWindow(display, event->window, np->cp->tray->window, 0, 0); JXMapRaised(display, event->window); JXFree(hint.res_name); JXFree(hint.res_class); np->cp->window = event->window; /* Update the size. */ JXGetWindowAttributes(display, event->window, &attr); np->border = attr.border_width; if(!np->userWidth) { np->cp->requestedWidth = attr.width + 2 * np->border; } if(!np->userHeight) { np->cp->requestedHeight = attr.height + 2 * np->border; } ResizeTray(np->cp->tray); return 1; } else { JXFree(hint.res_name); JXFree(hint.res_class); } } } return 0; }
/** Layout items on the dock. */ void UpdateDock(void) { XConfigureEvent event; DockNode *np; int x, y; int itemSize; Assert(dock); /* Determine the size of items in the dock. */ GetDockItemSize(&itemSize); x = 0; y = 0; memset(&event, 0, sizeof(event)); for(np = dock->nodes; np; np = np->next) { JXMoveResizeWindow(display, np->window, x, y, itemSize, itemSize); /* Reparent if this window likes to go other places. */ if(np->needs_reparent) { JXReparentWindow(display, np->window, dock->cp->window, x, y); } event.type = ConfigureNotify; event.event = np->window; event.window = np->window; event.x = x; event.y = y; event.width = itemSize; event.height = itemSize; JXSendEvent(display, np->window, False, StructureNotifyMask, (XEvent*)&event); if(orientation == SYSTEM_TRAY_ORIENTATION_HORZ) { x += itemSize; } else { y += itemSize; } } }
/** Handle an unmap notify event. */ void HandleUnmapNotify(const XUnmapEvent *event) { ClientNode *np; XEvent e; Assert(event); if(event->window != event->event) { /* Allow ICCCM synthetic UnmapNotify events through. */ if (event->event != rootWindow || !event->send_event) { return; } } np = FindClientByWindow(event->window); if(np) { /* Grab the server to prevent the client from destroying the * window after we check for a DestroyNotify. */ GrabServer(); if(np->controller) { (np->controller)(1); } if(JXCheckTypedWindowEvent(display, np->window, DestroyNotify, &e)) { UpdateTime(&e); RemoveClient(np); } else if((np->state.status & STAT_MAPPED) || event->send_event) { if(!(np->state.status & STAT_HIDDEN)) { np->state.status &= ~STAT_MAPPED; JXUngrabButton(display, AnyButton, AnyModifier, np->window); GravitateClient(np, 1); JXReparentWindow(display, np->window, rootWindow, np->x, np->y); WriteState(np); JXRemoveFromSaveSet(display, np->window); RemoveClient(np); } } UngrabServer(); } }
/** Add a window to the dock. */ void DockWindow(Window win) { DockNode *np; /* If no dock is running, just return. */ if(!dock) { return; } /* Make sure we have a valid window to add. */ if(JUNLIKELY(win == None)) { return; } /* If this window is already docked ignore it. */ for(np = dock->nodes; np; np = np->next) { if(np->window == win) { return; } } /* Add the window to our list. */ np = Allocate(sizeof(DockNode)); np->window = win; np->needs_reparent = 0; np->next = dock->nodes; dock->nodes = np; /* Update the requested size. */ GetDockSize(&dock->cp->requestedWidth, &dock->cp->requestedHeight); /* It's safe to reparent at (0, 0) since we call * ResizeTray which will invoke the Resize callback. */ JXAddToSaveSet(display, win); JXReparentWindow(display, win, dock->cp->window, 0, 0); JXMapRaised(display, win); /* Resize the tray containing the dock. */ ResizeTray(dock->cp->tray); }
/** Destroy a swallow tray component. */ void Destroy(TrayComponentType *cp) { ClientProtocolType protocols; /* Destroy the window if there is one. */ if(cp->window) { JXReparentWindow(display, cp->window, rootWindow, 0, 0); JXRemoveFromSaveSet(display, cp->window); protocols = ReadWMProtocols(cp->window); if(protocols & PROT_DELETE) { SendClientMessage(cp->window, ATOM_WM_PROTOCOLS, ATOM_WM_DELETE_WINDOW); } else { JXKillClient(display, cp->window); } } }
/** 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(); }
/** Reparent a client window. */ void ReparentClient(ClientNode *np, char notOwner) { XSetWindowAttributes attr; int attrMask; int x, y, width, height; int north, south, east, west; Assert(np); if(notOwner) { JXAddToSaveSet(display, np->window); attr.event_mask = EnterWindowMask | ColormapChangeMask | PropertyChangeMask | KeyReleaseMask | StructureNotifyMask; attr.do_not_propagate_mask = NoEventMask; JXChangeWindowAttributes(display, np->window, CWEventMask | CWDontPropagate, &attr); } JXGrabButton(display, AnyButton, AnyModifier, np->window, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); if((np->state.border & (BORDER_TITLE | BORDER_OUTLINE)) == 0) { return; } attrMask = 0; /* We can't use PointerMotionHint mask here since the exact location * of the mouse on the frame is important. */ attrMask |= CWEventMask; attr.event_mask = ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask | SubstructureRedirectMask | SubstructureNotifyMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask; attrMask |= CWDontPropagate; attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask; attrMask |= CWBackPixel; attr.background_pixel = colors[COLOR_TITLE_BG2]; attrMask |= CWBorderPixel; attr.border_pixel = 0; x = np->x; y = np->y; width = np->width; height = np->height; GetBorderSize(&np->state, &north, &south, &east, &west); x -= west; y -= north; width += east + west; height += north + south; /* Create the frame window. */ np->parent = JXCreateWindow(display, rootWindow, x, y, width, height, 0, rootDepth, InputOutput, rootVisual, attrMask, &attr); /* Update the window to get only the events we want. */ attrMask = CWDontPropagate; attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | KeyPressMask | KeyReleaseMask; /* Make sure client doesn't muck with these. */ attrMask |= CWBackingStore; attr.backing_store = NotUseful; attrMask |= CWWinGravity; attr.win_gravity = NorthWestGravity; JXChangeWindowAttributes(display, np->window, attrMask, &attr); JXSetWindowBorderWidth(display, np->window, 0); /* Reparent the client window. */ JXReparentWindow(display, np->window, np->parent, west, north); }
/** 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(); }
/** 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(); }