Merge pull request #1871 from Airblader/feature-1770

Introduce command criterion value __focused__
This commit is contained in:
Michael Stapelberg 2015-08-30 21:55:37 +02:00
commit 768fe4ca69
3 changed files with 158 additions and 17 deletions

View File

@ -1604,25 +1604,35 @@ for_window [class="^evil-app$"] floating enable, move container to workspace 4
The criteria which are currently implemented are: The criteria which are currently implemented are:
class:: class::
Compares the window class (the second part of WM_CLASS) Compares the window class (the second part of WM_CLASS). Use the
special value +__focused__+ to match all windows having the same window
class as the currently focused window.
instance:: instance::
Compares the window instance (the first part of WM_CLASS) Compares the window instance (the first part of WM_CLASS). Use the
special value +__focused__+ to match all windows having the same window
instance as the currently focused window.
window_role:: window_role::
Compares the window role (WM_WINDOW_ROLE). Compares the window role (WM_WINDOW_ROLE). Use the special value
+__focused__+ to match all windows having the same window role as the
currently focused window.
window_type:: window_type::
Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
+normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+, +normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
+popup_menu+ and +toolti+. +popup_menu+ and +tooltip+.
id:: id::
Compares the X11 window ID, which you can get via +xwininfo+ for example. Compares the X11 window ID, which you can get via +xwininfo+ for example.
title:: title::
Compares the X11 window title (_NET_WM_NAME or WM_NAME as fallback). Compares the X11 window title (_NET_WM_NAME or WM_NAME as fallback).
Use the special value +__focused__+ to match all windows having the
same window title as the currently focused window.
urgent:: urgent::
Compares the urgent state of the window. Can be "latest" or "oldest". Compares the urgent state of the window. Can be "latest" or "oldest".
Matches the latest or oldest urgent window, respectively. Matches the latest or oldest urgent window, respectively.
(The following aliases are also available: newest, last, recent, first) (The following aliases are also available: newest, last, recent, first)
workspace:: workspace::
Compares the workspace name of the workspace the window belongs to. Compares the workspace name of the workspace the window belongs to. Use
the special value +__focused__+ to match all windows in the currently
focused workspace.
con_mark:: con_mark::
Compares the mark set for this container, see <<vim_like_marks>>. Compares the mark set for this container, see <<vim_like_marks>>.
con_id:: con_id::

View File

