/** Destroy task bar data. */ void DestroyTaskBar(void) { TaskBarType *bp; while(bars) { bp = bars->next; UnregisterCallback(SignalTaskbar, bars); Release(bars); bars = bp; } }
/** Shutdown popups. */ void ShutdownPopup(void) { UnregisterCallback(SignalPopup, NULL); if(popup.text) { Release(popup.text); popup.text = NULL; } if(popup.window != None) { JXDestroyWindow(display, popup.window); JXFreePixmap(display, popup.pmap); popup.window = None; } }
/** Update window state information. */ void UpdateState(ClientNode *np) { const char alreadyMapped = (np->state.status & STAT_MAPPED) ? 1 : 0; const char active = (np->state.status & STAT_ACTIVE) ? 1 : 0; /* Remove from the layer list. */ if(np->prev != NULL) { np->prev->next = np->next; } else { Assert(nodes[np->state.layer] == np); nodes[np->state.layer] = np->next; } if(np->next != NULL) { np->next->prev = np->prev; } else { Assert(nodeTail[np->state.layer] == np); nodeTail[np->state.layer] = np->prev; } /* Read the state (and new layer). */ if(np->state.status & STAT_URGENT) { UnregisterCallback(SignalUrgent, np); } np->state = ReadWindowState(np->window, alreadyMapped); if(np->state.status & STAT_URGENT) { RegisterCallback(URGENCY_DELAY, SignalUrgent, np); } /* We don't handle mapping the window, so restore its mapped state. */ if(!alreadyMapped) { np->state.status &= ~STAT_MAPPED; } /* Add to the layer list. */ np->prev = NULL; np->next = nodes[np->state.layer]; if(np->next == NULL) { nodeTail[np->state.layer] = np; } else { np->next->prev = np; } nodes[np->state.layer] = np; if(active) { FocusClient(np); } }
/** Destroy clock(s). */ void DestroyClock(void) { while(clocks) { ClockType *cp = clocks->next; if(clocks->format) { Release(clocks->format); } if(clocks->zone) { Release(clocks->zone); } DestroyActions(clocks->actions); UnregisterCallback(SignalClock, clocks); Release(clocks); clocks = cp; } }
/** Stop move. */ void StopMove(ClientNode *np, int doMove, int oldx, int oldy, MaxFlags maxFlags) { int north, south, east, west; Assert(np); Assert(np->controller); (np->controller)(0); np->controller = NULL; SetDefaultCursor(np->parent); UnregisterCallback(SignalMove, NULL); if(!doMove) { np->x = oldx; np->y = oldy; /* Restore maximized status. */ if(maxFlags) { MaximizeClient(np, maxFlags); } return; } GetBorderSize(&np->state, &north, &south, &east, &west); if(np->parent != None) { JXMoveWindow(display, np->parent, np->x - west, np->y - north); } else { JXMoveWindow(display, np->window, np->x - west, np->y - north); } SendConfigureEvent(np); /* Restore maximized status. */ if(maxFlags) { MaximizeClient(np, maxFlags); } }
/** Destroy tray data. */ void DestroyTray(void) { TrayType *tp; TrayComponentType *cp; while(trays) { tp = trays->next; UnregisterCallback(SignalTray, trays); while(trays->components) { cp = trays->components->next; Release(trays->components); trays->components = cp; } Release(trays); trays = tp; } }
/** Release tray button data. */ void DestroyTrayButtons(void) { TrayButtonType *bp; while(buttons) { bp = buttons->next; UnregisterCallback(SignalTrayButton, buttons); if(buttons->label) { Release(buttons->label); } if(buttons->iconName) { Release(buttons->iconName); } DestroyActions(buttons->actions); if(buttons->popup) { Release(buttons->popup); } Release(buttons); buttons = bp; } }
/** Stop move. */ void StopMove(ClientNode *np, int doMove, int oldx, int oldy, int hmax, int vmax) { int north, south, east, west; Assert(np); Assert(np->controller); (np->controller)(0); np->controller = NULL; SetDefaultCursor(np->parent); UnregisterCallback(SignalMove, NULL); if(!doMove) { np->x = oldx; np->y = oldy; /* Restore maximized status if only maximized in one direction. */ if((hmax || vmax) && !(hmax && vmax)) { MaximizeClient(np, hmax, vmax); } return; } GetBorderSize(&np->state, &north, &south, &east, &west); JXMoveWindow(display, np->parent, np->x - west, np->y - north); SendConfigureEvent(np); /* Restore maximized status. */ if((hmax || vmax) && !(hmax && vmax)) { MaximizeClient(np, hmax, vmax); } }
/** Move a client window. */ char MoveClient(ClientNode *np, int startx, int starty) { XEvent event; int oldx, oldy; int doMove; int north, south, east, west; int height; MaxFlags maxFlags; Assert(np); if(!(np->state.border & BORDER_MOVE)) { return 0; } if(np->state.status & STAT_FULLSCREEN) { return 0; } GrabMouseForMove(); RegisterCallback(0, SignalMove, NULL); np->controller = MoveController; shouldStopMove = 0; oldx = np->x; oldy = np->y; maxFlags = np->state.maxFlags; if(!(GetMouseMask() & (Button1Mask | Button2Mask))) { StopMove(np, 0, oldx, oldy, maxFlags); return 0; } GetBorderSize(&np->state, &north, &south, &east, &west); startx -= west; starty -= north; currentClient = np; atTop = atBottom = atLeft = atRight = 0; doMove = 0; for(;;) { WaitForEvent(&event); if(shouldStopMove) { np->controller = NULL; SetDefaultCursor(np->parent); UnregisterCallback(SignalMove, NULL); return doMove; } switch(event.type) { case ButtonRelease: if(event.xbutton.button == Button1 || event.xbutton.button == Button2) { StopMove(np, doMove, oldx, oldy, maxFlags); return doMove; } break; case MotionNotify: DiscardMotionEvents(&event, np->window); np->x = event.xmotion.x_root - startx; np->y = event.xmotion.y_root - starty; /* Get the move time used for desktop switching. */ if(!(atLeft | atTop | atRight | atBottom)) { if(event.xmotion.state & Mod1Mask) { moveTime.seconds = 0; moveTime.ms = 0; } else { GetCurrentTime(&moveTime); } } /* Determine if we are at a border for desktop switching. */ atLeft = atTop = atRight = atBottom = 0; if(event.xmotion.x_root == 0) { atLeft = 1; } else if(event.xmotion.x_root == rootWidth - 1) { atRight = 1; } if(event.xmotion.y_root == 0) { atTop = 1; } else if(event.xmotion.y_root == rootHeight - 1) { atBottom = 1; } if(event.xmotion.state & Mod1Mask) { /* Switch desktops immediately if alt is pressed. */ if(atLeft | atRight | atTop | atBottom) { TimeType now; GetCurrentTime(&now); UpdateDesktop(&now); } } else { /* If alt is not pressed, snap to borders. */ DoSnap(np); } if(!doMove && (abs(np->x - oldx) > MOVE_DELTA || abs(np->y - oldy) > MOVE_DELTA)) { if(np->state.maxFlags) { MaximizeClient(np, MAX_NONE); startx = np->width / 2; starty = -north / 2; MoveMouse(np->parent, startx, starty); } CreateMoveWindow(np); doMove = 1; } if(doMove) { if(settings.moveMode == MOVE_OUTLINE) { ClearOutline(); height = north + south; if(!(np->state.status & STAT_SHADED)) { height += np->height; } DrawOutline(np->x - west, np->y - north, np->width + west + east, height); } else { JXMoveWindow(display, np->parent, np->x - west, np->y - north); SendConfigureEvent(np); } UpdateMoveWindow(np); RequirePagerUpdate(); } break; default: break; } } }
/** Move a client window (keyboard or menu initiated). */ char MoveClientKeyboard(ClientNode *np) { XEvent event; int oldx, oldy; int moved; int height; int north, south, east, west; MaxFlags maxFlags; Assert(np); if(!(np->state.border & BORDER_MOVE)) { return 0; } if(np->state.status & STAT_FULLSCREEN) { return 0; } maxFlags = np->state.maxFlags; if(np->state.maxFlags != MAX_NONE) { MaximizeClient(np, MAX_NONE); } if(JUNLIKELY(JXGrabKeyboard(display, np->parent, True, GrabModeAsync, GrabModeAsync, CurrentTime))) { Debug("MoveClient: could not grab keyboard"); return 0; } GrabMouseForMove(); GetBorderSize(&np->state, &north, &south, &east, &west); oldx = np->x; oldy = np->y; RegisterCallback(0, SignalMove, NULL); np->controller = MoveController; shouldStopMove = 0; CreateMoveWindow(np); UpdateMoveWindow(np); MoveMouse(rootWindow, np->x, np->y); DiscardMotionEvents(&event, np->window); if(np->state.status & STAT_SHADED) { height = 0; } else { height = np->height; } for(;;) { WaitForEvent(&event); if(shouldStopMove) { np->controller = NULL; SetDefaultCursor(np->parent); UnregisterCallback(SignalMove, NULL); return 1; } moved = 0; if(event.type == KeyPress) { DiscardKeyEvents(&event, np->window); switch(GetKey(&event.xkey) & 0xFF) { case KEY_UP: if(np->y + height > 0) { np->y -= 10; } break; case KEY_DOWN: if(np->y < rootHeight) { np->y += 10; } break; case KEY_RIGHT: if(np->x < rootWidth) { np->x += 10; } break; case KEY_LEFT: if(np->x + np->width > 0) { np->x -= 10; } break; default: StopMove(np, 1, oldx, oldy, maxFlags); return 1; } MoveMouse(rootWindow, np->x, np->y); DiscardMotionEvents(&event, np->window); moved = 1; } else if(event.type == MotionNotify) { DiscardMotionEvents(&event, np->window); np->x = event.xmotion.x; np->y = event.xmotion.y; moved = 1; } else if(event.type == ButtonRelease) { StopMove(np, 1, oldx, oldy, maxFlags); return 1; } if(moved) { if(settings.moveMode == MOVE_OUTLINE) { ClearOutline(); DrawOutline(np->x - west, np->y - west, np->width + west + east, height + north + west); } else { JXMoveWindow(display, np->parent, np->x - west, np->y - north); SendConfigureEvent(np); } UpdateMoveWindow(np); RequirePagerUpdate(); } } }
/** Move a client window. */ char MoveClient(ClientNode *np, int startx, int starty, int snap) { XEvent event; int oldx, oldy; int doMove; int north, south, east, west; int height; int hmax, vmax; Assert(np); if(!(np->state.border & BORDER_MOVE)) { return 0; } if(np->state.status & STAT_FULLSCREEN) { return 0; } GrabMouseForMove(); RegisterCallback(0, SignalMove, NULL); np->controller = MoveController; shouldStopMove = 0; oldx = np->x; oldy = np->y; vmax = 0; hmax = 0; if(!(GetMouseMask() & (Button1Mask | Button2Mask))) { StopMove(np, 0, oldx, oldy, 0, 0); return 0; } GetBorderSize(&np->state, &north, &south, &east, &west); startx -= west; starty -= north; currentClient = np; atTop = 0; atBottom = 0; atLeft = 0; atRight = 0; doMove = 0; for(;;) { WaitForEvent(&event); if(shouldStopMove) { np->controller = NULL; SetDefaultCursor(np->parent); UnregisterCallback(SignalMove, NULL); return doMove; } switch(event.type) { case ButtonRelease: if(event.xbutton.button == Button1 || event.xbutton.button == Button2) { StopMove(np, doMove, oldx, oldy, hmax, vmax); return doMove; } break; case MotionNotify: DiscardMotionEvents(&event, np->window); np->x = event.xmotion.x_root - startx; np->y = event.xmotion.y_root - starty; GetCurrentTime(&moveTime); atLeft = 0; atTop = 0; atRight = 0; atBottom = 0; if(event.xmotion.x_root == 0) { atLeft = 1; } else if(event.xmotion.x_root == rootWidth - 1) { atRight = 1; } if(event.xmotion.y_root == 0) { atTop = 1; } else if(event.xmotion.y_root == rootHeight - 1) { atBottom = 1; } if(snap) { DoSnap(np); } if(!doMove && (abs(np->x - oldx) > MOVE_DELTA || abs(np->y - oldy) > MOVE_DELTA)) { if(np->state.status & (STAT_HMAX | STAT_VMAX)) { if(np->state.status & STAT_HMAX) { hmax = 1; } if(np->state.status & STAT_VMAX) { vmax = 1; } MaximizeClient(np, 0, 0); startx = np->width / 2; starty = -north / 2; MoveMouse(np->parent, startx, starty); } CreateMoveWindow(np); doMove = 1; } if(doMove) { if(settings.moveMode == MOVE_OUTLINE) { ClearOutline(); height = north + south; if(!(np->state.status & STAT_SHADED)) { height += np->height; } DrawOutline(np->x - west, np->y - north, np->width + west + east, height); } else { JXMoveWindow(display, np->parent, np->x - west, np->y - north); SendConfigureEvent(np); } UpdateMoveWindow(np); UpdatePager(); } break; default: break; } } }
/** Handle a property notify event. */ char HandlePropertyNotify(const XPropertyEvent *event) { ClientNode *np = FindClientByWindow(event->window); if(np) { char changed = 0; switch(event->atom) { case XA_WM_NAME: ReadWMName(np); changed = 1; break; case XA_WM_NORMAL_HINTS: ReadWMNormalHints(np); if(ConstrainSize(np)) { ResetBorder(np); } changed = 1; break; case XA_WM_HINTS: if(np->state.status & STAT_URGENT) { UnregisterCallback(SignalUrgent, np); } ReadWMHints(np->window, &np->state, 1); if(np->state.status & STAT_URGENT) { RegisterCallback(URGENCY_DELAY, SignalUrgent, np); } WriteState(np); break; case XA_WM_TRANSIENT_FOR: JXGetTransientForHint(display, np->window, &np->owner); break; case XA_WM_ICON_NAME: case XA_WM_CLIENT_MACHINE: break; default: if(event->atom == atoms[ATOM_WM_COLORMAP_WINDOWS]) { ReadWMColormaps(np); UpdateClientColormap(np); } else if(event->atom == atoms[ATOM_WM_PROTOCOLS]) { ReadWMProtocols(np->window, &np->state); } else if(event->atom == atoms[ATOM_NET_WM_ICON]) { LoadIcon(np); changed = 1; } else if(event->atom == atoms[ATOM_NET_WM_NAME]) { ReadWMName(np); changed = 1; } else if(event->atom == atoms[ATOM_NET_WM_STRUT_PARTIAL]) { ReadClientStrut(np); } else if(event->atom == atoms[ATOM_NET_WM_STRUT]) { ReadClientStrut(np); } else if(event->atom == atoms[ATOM_MOTIF_WM_HINTS]) { UpdateState(np); WriteState(np); ResetBorder(np); changed = 1; } else if(event->atom == atoms[ATOM_NET_WM_WINDOW_OPACITY]) { ReadWMOpacity(np->window, &np->state.opacity); if(np->parent != None) { SetOpacity(np, np->state.opacity, 1); } } break; } if(changed) { DrawBorder(np); RequireTaskUpdate(); RequirePagerUpdate(); } if(np->state.status & STAT_WMDIALOG) { return 0; } else { return 1; } } return 1; }
/** 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(); }