/** Toggle the "show desktop" state. */ void ShowDesktop() { ClientNode *np; int layer; for(layer = 0; layer < LAYER_COUNT; layer++) { for(np = nodes[layer]; np; np = np->next) { /* Skip "nolist" items. */ if(np->state.status & STAT_NOLIST) { continue; } if(showingDesktop) { if(np->state.status & STAT_SDESKTOP) { RestoreClient(np, 0); } } else if(np->state.desktop == currentDesktop || (np->state.status & STAT_STICKY)) { if(np->state.status & (STAT_MAPPED | STAT_SHADED)) { MinimizeClient(np); np->state.status |= STAT_SDESKTOP; } } } } showingDesktop = !showingDesktop; RestackClients(); }
/** Minimize all clients in a group. */ void MinimizeGroup(const TaskEntry *tp) { ClientEntry *cp; for(cp = tp->clients; cp; cp = cp->next) { if(ShouldFocus(cp->client, 1)) { MinimizeClient(cp->client, 0); } } }
/** Move to the next window in the window stack. */ void WalkWindowStack(char forward) { ClientNode *np; if(windowStack != NULL) { int x; if(wasMinimized) { np = FindClientByWindow(windowStack[windowStackCurrent]); if(np) { MinimizeClient(np, 1); } } /* Loop until we either raise a window or go through them all. */ for(x = 0; x < windowStackSize; x++) { /* Move to the next/previous window (wrap if needed). */ if(forward) { windowStackCurrent = (windowStackCurrent + 1) % windowStackSize; } else { if(windowStackCurrent == 0) { windowStackCurrent = windowStackSize; } windowStackCurrent -= 1; } /* Look up the window. */ np = FindClientByWindow(windowStack[windowStackCurrent]); /* Skip this window if it no longer exists or is currently in * a state that doesn't allow focus. */ if(np == NULL || !ShouldFocus(np, 1)) { continue; } /* Show the window. * Only when the walk completes do we update the stacking order. */ RestackClients(); if(np->state.status & STAT_MINIMIZED) { RestoreClient(np, 1); wasMinimized = 1; } else { wasMinimized = 0; } JXRaiseWindow(display, np->parent ? np->parent : np->window); FocusClient(np); break; } } }
/** Run a menu action. */ void RunTaskBarCommand(MenuAction *action, unsigned button) { ClientEntry *cp; if(action->type & MA_GROUP_MASK) { TaskEntry *tp = action->context; for(cp = tp->clients; cp; cp = cp->next) { if(!ShouldFocus(cp->client, 0)) { continue; } switch(action->type & ~MA_GROUP_MASK) { case MA_SENDTO: SetClientDesktop(cp->client, action->value); break; case MA_CLOSE: DeleteClient(cp->client); break; case MA_RESTORE: RestoreClient(cp->client, 0); break; case MA_MINIMIZE: MinimizeClient(cp->client, 0); break; default: break; } } } else if(action->type == MA_EXECUTE) { if(button == Button3) { Window w; int x, y; GetMousePosition(&x, &y, &w); ShowWindowMenu(action->context, x, y, 0); } else { ClientNode *np = action->context; RestoreClient(np, 1); FocusClient(np); MoveMouse(np->window, np->width / 2, np->height / 2); } } else { RunWindowCommand(action, button); } }
/** Window menu action callback. */ void RunWindowCommand(const MenuAction *action) { switch(action->type) { case MA_STICK: if(client->state.status & STAT_STICKY) { SetClientSticky(client, 0); } else { SetClientSticky(client, 1); } break; case MA_MAXIMIZE: if(client->state.maxFlags) { MaximizeClient(client, MAX_NONE); } else { MaximizeClient(client, MAX_VERT | MAX_HORIZ); } break; case MA_MAXIMIZE_H: MaximizeClient(client, MAX_HORIZ); break; case MA_MAXIMIZE_V: MaximizeClient(client, MAX_VERT); break; case MA_MINIMIZE: MinimizeClient(client, 1); break; case MA_RESTORE: RestoreClient(client, 1); break; case MA_CLOSE: DeleteClient(client); break; case MA_SENDTO: case MA_DESKTOP: SetClientDesktop(client, action->data.i); break; case MA_SHADE: if(client->state.status & STAT_SHADED) { UnshadeClient(client); } else { ShadeClient(client); } break; case MA_MOVE: MoveClientKeyboard(client); break; case MA_RESIZE: ResizeClientKeyboard(client); break; case MA_KILL: KillClient(client); break; case MA_LAYER: SetClientLayer(client, action->data.i); break; default: Debug("unknown window command: %d", action->type); break; } }
/** Window menu action callback. */ void RunWindowCommand(MenuAction *action, unsigned button) { ClientNode *client = action->context; switch(action->type) { case MA_STICK: if(client->state.status & STAT_STICKY) { SetClientSticky(client, 0); } else { SetClientSticky(client, 1); } break; case MA_MAXIMIZE: if((client->state.maxFlags & MAX_HORIZ) && (client->state.maxFlags & MAX_VERT) && !(client->state.status & STAT_MINIMIZED)) { MaximizeClient(client, MAX_NONE); } else { MaximizeClient(client, MAX_VERT | MAX_HORIZ); } break; case MA_MAXIMIZE_H: if((client->state.maxFlags & MAX_HORIZ) && !(client->state.maxFlags & MAX_VERT) && !(client->state.status & STAT_MINIMIZED)) { MaximizeClient(client, MAX_NONE); } else { MaximizeClient(client, MAX_HORIZ); } break; case MA_MAXIMIZE_V: if((client->state.maxFlags & MAX_VERT) && !(client->state.maxFlags & MAX_HORIZ) && !(client->state.status & STAT_MINIMIZED)) { MaximizeClient(client, MAX_NONE); } else { MaximizeClient(client, MAX_VERT); } break; case MA_MINIMIZE: MinimizeClient(client, 1); break; case MA_RESTORE: RestoreClient(client, 1); break; case MA_CLOSE: DeleteClient(client); break; case MA_SENDTO: case MA_DESKTOP: SetClientDesktop(client, action->value); break; case MA_SHADE: if(client->state.status & STAT_SHADED) { UnshadeClient(client); } else { ShadeClient(client); } break; case MA_MOVE: MoveClientKeyboard(client); break; case MA_RESIZE: ResizeClientKeyboard(client); break; case MA_KILL: KillClient(client); break; case MA_LAYER: SetClientLayer(client, action->value); break; default: break; } }
/** Handle a client message. */ void HandleClientMessage(const XClientMessageEvent *event) { ClientNode *np; #ifdef DEBUG char *atomName; #endif np = FindClientByWindow(event->window); if(np) { 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, 1); break; case NormalState: RestoreClient(np, 1); break; default: break; } } else if(event->message_type == atoms[ATOM_NET_ACTIVE_WINDOW]) { RestoreClient(np, 1); UnshadeClient(np); 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)settings.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_MOVERESIZE]) { HandleNetWMMoveResize(event, np); } else if(event->message_type == atoms[ATOM_NET_RESTACK_WINDOW]) { HandleNetRestack(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_JWM_RELOAD]) { ReloadMenu(); } else if(event->message_type == atoms[ATOM_NET_CURRENT_DESKTOP]) { ChangeDesktop(event->data.l[0]); } else if(event->message_type == atoms[ATOM_NET_SHOWING_DESKTOP]) { ShowDesktop(); } 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_REQUEST_FRAME_EXTENTS]) { HandleFrameExtentsRequest(event); } else if(event->message_type == atoms[ATOM_NET_SYSTEM_TRAY_OPCODE]) { HandleDockEvent(event); } else { #ifdef DEBUG atomName = JXGetAtomName(display, event->message_type); Debug("ClientMessage to unknown window (0x%x): %s", event->window, atomName); JXFree(atomName); #endif } }
/** Process a key press event. */ void HandleKeyPress(const XKeyEvent *event) { ClientNode *np; KeyType key; SetMousePosition(event->x_root, event->y_root, event->window); key = GetKey(event); np = GetActiveClient(); switch(key & 0xFF) { case KEY_EXEC: RunKeyCommand(event); break; case KEY_DESKTOP: ChangeDesktop((key >> 8) - 1); break; case KEY_RDESKTOP: RightDesktop(); break; case KEY_LDESKTOP: LeftDesktop(); break; case KEY_UDESKTOP: AboveDesktop(); break; case KEY_DDESKTOP: BelowDesktop(); break; case KEY_SHOWDESK: ShowDesktop(); break; case KEY_SHOWTRAY: ShowAllTrays(); break; case KEY_NEXT: StartWindowWalk(); FocusNext(); break; case KEY_NEXTSTACK: StartWindowStackWalk(); WalkWindowStack(1); break; case KEY_PREV: StartWindowWalk(); FocusPrevious(); break; case KEY_PREVSTACK: StartWindowStackWalk(); WalkWindowStack(0); break; case KEY_CLOSE: if(np) { DeleteClient(np); } break; case KEY_SHADE: if(np) { if(np->state.status & STAT_SHADED) { UnshadeClient(np); } else { ShadeClient(np); } } break; case KEY_STICK: if(np) { if(np->state.status & STAT_STICKY) { SetClientSticky(np, 0); } else { SetClientSticky(np, 1); } } break; case KEY_MOVE: if(np) { MoveClientKeyboard(np); } break; case KEY_RESIZE: if(np) { ResizeClientKeyboard(np); } break; case KEY_MIN: if(np) { MinimizeClient(np, 1); } break; case KEY_MAX: if(np) { if(np->state.maxFlags) { MaximizeClient(np, MAX_NONE); } else { MaximizeClient(np, MAX_HORIZ | MAX_VERT); } } break; case KEY_MAXTOP: ToggleMaximized(np, MAX_TOP | MAX_HORIZ); break; case KEY_MAXBOTTOM: ToggleMaximized(np, MAX_BOTTOM | MAX_HORIZ); break; case KEY_MAXLEFT: ToggleMaximized(np, MAX_LEFT | MAX_VERT); break; case KEY_MAXRIGHT: ToggleMaximized(np, MAX_RIGHT | MAX_VERT); break; case KEY_MAXV: ToggleMaximized(np, MAX_VERT); break; case KEY_MAXH: ToggleMaximized(np, MAX_HORIZ); break; case KEY_ROOT: ShowKeyMenu(event); break; case KEY_WIN: if(np) { RaiseClient(np); ShowWindowMenu(np, np->x, np->y, 1); } break; case KEY_RESTART: Restart(); break; case KEY_EXIT: Exit(); break; case KEY_FULLSCREEN: if(np) { if(np->state.status & STAT_FULLSCREEN) { SetClientFullScreen(np, 0); } else { SetClientFullScreen(np, 1); } } break; case KEY_SENDR: if(np) { SetClientDesktop(np, GetRightDesktop(np->state.desktop)); } break; case KEY_SENDL: if(np) { SetClientDesktop(np, GetLeftDesktop(np->state.desktop)); } break; case KEY_SENDU: if(np) { SetClientDesktop(np, GetAboveDesktop(np->state.desktop)); } break; case KEY_SENDD: if(np) { SetClientDesktop(np, GetBelowDesktop(np->state.desktop)); } break; default: break; } DiscardEnterEvents(); }
/** Take the appropriate action for a click on a client border. */ void DispatchBorderButtonEvent(const XButtonEvent *event, ClientNode *np) { static Time lastClickTime = 0; static int lastX = 0, lastY = 0; static char doubleClickActive = 0; BorderActionType action; int bsize; /* Middle click starts a move unless it's over the maximize button. */ action = GetBorderActionType(np, event->x, event->y); if(event->button == Button2 && action != BA_MAXIMIZE) { MoveClient(np, event->x, event->y); return; } /* Determine the size of the border. */ if(np->state.border & BORDER_OUTLINE) { bsize = settings.borderWidth; } else { bsize = 0; } /* Other buttons are context sensitive. */ switch(action & 0x0F) { case BA_RESIZE: /* Border */ if(event->type == ButtonPress) { if(event->button == Button1) { ResizeClient(np, action, event->x, event->y); } else if(event->button == Button3) { const int x = np->x + event->x - bsize; const int y = np->y + event->y - settings.titleHeight - bsize; ShowWindowMenu(np, x, y, 0); } } break; case BA_MOVE: /* Title bar */ if(event->button == Button1) { if(event->type == ButtonPress) { if(doubleClickActive && event->time != lastClickTime && event->time - lastClickTime <= settings.doubleClickSpeed && abs(event->x - lastX) <= settings.doubleClickDelta && abs(event->y - lastY) <= settings.doubleClickDelta) { MaximizeClientDefault(np); doubleClickActive = 0; } else { if(MoveClient(np, event->x, event->y)) { doubleClickActive = 0; } else { doubleClickActive = 1; lastClickTime = event->time; lastX = event->x; lastY = event->y; } } } } else if(event->button == Button3) { const int x = np->x + event->x - bsize; const int y = np->y + event->y - settings.titleHeight - bsize; ShowWindowMenu(np, x, y, 0); } else if(event->button == Button4) { ShadeClient(np); } else if(event->button == Button5) { UnshadeClient(np); } break; case BA_MENU: /* Menu button */ if(event->button == Button4) { ShadeClient(np); } else if(event->button == Button5) { UnshadeClient(np); } else if(event->type == ButtonPress) { const int x = np->x + event->x - bsize; const int y = np->y + event->y - settings.titleHeight - bsize; ShowWindowMenu(np, x, y, 0); } break; case BA_CLOSE: /* Close button */ if(event->type == ButtonRelease && (event->button == Button1 || event->button == Button3)) { DeleteClient(np); } break; case BA_MAXIMIZE: /* Maximize button */ if(event->type == ButtonRelease) { switch(event->button) { case Button1: MaximizeClientDefault(np); break; case Button2: MaximizeClient(np, np->state.maxFlags ^ MAX_VERT); break; case Button3: MaximizeClient(np, np->state.maxFlags ^ MAX_HORIZ); break; default: break; } } break; case BA_MINIMIZE: /* Minimize button */ if(event->type == ButtonRelease) { if(event->button == Button3) { if(np->state.status & STAT_SHADED) { UnshadeClient(np); } else { ShadeClient(np); } } else if(event->button == Button1) { MinimizeClient(np, 1); } } break; default: break; } DiscardEnterEvents(); }
/** Handle a _NET_WM_STATE request. */ void HandleNetWMState(const XClientMessageEvent *event, ClientNode *np) { unsigned int x; MaxFlags maxFlags; char actionStick; char actionShade; char actionFullScreen; char actionMinimize; char actionNolist; char actionNopager; char actionBelow; char actionAbove; /* Up to two actions to be applied together. */ maxFlags = MAX_NONE; 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]) { maxFlags |= MAX_VERT; } else if(event->data.l[x] == (long)atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ]) { maxFlags |= MAX_HORIZ; } 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(maxFlags != MAX_NONE && np->state.maxFlags) { MaximizeClient(np, np->state.maxFlags & ~maxFlags); } if(actionShade) { UnshadeClient(np); } if(actionFullScreen) { SetClientFullScreen(np, 0); } if(actionMinimize) { RestoreClient(np, 0); } if(actionNolist) { np->state.status &= ~STAT_NOLIST; RequireTaskUpdate(); } if(actionNopager) { np->state.status &= ~STAT_NOPAGER; RequirePagerUpdate(); } 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(maxFlags != MAX_NONE) { MaximizeClient(np, np->state.maxFlags | maxFlags); } if(actionShade) { ShadeClient(np); } if(actionFullScreen) { SetClientFullScreen(np, 1); } if(actionMinimize) { MinimizeClient(np, 1); } if(actionNolist) { np->state.status |= STAT_NOLIST; RequireTaskUpdate(); } if(actionNopager) { np->state.status |= STAT_NOPAGER; RequirePagerUpdate(); } 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(maxFlags) { MaximizeClient(np, np->state.maxFlags ^ maxFlags); } 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; RequireTaskUpdate(); } if(actionNopager) { np->state.status ^= STAT_NOPAGER; RequirePagerUpdate(); } 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); } }
/** Add a window to management. */ ClientNode *AddClientWindow(Window w, char alreadyMapped, char notOwner) { XWindowAttributes attr; ClientNode *np; Assert(w != None); /* Get window attributes. */ if(JXGetWindowAttributes(display, w, &attr) == 0) { return NULL; } /* Determine if we should care about this window. */ if(attr.override_redirect == True) { return NULL; } if(attr.class == InputOnly) { return NULL; } /* Prepare a client node for this window. */ np = Allocate(sizeof(ClientNode)); memset(np, 0, sizeof(ClientNode)); np->window = w; np->owner = None; np->state.desktop = currentDesktop; np->x = attr.x; np->y = attr.y; np->width = attr.width; np->height = attr.height; np->cmap = attr.colormap; np->state.status = STAT_NONE; np->state.maxFlags = MAX_NONE; np->state.layer = LAYER_NORMAL; np->state.defaultLayer = LAYER_NORMAL; np->state.border = BORDER_DEFAULT; np->borderAction = BA_NONE; ReadClientInfo(np, alreadyMapped); if(!notOwner) { np->state.border = BORDER_OUTLINE | BORDER_TITLE | BORDER_MOVE; np->state.status |= STAT_WMDIALOG | STAT_STICKY; np->state.layer = LAYER_ABOVE; np->state.defaultLayer = LAYER_ABOVE; } ApplyGroups(np); if(np->icon == NULL) { LoadIcon(np); } /* We now know the layer, so insert */ np->prev = NULL; np->next = nodes[np->state.layer]; if(np->next) { np->next->prev = np; } else { nodeTail[np->state.layer] = np; } nodes[np->state.layer] = np; SetDefaultCursor(np->window); ReparentClient(np, notOwner); PlaceClient(np, alreadyMapped); if(!((np->state.status & STAT_FULLSCREEN) || np->state.maxFlags)) { int north, south, east, west; GetBorderSize(&np->state, &north, &south, &east, &west); if(np->parent != None) { JXMoveResizeWindow(display, np->parent, np->x - west, np->y - north, np->width + east + west, np->height + north + south); JXMoveResizeWindow(display, np->window, west, north, np->width, np->height); } else { JXMoveResizeWindow(display, np->window, np->x, np->y, np->width, np->height); } } /* If one of these fails we are SOL, so who cares. */ XSaveContext(display, np->window, clientContext, (void*)np); if(np->parent != None) { XSaveContext(display, np->parent, frameContext, (void*)np); } if(np->state.status & STAT_MAPPED) { JXMapWindow(display, np->window); if(np->parent != None) { JXMapWindow(display, np->parent); } } clientCount += 1; if(!alreadyMapped) { RaiseClient(np); } if(np->state.status & STAT_OPACITY) { SetOpacity(np, np->state.opacity, 1); } else { SetOpacity(np, settings.inactiveClientOpacity, 1); } if(np->state.status & STAT_STICKY) { SetCardinalAtom(np->window, ATOM_NET_WM_DESKTOP, ~0UL); } else { SetCardinalAtom(np->window, ATOM_NET_WM_DESKTOP, np->state.desktop); } /* Shade the client if requested. */ if(np->state.status & STAT_SHADED) { np->state.status &= ~STAT_SHADED; ShadeClient(np); } /* Minimize the client if requested. */ if(np->state.status & STAT_MINIMIZED) { np->state.status &= ~STAT_MINIMIZED; MinimizeClient(np, 0); } /* Maximize the client if requested. */ if(np->state.maxFlags) { const MaxFlags flags = np->state.maxFlags; np->state.maxFlags = MAX_NONE; MaximizeClient(np, flags); } if(np->state.status & STAT_URGENT) { RegisterCallback(URGENCY_DELAY, SignalUrgent, np); } /* Update task bars. */ AddClientToTaskBar(np); /* Make sure we're still in sync */ WriteState(np); SendConfigureEvent(np); /* Hide the client if we're not on the right desktop. */ if(np->state.desktop != currentDesktop && !(np->state.status & STAT_STICKY)) { HideClient(np); } ReadClientStrut(np); /* Focus transients if their parent has focus. */ if(np->owner != None) { if(activeClient && np->owner == activeClient->window) { FocusClient(np); } } /* Make the client fullscreen if requested. */ if(np->state.status & STAT_FULLSCREEN) { np->state.status &= ~STAT_FULLSCREEN; SetClientFullScreen(np, 1); } ResetBorder(np); return np; }
/** Process a key press event. */ void HandleKeyPress(const XKeyEvent *event) { ClientNode *np; KeyType key; key = GetKey(event); np = GetActiveClient(); switch(key & 0xFF) { case KEY_EXEC: RunKeyCommand(event); break; case KEY_DESKTOP: ChangeDesktop((key >> 8) - 1); break; case KEY_RDESKTOP: RightDesktop(); break; case KEY_LDESKTOP: LeftDesktop(); break; case KEY_UDESKTOP: AboveDesktop(); break; case KEY_DDESKTOP: BelowDesktop(); break; case KEY_SHOWDESK: ShowDesktop(); break; case KEY_SHOWTRAY: ShowAllTrays(); break; case KEY_NEXT: StartWindowWalk(); FocusNext(); break; case KEY_NEXTSTACK: StartWindowStackWalk(); WalkWindowStack(1); break; case KEY_PREV: StartWindowWalk(); FocusPrevious(); break; case KEY_PREVSTACK: StartWindowStackWalk(); WalkWindowStack(0); break; case KEY_CLOSE: if(np) { DeleteClient(np); } break; case KEY_SHADE: if(np) { if(np->state.status & STAT_SHADED) { UnshadeClient(np); } else { ShadeClient(np); } } break; case KEY_STICK: if(np) { if(np->state.status & STAT_STICKY) { SetClientSticky(np, 0); } else { SetClientSticky(np, 1); } } break; case KEY_MOVE: if(np) { MoveClientKeyboard(np); } break; case KEY_RESIZE: if(np) { ResizeClientKeyboard(np); } break; case KEY_MIN: if(np) { MinimizeClient(np, 1); } break; case KEY_MAX: if(np) { MaximizeClient(np, 1, 1); } break; case KEY_ROOT: ShowKeyMenu(event); break; case KEY_WIN: if(np) { ShowWindowMenu(np, np->x, np->y); } break; case KEY_RESTART: Restart(); break; case KEY_EXIT: Exit(); break; case KEY_FULLSCREEN: if(np) { if(np->state.status & STAT_FULLSCREEN) { SetClientFullScreen(np, 0); } else { SetClientFullScreen(np, 1); } } break; case KEY_SENDR: if(np) { SetClientDesktop(np, GetRightDesktop(np->state.desktop)); } break; case KEY_SENDL: if(np) { SetClientDesktop(np, GetLeftDesktop(np->state.desktop)); } break; case KEY_SENDU: if(np) { SetClientDesktop(np, GetAboveDesktop(np->state.desktop)); } break; case KEY_SENDD: if(np) { SetClientDesktop(np, GetBelowDesktop(np->state.desktop)); } break; default: break; } }
/** 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); } }
/** Take the appropriate action for a click on a client border. */ void DispatchBorderButtonEvent(const XButtonEvent *event, ClientNode *np) { static Time lastClickTime = 0; static int lastX = 0, lastY = 0; static int doubleClickActive = 0; BorderActionType action; int bsize; action = GetBorderActionType(np, event->x, event->y); switch(action & 0x0F) { case BA_RESIZE: if(event->type == ButtonPress) { ResizeClient(np, action, event->x, event->y); } break; case BA_MOVE: if(event->type == ButtonPress) { if(doubleClickActive && abs(event->time - lastClickTime) > 0 && abs(event->time - lastClickTime) <= doubleClickSpeed && abs(event->x - lastX) <= doubleClickDelta && abs(event->y - lastY) <= doubleClickDelta) { MaximizeClientDefault(np); doubleClickActive = 0; } else { if(MoveClient(np, event->x, event->y)) { doubleClickActive = 0; } else { doubleClickActive = 1; lastClickTime = event->time; lastX = event->x; lastY = event->y; } } } break; case BA_MENU: if(event->type == ButtonPress) { if(np->state.border & BORDER_OUTLINE) { bsize = borderWidth; } else { bsize = 0; } ShowWindowMenu(np, np->x + event->x - bsize, np->y + event->y - titleHeight - bsize); } break; case BA_CLOSE: if(event->type == ButtonRelease) { DeleteClient(np); } break; case BA_MAXIMIZE: if(event->type == ButtonRelease) { MaximizeClientDefault(np); } break; case BA_MINIMIZE: if(event->type == ButtonRelease) { MinimizeClient(np); } break; default: break; } }