@ -90,8 +90,12 @@ bool match_matches_window(Match *match, i3Window *window) {
LOG("Checking window 0x%08x (class %s)\n", window->id, window->class_class); LOG("Checking window 0x%08x (class %s)\n", window->id, window->class_class);
if (match->class != NULL) { if (match->class != NULL) {
if (window->class_class != NULL && if (window->class_class == NULL)
regex_matches(match->class, window->class_class)) { return false;
if (strcmp(match->class->pattern, "__focused__") == 0 &&
strcmp(window->class_class, focused->window->class_class) == 0) {
LOG("window class matches focused window\n");
} else if (regex_matches(match->class, window->class_class)) {
LOG("window class matches (%s)\n", window->class_class); LOG("window class matches (%s)\n", window->class_class);
} else { } else {
return false; return false;
@ -99,8 +103,12 @@ bool match_matches_window(Match *match, i3Window *window) {
} }
if (match->instance != NULL) { if (match->instance != NULL) {
if (window->class_instance != NULL && if (window->class_instance == NULL)
regex_matches(match->instance, window->class_instance)) { return false;
if (strcmp(match->instance->pattern, "__focused__") == 0 &&
strcmp(window->class_instance, focused->window->class_instance) == 0) {
LOG("window instance matches focused window\n");
} else if (regex_matches(match->instance, window->class_instance)) {
LOG("window instance matches (%s)\n", window->class_instance); LOG("window instance matches (%s)\n", window->class_instance);
} else { } else {
return false; return false;
@ -117,17 +125,27 @@ bool match_matches_window(Match *match, i3Window *window) {
} }
if (match->title != NULL) { if (match->title != NULL) {
if (window->name != NULL && if (window->name == NULL)
regex_matches(match->title, i3string_as_utf8(window->name))) { return false;
LOG("title matches (%s)\n", i3string_as_utf8(window->name));
const char *title = i3string_as_utf8(window->name);
if (strcmp(match->title->pattern, "__focused__") == 0 &&
strcmp(title, i3string_as_utf8(focused->window->name)) == 0) {
LOG("window title matches focused window\n");
} else if (regex_matches(match->title, title)) {
LOG("title matches (%s)\n", title);
} else { } else {
return false; return false;
} }
} }
if (match->window_role != NULL) { if (match->window_role != NULL) {
if (window->role != NULL && if (window->role == NULL)
regex_matches(match->window_role, window->role)) { return false;
if (strcmp(match->window_role->pattern, "__focused__") == 0 &&
strcmp(window->role, focused->window->role) == 0) {
LOG("window role matches focused window\n");
} else if (regex_matches(match->window_role, window->role)) {
LOG("window_role matches (%s)\n", window->role); LOG("window_role matches (%s)\n", window->role);
} else { } else {
return false; return false;
@ -182,7 +200,10 @@ bool match_matches_window(Match *match, i3Window *window) {
if (ws == NULL) if (ws == NULL)
return false; return false;
if (regex_matches(match->workspace, ws->name)) { if (strcmp(match->workspace->pattern, "__focused__") == 0 &&
strcmp(ws->name, con_get_workspace(focused)->name) == 0) {
LOG("workspace matches focused workspace\n");
} else if (regex_matches(match->workspace, ws->name)) {
LOG("workspace matches (%s)\n", ws->name); LOG("workspace matches (%s)\n", ws->name);
} else { } else {
return false; return false;

View File

@ -0,0 +1,110 @@
#!perl
# vim:ts=4:sw=4:expandtab
#
# Please read the following documents before working on tests:
# • http://build.i3wm.org/docs/testsuite.html
# (or docs/testsuite)
#
# • http://build.i3wm.org/docs/lib-i3test.html
# (alternatively: perldoc ./testcases/lib/i3test.pm)
#
# • http://build.i3wm.org/docs/ipc.html
# (or docs/ipc)
#
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
# (unless you are already familiar with Perl)
#
# Tests for the special value "__focused__" in command criteria.
# Ticket: #1770
use i3test;
use X11::XCB qw(PROP_MODE_REPLACE);
my ($ws);
sub open_window_with_role {
my ($role) = @_;
open_window(
before_map => sub {
my ($window) = @_;
my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
my $atomtype = $x->atom(name => 'STRING');
$x->change_property(
PROP_MODE_REPLACE,
$window->id,
$atomname->id,
$atomtype->id,
8,
length($role) + 1,
"$role\x00"
);
}
);
}
###############################################################################
# 1: Test __focused__ for window class.
###############################################################################
$ws = fresh_workspace;
open_window(wm_class => 'notme');
open_window(wm_class => 'magic');
open_window(wm_class => 'magic');
is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
cmd '[class=__focused__] move to workspace trash';
is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for window class');
###############################################################################
# 2: Test __focused__ for window instance.
###############################################################################
$ws = fresh_workspace;
open_window(instance => 'notme', wm_class => 'test');
open_window(instance => 'magic', wm_class => 'test');
open_window(instance => 'magic', wm_class => 'test');
is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
cmd '[instance=__focused__] move to workspace trash';
is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for window instance');
###############################################################################
# 3: Test __focused__ for window title.
###############################################################################
$ws = fresh_workspace;
open_window(name => 'notme');
open_window(name => 'magic');
open_window(name => 'magic');
is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
cmd '[title=__focused__] move to workspace trash';
is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for title');
###############################################################################
# 4: Test __focused__ for window role.
###############################################################################
$ws = fresh_workspace;
open_window_with_role("notme");
open_window_with_role("magic");
open_window_with_role("magic");
is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
cmd '[window_role=__focused__] move to workspace trash';
is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for window_role');
###############################################################################
# 5: Test __focused__ for workspace.
###############################################################################
$ws = fresh_workspace;
open_window;
open_window;
is(@{get_ws($ws)->{nodes}}, 2, 'sanity check: workspace contains two windows');
cmd '[workspace=__focused__] move to workspace trash';
is(@{get_ws($ws)->{nodes}}, 0, '__focused__ works for workspace');
###############################################################################
done_testing;