/** Handle an unmap notify event. */ void HandleUnmapNotify(const XUnmapEvent *event) { ClientNode *np; XEvent e; Assert(event); np = FindClientByWindow(event->window); if(np && np->window == event->window) { if(JXCheckTypedWindowEvent(display, np->window, DestroyNotify, &e)) { HandleDestroyNotify(&e.xdestroywindow); return; } if(np->controller) { (np->controller)(1); } if(np->state.status & STAT_MAPPED) { np->state.status &= ~STAT_MAPPED; JXUnmapWindow(display, np->parent); WriteState(np); UpdateTaskBar(); UpdatePager(); } } }
/** Change to the specified desktop. */ void ChangeDesktop(unsigned int desktop) { ClientNode *np; unsigned int x; if(desktop >= desktopCount) { return; } if(currentDesktop == desktop && !initializing) { return; } /* Hide clients from the old desktop. * Note that we show clients in a separate loop to prevent an issue * with clients losing focus. */ for(x = 0; x < LAYER_COUNT; x++) { for(np = nodes[x]; np; np = np->next) { if(np->state.status & STAT_STICKY) { continue; } if(np->state.desktop == currentDesktop) { HideClient(np); } } } /* Show clients on the new desktop. */ for(x = 0; x < LAYER_COUNT; x++) { for(np = nodes[x]; np; np = np->next) { if(np->state.status & STAT_STICKY) { continue; } if(np->state.desktop == desktop) { ShowClient(np); } } } currentDesktop = desktop; SetCardinalAtom(rootWindow, ATOM_NET_CURRENT_DESKTOP, currentDesktop); SetCardinalAtom(rootWindow, ATOM_WIN_WORKSPACE, currentDesktop); RestackClients(); UpdatePager(); UpdateTaskBar(); LoadBackground(desktop); }
/** Handle a property notify event. */ int HandlePropertyNotify(const XPropertyEvent *event) { ClientNode *np; int changed; np = FindClientByWindow(event->window); if(np) { changed = 0; switch(event->atom) { case XA_WM_NAME: ReadWMName(np); changed = 1; break; case XA_WM_NORMAL_HINTS: ReadWMNormalHints(np); changed = 1; break; case XA_WM_HINTS: 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_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); } break; } if(changed) { DrawBorder(np, NULL); UpdateTaskBar(); UpdatePager(); } if(np->state.status & STAT_WMDIALOG) { return 0; } else { return 1; } } return 1; }
/** 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(); }
/** 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); 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); UpdateTaskBar(); UpdatePager(); } } RestackClients(); }
/** 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; } } }
/** 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; int hmax, vmax; Assert(np); if(!(np->state.border & BORDER_MOVE)) { return 0; } if(np->state.status & STAT_FULLSCREEN) { return 0; } hmax = 0; if(np->state.status & STAT_HMAX) { hmax = 1; } vmax = 0; if(np->state.status & STAT_VMAX) { vmax = 1; } if(vmax || hmax) { MaximizeClient(np, 0, 0); } 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, hmax, vmax); 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, hmax, vmax); 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); UpdatePager(); } } }
/** Wait for an event and process it. */ char WaitForEvent(XEvent *event) { struct timeval timeout; CallbackNode *cp; fd_set fds; long sleepTime; int fd; char handled; #ifdef ConnectionNumber fd = ConnectionNumber(display); #else fd = JXConnectionNumber(display); #endif /* Compute how long we should sleep. */ sleepTime = 10 * 1000; /* 10 seconds. */ for(cp = callbacks; cp; cp = cp->next) { if(cp->freq > 0 && cp->freq < sleepTime) { sleepTime = cp->freq; } } do { if(restack_pending) { RestackClients(); restack_pending = 0; } if(task_update_pending) { UpdateTaskBar(); task_update_pending = 0; } if(pager_update_pending) { UpdatePager(); pager_update_pending = 0; } while(JXPending(display) == 0) { FD_ZERO(&fds); FD_SET(fd, &fds); timeout.tv_sec = sleepTime / 1000; timeout.tv_usec = (sleepTime % 1000) * 1000; if(select(fd + 1, &fds, NULL, NULL, &timeout) <= 0) { Signal(); } if(JUNLIKELY(shouldExit)) { return 0; } } Signal(); JXNextEvent(display, event); UpdateTime(event); switch(event->type) { case ConfigureRequest: HandleConfigureRequest(&event->xconfigurerequest); handled = 1; break; case MapRequest: HandleMapRequest(&event->xmap); handled = 1; break; case PropertyNotify: handled = HandlePropertyNotify(&event->xproperty); break; case ClientMessage: HandleClientMessage(&event->xclient); handled = 1; break; case UnmapNotify: HandleUnmapNotify(&event->xunmap); handled = 1; break; case Expose: handled = HandleExpose(&event->xexpose); break; case ColormapNotify: HandleColormapChange(&event->xcolormap); handled = 1; break; case DestroyNotify: handled = HandleDestroyNotify(&event->xdestroywindow); break; case SelectionClear: handled = HandleSelectionClear(&event->xselectionclear); break; case ResizeRequest: handled = HandleDockResizeRequest(&event->xresizerequest); break; case MotionNotify: SetMousePosition(event->xmotion.x_root, event->xmotion.y_root, event->xmotion.window); handled = 0; break; case ButtonPress: case ButtonRelease: SetMousePosition(event->xbutton.x_root, event->xbutton.y_root, event->xbutton.window); handled = 0; break; case EnterNotify: SetMousePosition(event->xcrossing.x_root, event->xcrossing.y_root, event->xcrossing.window); handled = 0; break; case LeaveNotify: SetMousePosition(event->xcrossing.x_root, event->xcrossing.y_root, None); handled = 0; break; case ReparentNotify: HandleDockReparentNotify(&event->xreparent); handled = 1; break; case ConfigureNotify: handled = HandleConfigureNotify(&event->xconfigure); break; case CreateNotify: case MapNotify: case GraphicsExpose: case NoExpose: handled = 1; break; default: if(0) { #ifdef USE_SHAPE } else if(haveShape && event->type == shapeEvent) { HandleShapeEvent((XShapeEvent*)event); handled = 1; #endif } else { handled = 0; } break; } if(!handled) { handled = ProcessTrayEvent(event); } if(!handled) { handled = ProcessDialogEvent(event); } if(!handled) { handled = ProcessSwallowEvent(event); } if(!handled) { handled = ProcessPopupEvent(event); } } while(handled && JLIKELY(!shouldExit)); return !handled; }
/** Handle a _NET_MOVERESIZE_WINDOW request. */ void HandleNetMoveResize(const XClientMessageEvent *event, ClientNode *np) { long flags; int gravity; int deltax, deltay; Assert(event); Assert(np); flags = event->data.l[0] >> 8; gravity = event->data.l[0] & 0xFF; if(gravity == 0) { gravity = np->gravity; } GetGravityDelta(np, gravity, &deltax, &deltay); if(flags & (1 << 2)) { const long width = event->data.l[3]; switch(gravity) { case EastGravity: case NorthEastGravity: case SouthEastGravity: /* Right side should not move. */ np->x -= 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 -= (width - np->width) / 2; break; default: break; } np->width = width; } if(flags & (1 << 3)) { const long height = event->data.l[4]; switch(gravity) { case NorthGravity: case NorthEastGravity: case NorthWestGravity: /* Top should not move. */ break; case SouthGravity: case SouthEastGravity: case SouthWestGravity: /* Bottom should not move. */ np->y -= height - np->height; break; case CenterGravity: /* Center of the window should not move. */ np->y -= (height - np->height) / 2; break; default: break; } np->height = height; } if(flags & (1 << 0)) { np->x = event->data.l[1] - deltax; } if(flags & (1 << 1)) { np->y = event->data.l[2] - deltay; } /* Don't let maximized clients be moved or resized. */ if(JUNLIKELY(np->state.status & STAT_FULLSCREEN)) { SetClientFullScreen(np, 0); } if(JUNLIKELY(np->state.status & (STAT_HMAX | STAT_VMAX))) { MaximizeClient(np, 0, 0); } ConstrainSize(np); ResetBorder(np); SendConfigureEvent(np); UpdatePager(); }
/** 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); } 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); } break; } if(changed) { DrawBorder(np); UpdateTaskBar(); UpdatePager(); } if(np->state.status & STAT_WMDIALOG) { return 0; } else { return 1; } } return 1; }
/** Process a configure request. */ void HandleConfigureRequest(const XConfigureRequestEvent *event) { XWindowChanges wc; ClientNode *np; if(HandleDockConfigureRequest(event)) { return; } np = FindClientByWindow(event->window); if(np) { char changed = 0; char resized = 0; int deltax, deltay; 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.status & (STAT_VMAX | STAT_HMAX)) { MaximizeClient(np, 0, 0); } 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); JXMoveWindow(display, np->parent, np->x - west, np->y - north); } SendConfigureEvent(np); UpdatePager(); } 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); } }
/** Handle a _NET_WM_STATE request. */ void HandleNetWMState(const XClientMessageEvent *event, ClientNode *np) { unsigned int x; char actionMaxH; char actionMaxV; char actionStick; char actionShade; char actionFullScreen; char actionMinimize; char actionNolist; char actionNopager; char actionBelow; char actionAbove; /* Up to two actions to be applied together. */ actionMaxH = 0; actionMaxV = 0; actionStick = 0; actionShade = 0; actionFullScreen = 0; actionMinimize = 0; actionNolist = 0; actionNopager = 0; actionBelow = 0; actionAbove = 0; for(x = 1; x <= 2; x++) { if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_STICKY]) { actionStick = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_MAXIMIZED_VERT]) { actionMaxV = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ]) { actionMaxH = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_SHADED]) { actionShade = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_FULLSCREEN]) { actionFullScreen = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_HIDDEN]) { actionMinimize = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_SKIP_TASKBAR]) { actionNolist = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_SKIP_PAGER]) { actionNopager = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_BELOW]) { actionBelow = 1; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_ABOVE]) { actionAbove = 1; } } switch(event->data.l[0]) { case 0: /* Remove */ if(actionStick) { SetClientSticky(np, 0); } if(actionMaxH || actionMaxV) { if(np->state.status & (STAT_HMAX | STAT_VMAX)) { MaximizeClient(np, 0, 0); } } if(actionShade) { UnshadeClient(np); } if(actionFullScreen) { SetClientFullScreen(np, 0); } if(actionMinimize) { RestoreClient(np, 0); } if(actionNolist) { np->state.status &= ~STAT_NOLIST; UpdateTaskBar(); } if(actionNopager) { np->state.status &= ~STAT_NOPAGER; UpdatePager(); } if(actionBelow && np->state.layer == LAYER_BELOW) { SetClientLayer(np, np->state.defaultLayer); } if(actionAbove && np->state.layer == LAYER_ABOVE) { SetClientLayer(np, np->state.defaultLayer); } break; case 1: /* Add */ if(actionStick) { SetClientSticky(np, 1); } if(!(np->state.status & (STAT_HMAX | STAT_VMAX))) { MaximizeClient(np, actionMaxH, actionMaxV); } if(actionShade) { ShadeClient(np); } if(actionFullScreen) { SetClientFullScreen(np, 1); } if(actionMinimize) { MinimizeClient(np, 1); } if(actionNolist) { np->state.status |= STAT_NOLIST; UpdateTaskBar(); } if(actionNopager) { np->state.status |= STAT_NOPAGER; UpdatePager(); } if(actionBelow) { SetClientLayer(np, LAYER_BELOW); } if(actionAbove) { SetClientLayer(np, LAYER_ABOVE); } break; case 2: /* Toggle */ if(actionStick) { if(np->state.status & STAT_STICKY) { SetClientSticky(np, 0); } else { SetClientSticky(np, 1); } } if(actionMaxH || actionMaxV) { MaximizeClient(np, actionMaxH, actionMaxV); } if(actionShade) { if(np->state.status & STAT_SHADED) { UnshadeClient(np); } else { ShadeClient(np); } } if(actionFullScreen) { if(np->state.status & STAT_FULLSCREEN) { SetClientFullScreen(np, 0); } else { SetClientFullScreen(np, 1); } } if(actionBelow) { if(np->state.layer == LAYER_BELOW) { SetClientLayer(np, np->state.defaultLayer); } else { SetClientLayer(np, LAYER_BELOW); } } if(actionAbove) { if(np->state.layer == LAYER_ABOVE) { SetClientLayer(np, np->state.defaultLayer); } else { SetClientLayer(np, LAYER_ABOVE); } } /* Note that we don't handle toggling of hidden per EWMH * recommendations. */ if(actionNolist) { np->state.status ^= STAT_NOLIST; UpdateTaskBar(); } if(actionNopager) { np->state.status ^= STAT_NOPAGER; UpdatePager(); } break; default: Debug("bad _NET_WM_STATE action: %ld", event->data.l[0]); break; } /* Update _NET_WM_STATE if needed. * The state update is handled elsewhere for the other actions. */ if(actionNolist | actionNopager | actionAbove | actionBelow) { WriteState(np); } }
/** Handle a client message. */ void HandleClientMessage(const XClientMessageEvent *event) { ClientNode *np; long mask, flags; #ifdef DEBUG char *atomName; #endif np = FindClientByWindow(event->window); if(np) { if(event->message_type == atoms[ATOM_WIN_STATE]) { mask = event->data.l[0]; flags = event->data.l[1]; if(mask & WIN_STATE_STICKY) { if(flags & WIN_STATE_STICKY) { SetClientSticky(np, 1); } else { SetClientSticky(np, 0); } } if(mask & WIN_STATE_HIDDEN) { if(flags & WIN_STATE_HIDDEN) { np->state.status |= STAT_NOLIST; } else { np->state.status &= ~STAT_NOLIST; } UpdateTaskBar(); UpdatePager(); } } else if(event->message_type == atoms[ATOM_WIN_LAYER]) { SetClientLayer(np, event->data.l[0]); } else if(event->message_type == atoms[ATOM_WM_CHANGE_STATE]) { if(np->controller) { (np->controller)(0); } switch(event->data.l[0]) { case WithdrawnState: SetClientWithdrawn(np); break; case IconicState: MinimizeClient(np); break; case NormalState: RestoreClient(np, 1); break; default: break; } } else if(event->message_type == atoms[ATOM_NET_ACTIVE_WINDOW]) { RestoreClient(np, 1); FocusClient(np); } else if(event->message_type == atoms[ATOM_NET_WM_DESKTOP]) { if(event->data.l[0] == ~0L) { SetClientSticky(np, 1); } else { if(np->controller) { (np->controller)(0); } if(event->data.l[0] >= 0 && event->data.l[0] < (long)desktopCount) { np->state.status &= ~STAT_STICKY; SetClientDesktop(np, event->data.l[0]); } } } else if(event->message_type == atoms[ATOM_NET_CLOSE_WINDOW]) { DeleteClient(np); } else if(event->message_type == atoms[ATOM_NET_MOVERESIZE_WINDOW]) { HandleNetMoveResize(event, np); } else if(event->message_type == atoms[ATOM_NET_WM_STATE]) { HandleNetWMState(event, np); } else { #ifdef DEBUG atomName = JXGetAtomName(display, event->message_type); Debug("Unknown ClientMessage to client: %s", atomName); JXFree(atomName); #endif } } else if(event->window == rootWindow) { if(event->message_type == atoms[ATOM_JWM_RESTART]) { Restart(); } else if(event->message_type == atoms[ATOM_JWM_EXIT]) { Exit(); } else if(event->message_type == atoms[ATOM_NET_CURRENT_DESKTOP]) { ChangeDesktop(event->data.l[0]); } else { #ifdef DEBUG atomName = JXGetAtomName(display, event->message_type); Debug("Unknown ClientMessage to root: %s", atomName); JXFree(atomName); #endif } } else if(event->message_type == atoms[ATOM_NET_SYSTEM_TRAY_OPCODE]) { HandleDockEvent(event); } }
/** Process a button event. */ void HandleButtonEvent(const XButtonEvent *event) { int x, y; ClientNode *np; int north, south, east, west; np = FindClientByParent(event->window); if(np) { RaiseClient(np); if(focusModel == FOCUS_CLICK) { FocusClient(np); } switch(event->button) { case Button1: DispatchBorderButtonEvent(event, np); break; case Button2: MoveClient(np, event->x, event->y); break; case Button3: GetBorderSize(np, &north, &south, &east, &west); x = event->x + np->x - west; y = event->y + np->y - north; ShowWindowMenu(np, x, y); break; case Button4: ShadeClient(np); break; case Button5: UnshadeClient(np); break; default: break; } } else if(event->window == rootWindow && event->type == ButtonPress) { if(!ShowRootMenu(event->button, event->x, event->y)) { if(event->button == 4) { LeftDesktop(); } else if(event->button == 5) { RightDesktop(); } } } else { np = FindClientByWindow(event->window); if(np) { switch(event->button) { case Button1: case Button2: RaiseClient(np); if(focusModel == FOCUS_CLICK) { FocusClient(np); } if(event->state & Mod1Mask) { GetBorderSize(np, &north, &south, &east, &west); MoveClient(np, event->x + west, event->y + north); } break; case Button3: if(event->state & Mod1Mask) { GetBorderSize(np, &north, &south, &east, &west); ResizeClient(np, BA_RESIZE | BA_RESIZE_E | BA_RESIZE_S, event->x + west, event->y + north); } else { RaiseClient(np); if(focusModel == FOCUS_CLICK) { FocusClient(np); } } break; default: break; } JXAllowEvents(display, ReplayPointer, CurrentTime); } } UpdatePager(); }
/** Resize a client window (mouse initiated). */ void ResizeClient(ClientNode *np, BorderActionType action, int startx, int starty) { XEvent event; int oldx, oldy; int oldw, oldh; int gwidth, gheight; int lastgwidth, lastgheight; int delta; int north, south, east, west; int ratio, minr, maxr; Assert(np); if(!(np->state.border & BORDER_RESIZE)) { return; } if(JUNLIKELY(!GrabMouseForResize(action))) { Debug("ResizeClient: could not grab mouse"); return; } if(np->state.status & STAT_SHADED) { action &= ~(BA_RESIZE_N | BA_RESIZE_S); } np->controller = ResizeController; shouldStopResize = 0; oldx = np->x; oldy = np->y; oldw = np->width; oldh = np->height; gwidth = (np->width - np->baseWidth) / np->xinc; gheight = (np->height - np->baseHeight) / np->yinc; GetBorderSize(np, &north, &south, &east, &west); startx += np->x - west; starty += np->y - north; CreateResizeWindow(np); UpdateResizeWindow(np, gwidth, gheight); if(!(GetMouseMask() & (Button1Mask | Button3Mask))) { StopResize(np); return; } for(;;) { WaitForEvent(&event); if(shouldStopResize) { np->controller = NULL; return; } switch(event.type) { case ButtonRelease: if( event.xbutton.button == Button1 || event.xbutton.button == Button3) { StopResize(np); return; } break; case MotionNotify: SetMousePosition(event.xmotion.x_root, event.xmotion.y_root); DiscardMotionEvents(&event, np->window); if(action & BA_RESIZE_N) { delta = (event.xmotion.y - starty) / np->yinc; delta *= np->yinc; if(oldh - delta >= np->minHeight && (oldh - delta <= np->maxHeight || delta > 0)) { np->height = oldh - delta; np->y = oldy + delta; } if(!(action & (BA_RESIZE_E | BA_RESIZE_W))) { FixWidth(np); } } if(action & BA_RESIZE_S) { delta = (event.xmotion.y - starty) / np->yinc; delta *= np->yinc; np->height = oldh + delta; np->height = Max(np->height, np->minHeight); np->height = Min(np->height, np->maxHeight); if(!(action & (BA_RESIZE_E | BA_RESIZE_W))) { FixWidth(np); } } if(action & BA_RESIZE_E) { delta = (event.xmotion.x - startx) / np->xinc; delta *= np->xinc; np->width = oldw + delta; np->width = Max(np->width, np->minWidth); np->width = Min(np->width, np->maxWidth); if(!(action & (BA_RESIZE_N | BA_RESIZE_S))) { FixHeight(np); } } if(action & BA_RESIZE_W) { delta = (event.xmotion.x - startx) / np->xinc; delta *= np->xinc; if(oldw - delta >= np->minWidth && (oldw - delta <= np->maxWidth || delta > 0)) { np->width = oldw - delta; np->x = oldx + delta; } if(!(action & (BA_RESIZE_N | BA_RESIZE_S))) { FixHeight(np); } } if(np->sizeFlags & PAspect) { if((action & (BA_RESIZE_N | BA_RESIZE_S)) && (action & (BA_RESIZE_E | BA_RESIZE_W))) { /* Fixed point with a 16-bit fraction. */ ratio = (np->width << 16) / np->height; minr = (np->aspect.minx << 16) / np->aspect.miny; if(ratio < minr) { delta = np->width; np->width = (np->height * minr) >> 16; if(action & BA_RESIZE_W) { np->x -= np->width - delta; } } maxr = (np->aspect.maxx << 16) / np->aspect.maxy; if(ratio > maxr) { delta = np->height; np->height = (np->width << 16) / maxr; if(action & BA_RESIZE_N) { np->y -= np->height - delta; } } } } lastgwidth = gwidth; lastgheight = gheight; gwidth = (np->width - np->baseWidth) / np->xinc; gheight = (np->height - np->baseHeight) / np->yinc; if(lastgheight != gheight || lastgwidth != gwidth) { if(np->state.status & (STAT_HMAX | STAT_VMAX)) { np->state.status &= ~(STAT_HMAX | STAT_VMAX); WriteState(np); SendConfigureEvent(np); } UpdateResizeWindow(np, gwidth, gheight); if(resizeMode == RESIZE_OUTLINE) { ClearOutline(); if(np->state.status & STAT_SHADED) { DrawOutline(np->x - west, np->y - north, np->width + west + east, north + south); } else { DrawOutline(np->x - west, np->y - north, np->width + west + east, np->height + north + south); } } else { ResetRoundedRectWindow(np->parent); if(np->state.status & STAT_SHADED) { ShapeRoundedRectWindow(np->parent, np->width + east + west, north + south); JXMoveResizeWindow(display, np->parent, np->x - west, np->y - north, np->width + west + east, north + south); } else { ShapeRoundedRectWindow(np->parent, np->width + east + west, np->height + north + south); JXMoveResizeWindow(display, np->parent, np->x - west, np->y - north, np->width + west + east, np->height + north + south); } JXMoveResizeWindow(display, np->window, west, north, np->width, np->height); SendConfigureEvent(np); } UpdatePager(); } break; default: break; }
/** 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(); }