/*-
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1