/** 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(); }
/** 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 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; }
/** 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 _NET_WM_STATE request. */ void HandleNetWMState(const XClientMessageEvent *event, ClientNode *np) { int actionMaxH; int actionMaxV; int actionStick; int actionShade; int actionFullScreen; int actionMinimize; int actionNolist; int x; /* Up to two actions to be applied together, figure it out. */ actionMaxH = 0; actionMaxV = 0; actionStick = 0; actionShade = 0; actionFullScreen = 0; actionMinimize = 0; actionNolist = 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; } } 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(); } 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); } if(actionNolist) { np->state.status |= STAT_NOLIST; UpdateTaskBar(); } 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); } } /* Note that we don't handle toggling of hidden per EWMH * recommendations. */ if(actionNolist) { np->state.status ^= STAT_NOLIST; UpdateTaskBar(); } break; default: Debug("bad _NET_WM_STATE action: %ld", event->data.l[0]); break; } }
/** 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); } }
/** 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 = tp->border; yoffset = tp->border; 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 - 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->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); UpdateTaskBar(); DrawSpecificTray(tp); if(tp->hidden) { HideTray(tp); } }
/** 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(); }