/*- * Copyright (c) 2001 Jordan DeLong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "wm.h" /* * remove the input focus from a client; the client may or may * not actually be the one with the X input focus right now, but it * must be the "focused" client on it's workspace. this function * first removes the focus from the client, and also cycles to the * next client in the focus list that this client is on, updating * the workspace focused pointer accordingly. * * for non click-to-focus modes this method is a lot simpler, * because we don't need to focus a new window. */ void focus_unfocus(client_t *client) { client_t *focusthis; /* * make sure the window is focused (not neccessarily with the * X input focus, but the 'focused' window on it's workspace). */ if (client->workspace->focused != client) return; /* cycle to the next one in the focus lists */ if (options.focus == FOCUS_CLICK) { focusthis = TAILQ_NEXT(client, c_focus); if (focusthis) goto gotit; focusthis = TAILQ_FIRST(&client->workspace->w_foclist); if (focusthis && focusthis != client) goto gotit; goto nofocus; gotit: focus_setfocused(focusthis); return; } /* * for non click-to-focus modes, or when there is not a suitable * client on the focus list we set focus to none. either by just * setting the workspace->focused pointer to nothing or if it's * on the current workspace by doing a focus_none call. */ nofocus: client->workspace->focused = NULL; if (client_focused == client) focus_none(client->screen); } /* * set the input focus to none: our dummy input focus window for * screen. if there's a focused window we prepare it to lose the * focus by doing grabs for the click_focus stuff, and decor_unfocus. */ void focus_none(screen_t *screen) { if (client_focused) { if (options.focus == FOCUS_CLICK) XGrabButton(display, AnyButton, AnyModifier, client_focused->frame, 1, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); decor_unfocus(client_focused); } XSetInputFocus(display, screen->nofocus, RevertToNone, CurrentTime); client_focused = NULL; plugin_focus_change(NULL); } /* * focus a client but take into account that it might be on a different * workspace or desktop than is currently visible. So either set * the client's workspace->focused pointer or call focus_client * depending on that. */ void focus_setfocused(client_t *client) { if (client->flags.nofocus) return; /* * determine if the client is on the current workspace, or * if it is on some other desktop/workspace. */ if (client->workspace == client->screen->desktop->current_space) focus_client(client); else client->workspace->focused = client; } /* * focus a specific client; this call is to actually set input focus * to a client, and shouldn't be used when the client may not be on * the current workspace (use focus_setfocused). */ void focus_client(client_t *client) { client_t *lastfocused; /* ignore clients w/ nofocus flag */ if (client->flags.nofocus) return; /* prepare a focused client to lose focus */ lastfocused = client_focused; if (lastfocused) { if (options.focus == FOCUS_CLICK) XGrabButton(display, AnyButton, AnyModifier, lastfocused->frame, 1, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); decor_unfocus(lastfocused); } /* prepare this client to recieve the focus */ if (options.focus == FOCUS_CLICK) { XUngrabButton(display, AnyButton, AnyModifier, client->frame); buttongrabhack(client->frame, AnyButton, options.mouse_modifier); } /* now we actually focus it */ decor_focus(client); XSetInputFocus(display, client->window, RevertToNone, CurrentTime); client_focused = client; client->workspace->focused = client; plugin_focus_change(client); #if 0 /* * XXX: this probably requires implementation of windows-style * focus cycling. */ /* * reorder this workspace's focus list to put the lastfocused * window right before the newly focused one. */ if (lastfocused && lastfocused->workspace == client->workspace) { focus_list_rm(lastfocused); focus_list_add(lastfocused); } #endif } /* add a client onto the focus list for it's workspace */ void focus_list_add(client_t *client) { if (client->workspace->focused) TAILQ_INSERT_BEFORE(client->workspace->focused, client, c_focus); else TAILQ_INSERT_HEAD(&client->workspace->w_foclist, client, c_focus); } /* remove a client from the focus list for it's workspace */ void focus_list_rm(client_t *client) { TAILQ_REMOVE(&client->workspace->w_foclist, client, c_focus); }