/** Hide a tray (for autohide). */ void HideTray(TrayType *tp) { int x, y; tp->hidden = 1; /* Determine where to move the tray. */ if(tp->layout == LAYOUT_HORIZONTAL) { x = tp->x; if(tp->y >= rootHeight / 2) { y = rootHeight - 1; } else { y = 1 - tp->height; } } else { y = tp->y; if(tp->x >= rootWidth / 2) { x = rootWidth - 1; } else { x = 1 - tp->width; } } /* Move it. */ JXMoveWindow(display, tp->window, x, y); }
/** 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); } }
/** Display a tray (for autohide). */ void ShowTray(TrayType *tp) { Window win1, win2; int winx, winy; unsigned int mask; int mousex, mousey; if(tp->hidden) { tp->hidden = 0; JXMoveWindow(display, tp->window, tp->x, tp->y); JXQueryPointer(display, rootWindow, &win1, &win2, &mousex, &mousey, &winx, &winy, &mask); SetMousePosition(mousex, mousey); } }
/** Hide a tray (for autohide). */ void HideTray(TrayType *tp) { const ScreenType *sp; int x, y; /* Don't hide if the tray is raised. */ if(tp->autoHide & THIDE_RAISED) { return; } tp->hidden = 1; /* Determine where to move the tray. */ sp = GetCurrentScreen(tp->x, tp->y); switch(tp->autoHide) { case THIDE_LEFT: x = sp->y - tp->width - TRAY_BORDER_SIZE; y = tp->y; break; case THIDE_RIGHT: x = sp->y + sp->width - TRAY_BORDER_SIZE; y = tp->y; break; case THIDE_TOP: x = tp->x; y = sp->y - tp->height - TRAY_BORDER_SIZE; break; case THIDE_BOTTOM: x = tp->x; y = sp->y + sp->height - TRAY_BORDER_SIZE; break; default: Assert(0); break; } /* Move and redraw. */ JXMoveWindow(display, tp->window, x, y); DrawSpecificTray(tp); }
/** 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; } } }
/** Resize a tray. */ void ResizeTray(TrayType *tp) { TrayComponentType *cp; int variableSize; int variableRemainder; int xoffset, yoffset; int width, height; Assert(tp); LayoutTray(tp, &variableSize, &variableRemainder); /* Reposition items on the tray. */ xoffset = 0; yoffset = 0; for(cp = tp->components; cp; cp = cp->next) { cp->x = xoffset; cp->y = yoffset; cp->screenx = tp->x + xoffset; cp->screeny = tp->y + yoffset; if(cp->Resize) { 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 = width; cp->height = height; (cp->Resize)(cp); } if(cp->window != None) { JXMoveWindow(display, cp->window, xoffset, yoffset); } if(tp->layout == LAYOUT_HORIZONTAL) { xoffset += cp->width; } else { yoffset += cp->height; } } JXMoveResizeWindow(display, tp->window, tp->x, tp->y, tp->width, tp->height); RequireTaskUpdate(); DrawSpecificTray(tp); if(tp->hidden) { HideTray(tp); } }
/** Process a configure request. */ void HandleConfigureRequest(const XConfigureRequestEvent *event) { XWindowChanges wc; ClientNode *np; if(HandleDockConfigureRequest(event)) { return; } np = FindClientByWindow(event->window); if(np) { int deltax, deltay; char changed = 0; char resized = 0; GetGravityDelta(np, np->gravity, &deltax, &deltay); if((event->value_mask & CWWidth) && (event->width != np->width)) { switch(np->gravity) { case EastGravity: case NorthEastGravity: case SouthEastGravity: /* Right side should not move. */ np->x -= event->width - np->width; break; case WestGravity: case NorthWestGravity: case SouthWestGravity: /* Left side should not move. */ break; case CenterGravity: /* Center of the window should not move. */ np->x -= (event->width - np->width) / 2; break; default: break; } np->width = event->width; changed = 1; resized = 1; } if((event->value_mask & CWHeight) && (event->height != np->height)) { switch(np->gravity) { case NorthGravity: case NorthEastGravity: case NorthWestGravity: /* Top should not move. */ break; case SouthGravity: case SouthEastGravity: case SouthWestGravity: /* Bottom should not move. */ np->y -= event->height - np->height; break; case CenterGravity: /* Center of the window should not move. */ np->y -= (event->height - np->height) / 2; break; default: break; } np->height = event->height; changed = 1; resized = 1; } if((event->value_mask & CWX) && (event->x - deltax != np->x)) { np->x = event->x - deltax; changed = 1; } if((event->value_mask & CWY) && (event->y - deltay != np->y)) { np->y = event->y - deltay; changed = 1; } /* Update stacking. */ if((event->value_mask & CWStackMode)) { Window above = None; if(event->value_mask & CWSibling) { above = event->above; } RestackClient(np, above, event->detail); } /* Return early if there's nothing to do. */ if(!changed) { return; } if(np->controller) { (np->controller)(0); } if(np->state.maxFlags) { MaximizeClient(np, MAX_NONE); } if(np->state.border & BORDER_CONSTRAIN) { resized = 1; } if(resized) { ConstrainSize(np); ConstrainPosition(np); ResetBorder(np); } else { int north, south, east, west; 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, np->y); } } SendConfigureEvent(np); RequirePagerUpdate(); } else { /* We don't know about this window, just let the configure through. */ wc.stack_mode = event->detail; wc.sibling = event->above; wc.border_width = event->border_width; wc.x = event->x; wc.y = event->y; wc.width = event->width; wc.height = event->height; JXConfigureWindow(display, event->window, event->value_mask, &wc); } }