Skip to content

Commit

Permalink
Add nofocus
Browse files Browse the repository at this point in the history
*nofocus* is a modification to certain classes of windows which prevents
them from being focused automatically - the only way to give them the input
focus is by clicking on them. This prevents accidentally moving/resizing
things like system trays or clocks.

- `nofocus` is now a class action, which enables this behavior for a
  given class of windows.
- `ClientModel` has been augmented to store which windows are/are not
  autofocusable. It also allows the focus to be forced, which is useful
  when clicking on windows.
- Added tests to `ClientModel` as well as `WMConfig` ensure that the
  modifications work.
  • Loading branch information
adamnew123456 committed Dec 27, 2015
1 parent bd64e5c commit b6fc626
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 79 deletions.
3 changes: 3 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ The possibilities for a class action are:
- `xpos:X` and `ypos:Y` set the relative position of the window on the screen. `X` and `Y` are decimals in the range 0 to 100,
inclusive. For example, setting `xpos:50` puts the window's left edge in the middle of the screen (because `xpos:50` is
equivalent to saying that the X position should be 50 percent of the screen's width).
- `nofocus` prevents SmallWM from automatically focusing windows of the given class. This is useful for windows like system trays,
clocks, or other utility windows that you don't want to manipulate by accident. SmallWM will not focus this window after moving
it or resizing it; SmallWM will also not focus this window when another is closed, or when switching virtual desktops.

Keyboard Bindings
=================
Expand Down
6 changes: 6 additions & 0 deletions src/configparse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ int WMConfig::config_parser(void *user, const char *c_section,
action.relative_y = relative_y;
}
}
else if (!strcmp(stripped, "nofocus"))
{
// This looks different, because it is not an action, but a
// persistent setting which is respected in multiple places
self->no_autofocus.push_back(name);
}

delete[] stripped;
} while((option = strtok(NULL, ",")));
Expand Down
4 changes: 4 additions & 0 deletions src/configparse.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <map>
#include <string>
#include <utility>
#include <vector>

#include "ini.h"
#include "actions.h"
Expand Down Expand Up @@ -181,6 +182,9 @@ class WMConfig
/// Handles all the configured class actions.
std::map<std::string, ClassActions> classactions;

/// All of the X11 classes which should not be focusable by default
std::vector<std::string> no_autofocus;

/// Whether or not to show images inside icons for hidden windows
bool show_icons;

Expand Down
59 changes: 57 additions & 2 deletions src/model/client-model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ void ClientModel::get_visible_in_layer_order(std::vector<Window> &return_clients
* Adds a new client with some basic initial state.
*/
void ClientModel::add_client(Window client, InitialState state,
Dimension2D location, Dimension2D size)
Dimension2D location, Dimension2D size, bool autofocus)
{
if (DIM2D_WIDTH(size) <= 0 || DIM2D_HEIGHT(size) <= 0)
return;
Expand Down Expand Up @@ -124,7 +124,13 @@ void ClientModel::add_client(Window client, InitialState state,
m_screen[client] = screen_box;
}

focus(client);
if (autofocus)
{
set_autofocus(client, true);
focus(client);
}
else
set_autofocus(client, false);
}

/**
Expand Down Expand Up @@ -156,6 +162,7 @@ void ClientModel::remove_client(Window client)
m_size.erase(client);
m_cps_mode.erase(client);
m_screen.erase(client);
m_autofocus.erase(client);

m_changes.push(new DestroyChange(client, desktop, layer));
}
Expand Down Expand Up @@ -262,22 +269,70 @@ Window ClientModel::get_focused()
return m_focused;
}

/**
* Returns true if a window can be automatically focused, or false otherwise.
*/
bool ClientModel::is_autofocusable(Window client)
{
if (!is_client(client))
return false;

return m_autofocus[client];
}

/**
* Either allows, or prevents, a client from being autofocused.
*/
void ClientModel::set_autofocus(Window client, bool can_autofocus)
{
if (!is_client(client))
return;

m_autofocus[client] = can_autofocus;
}

/**
* Changes the focus to another window. Note that this fails if the client
* is not currently visible.
*
* This is used for automated focusing, and is subject to the rules of
* autofocusing - in particular, if the user enables nofocus for this type of
* window, then this will do nothing.
*/
void ClientModel::focus(Window client)
{
if (!is_visible(client))
return;

if (!m_autofocus[client])
return;

Window old_focus = m_focused;
m_focused = client;

m_current_desktop->focus_history.push(client);
m_changes.push(new ChangeFocus(old_focus, client));
}

/**
* Forces a window to be focused, ignoring the nofocus policy.
*/
void ClientModel::force_focus(Window client)
{
if (!is_visible(client))
return;

Window old_focus = m_focused;
m_focused = client;

// Since the focus history is used for automatic focusing, avoid
// polluting it with windows that cannot be focused
if (m_autofocus[client])
m_current_desktop->focus_history.push(client);

m_changes.push(new ChangeFocus(old_focus, client));
}

/**
* Unfocuses a window if it is currently focused.
*/
Expand Down
9 changes: 8 additions & 1 deletion src/model/client-model.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class ClientModel
void get_visible_clients(std::vector<Window>&);
void get_visible_in_layer_order(std::vector<Window>&);

void add_client(Window, InitialState, Dimension2D, Dimension2D);
void add_client(Window, InitialState, Dimension2D, Dimension2D, bool);
void remove_client(Window);
void unmap_client(Window);

Expand All @@ -102,7 +102,11 @@ class ClientModel
bool remove_from_focus_history(Window);

Window get_focused();
bool is_autofocusable(Window);
void set_autofocus(Window, bool);

void focus(Window);
void force_focus(Window);
void unfocus();
void unfocus_if_focused(Window);

Expand Down Expand Up @@ -166,6 +170,9 @@ class ClientModel
/// A mapping between clients and their screens
std::map<Window, Box> m_screen;

/// Which clients may be auto-focused, and which may not
std::map<Window, bool> m_autofocus;

/** A mapping between clients that are iconified, or being moved/resized,
and whether or not they were stuck before they were moved/resized or
iconfied. */
Expand Down
15 changes: 11 additions & 4 deletions src/x-events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ void XEvents::handle_buttonpress()
m_clients.start_resizing(m_event.xbutton.subwindow);
}
else if (is_client) // Any other click on a client focuses that client
m_clients.focus(m_event.xbutton.window);
m_clients.force_focus(m_event.xbutton.window);
}

/**
Expand Down Expand Up @@ -509,18 +509,25 @@ void XEvents::add_window(Window window)
hints.initial_state == IconicState)
init_state = IS_HIDDEN;

std::string win_class;
m_xdata.get_class(window, win_class);
bool should_focus =
std::find(m_config.no_autofocus.begin(),
m_config.no_autofocus.end(),
win_class) ==
m_config.no_autofocus.end();

m_clients.add_client(window, init_state,
Dimension2D(win_attr.x, win_attr.y),
Dimension2D(win_attr.width, win_attr.height));
Dimension2D(win_attr.width, win_attr.height),
should_focus);

// If the client is a dialog, this will be represented in the transient
// hint (which is None if the client is not a dialog, or not-None if it is)
if (m_xdata.get_transient_hint(window) != None)
m_clients.set_layer(window, DIALOG_LAYER);

// Finally, execute the actions tied to the window's class
std::string win_class;
m_xdata.get_class(window, win_class);

if (m_config.classactions.count(win_class) > 0 && init_state != IS_HIDDEN)
{
Expand Down
2 changes: 2 additions & 0 deletions src/x-events.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#ifndef __SMALLWM_X_EVENTS__
#define __SMALLWM_X_EVENTS__

#include <algorithm>

#include "model/client-model.h"
#include "model/focus-cycle.h"
#include "model/x-model.h"
Expand Down
Loading

0 comments on commit b6fc626

Please sign in to comment.