Add support for _NET_WM_STATE_DEMANDS_ATTENTION.
_NET_WM_STATE_DEMANDS_ATTENTION indicates that some action in or with the window happened. It's a weaker hint than urgency flag of WM_HINTS, but some applications and almost all Qt applications use it instead of WM_HINTS' urgency flag (one example is Skype).
This commit is contained in:
parent
8327f837a0
commit
8a4a719093
|
@ -28,7 +28,8 @@ In the case of i3, the tasks (and order of them) are the following:
|
||||||
the first client of X) and manage them (reparent them, create window
|
the first client of X) and manage them (reparent them, create window
|
||||||
decorations, etc.)
|
decorations, etc.)
|
||||||
. When new windows are created, manage them
|
. When new windows are created, manage them
|
||||||
. Handle the client’s `_WM_STATE` property, but only the `_WM_STATE_FULLSCREEN`
|
. Handle the client’s `_WM_STATE` property, but only `_WM_STATE_FULLSCREEN` and
|
||||||
|
`_NET_WM_STATE_DEMANDS_ATTENTION`
|
||||||
. Handle the client’s `WM_NAME` property
|
. Handle the client’s `WM_NAME` property
|
||||||
. Handle the client’s size hints to display them proportionally
|
. Handle the client’s size hints to display them proportionally
|
||||||
. Handle the client’s urgency hint
|
. Handle the client’s urgency hint
|
||||||
|
|
|
@ -2,6 +2,7 @@ xmacro(_NET_SUPPORTED)
|
||||||
xmacro(_NET_SUPPORTING_WM_CHECK)
|
xmacro(_NET_SUPPORTING_WM_CHECK)
|
||||||
xmacro(_NET_WM_NAME)
|
xmacro(_NET_WM_NAME)
|
||||||
xmacro(_NET_WM_STATE_FULLSCREEN)
|
xmacro(_NET_WM_STATE_FULLSCREEN)
|
||||||
|
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
|
||||||
xmacro(_NET_WM_STATE)
|
xmacro(_NET_WM_STATE)
|
||||||
xmacro(_NET_WM_WINDOW_TYPE)
|
xmacro(_NET_WM_WINDOW_TYPE)
|
||||||
xmacro(_NET_WM_WINDOW_TYPE_DOCK)
|
xmacro(_NET_WM_WINDOW_TYPE_DOCK)
|
||||||
|
|
|
@ -324,6 +324,12 @@ bool con_has_urgent_child(Con *con);
|
||||||
*/
|
*/
|
||||||
void con_update_parents_urgency(Con *con);
|
void con_update_parents_urgency(Con *con);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set urgency flag to the container, all the parent containers and the workspace.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void con_set_urgency(Con *con, bool urgent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a string representing the subtree under con.
|
* Create a string representing the subtree under con.
|
||||||
*
|
*
|
||||||
|
|
39
src/con.c
39
src/con.c
|
@ -1530,6 +1530,45 @@ void con_update_parents_urgency(Con *con) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set urgency flag to the container, all the parent containers and the workspace.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void con_set_urgency(Con *con, bool urgent) {
|
||||||
|
if (focused == con) {
|
||||||
|
DLOG("Ignoring urgency flag for current client\n");
|
||||||
|
con->window->urgent.tv_sec = 0;
|
||||||
|
con->window->urgent.tv_usec = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (con->urgency_timer == NULL) {
|
||||||
|
con->urgent = urgent;
|
||||||
|
} else
|
||||||
|
DLOG("Discarding urgency WM_HINT because timer is running\n");
|
||||||
|
|
||||||
|
//CLIENT_LOG(con);
|
||||||
|
if (con->window) {
|
||||||
|
if (con->urgent) {
|
||||||
|
gettimeofday(&con->window->urgent, NULL);
|
||||||
|
} else {
|
||||||
|
con->window->urgent.tv_sec = 0;
|
||||||
|
con->window->urgent.tv_usec = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
con_update_parents_urgency(con);
|
||||||
|
|
||||||
|
if (con->urgent == urgent)
|
||||||
|
LOG("Urgency flag changed to %d\n", con->urgent);
|
||||||
|
|
||||||
|
Con *ws;
|
||||||
|
/* Set the urgency flag on the workspace, if a workspace could be found
|
||||||
|
* (for dock clients, that is not the case). */
|
||||||
|
if ((ws = con_get_workspace(con)) != NULL)
|
||||||
|
workspace_update_urgent_flag(ws);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a string representing the subtree under con.
|
* Create a string representing the subtree under con.
|
||||||
*
|
*
|
||||||
|
|
|
@ -164,5 +164,5 @@ void ewmh_setup_hints(void) {
|
||||||
/* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
|
/* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
|
||||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
|
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
|
||||||
|
|
||||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 16, supported_atoms);
|
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 19, supported_atoms);
|
||||||
}
|
}
|
||||||
|
|
|
@ -619,10 +619,10 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
||||||
|
|
||||||
LOG("ClientMessage for window 0x%08x\n", event->window);
|
LOG("ClientMessage for window 0x%08x\n", event->window);
|
||||||
if (event->type == A__NET_WM_STATE) {
|
if (event->type == A__NET_WM_STATE) {
|
||||||
if (event->format != 32 || event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN) {
|
if (event->format != 32 ||
|
||||||
DLOG("atom in clientmessage is %d, fullscreen is %d\n",
|
(event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN &&
|
||||||
event->data.data32[1], A__NET_WM_STATE_FULLSCREEN);
|
event->data.data32[1] != A__NET_WM_STATE_DEMANDS_ATTENTION)) {
|
||||||
DLOG("not about fullscreen atom\n");
|
DLOG("Unknown atom in clientmessage of type %d\n", event->data.data32[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,6 +632,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event->data.data32[1] == A__NET_WM_STATE_FULLSCREEN) {
|
||||||
/* Check if the fullscreen state should be toggled */
|
/* Check if the fullscreen state should be toggled */
|
||||||
if ((con->fullscreen_mode != CF_NONE &&
|
if ((con->fullscreen_mode != CF_NONE &&
|
||||||
(event->data.data32[0] == _NET_WM_STATE_REMOVE ||
|
(event->data.data32[0] == _NET_WM_STATE_REMOVE ||
|
||||||
|
@ -642,6 +643,15 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
||||||
DLOG("toggling fullscreen\n");
|
DLOG("toggling fullscreen\n");
|
||||||
con_toggle_fullscreen(con, CF_OUTPUT);
|
con_toggle_fullscreen(con, CF_OUTPUT);
|
||||||
}
|
}
|
||||||
|
} else if (event->data.data32[1] == A__NET_WM_STATE_DEMANDS_ATTENTION) {
|
||||||
|
/* Check if the urgent flag must be set or not */
|
||||||
|
if (event->data.data32[0] == _NET_WM_STATE_ADD)
|
||||||
|
con_set_urgency(con, true);
|
||||||
|
else if (event->data.data32[0] == _NET_WM_STATE_REMOVE)
|
||||||
|
con_set_urgency(con, false);
|
||||||
|
else if (event->data.data32[0] == _NET_WM_STATE_TOGGLE)
|
||||||
|
con_set_urgency(con, !con->urgent);
|
||||||
|
}
|
||||||
|
|
||||||
tree_render();
|
tree_render();
|
||||||
} else if (event->type == A__NET_ACTIVE_WINDOW) {
|
} else if (event->type == A__NET_ACTIVE_WINDOW) {
|
||||||
|
@ -833,44 +843,12 @@ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_
|
||||||
if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply))
|
if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!con->urgent && focused == con) {
|
|
||||||
DLOG("Ignoring urgency flag for current client\n");
|
|
||||||
con->window->urgent.tv_sec = 0;
|
|
||||||
con->window->urgent.tv_usec = 0;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the flag on the client directly */
|
/* Update the flag on the client directly */
|
||||||
bool hint_urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0);
|
bool hint_urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0);
|
||||||
|
con_set_urgency(con, hint_urgent);
|
||||||
if (con->urgency_timer == NULL) {
|
|
||||||
con->urgent = hint_urgent;
|
|
||||||
} else
|
|
||||||
DLOG("Discarding urgency WM_HINT because timer is running\n");
|
|
||||||
|
|
||||||
//CLIENT_LOG(con);
|
|
||||||
if (con->window) {
|
|
||||||
if (con->urgent) {
|
|
||||||
gettimeofday(&con->window->urgent, NULL);
|
|
||||||
} else {
|
|
||||||
con->window->urgent.tv_sec = 0;
|
|
||||||
con->window->urgent.tv_usec = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
con_update_parents_urgency(con);
|
|
||||||
|
|
||||||
LOG("Urgency flag changed to %d\n", con->urgent);
|
|
||||||
|
|
||||||
Con *ws;
|
|
||||||
/* Set the urgency flag on the workspace, if a workspace could be found
|
|
||||||
* (for dock clients, that is not the case). */
|
|
||||||
if ((ws = con_get_workspace(con)) != NULL)
|
|
||||||
workspace_update_urgent_flag(ws);
|
|
||||||
|
|
||||||
tree_render();
|
tree_render();
|
||||||
|
|
||||||
end:
|
|
||||||
if (con->window)
|
if (con->window)
|
||||||
window_update_hints(con->window, reply);
|
window_update_hints(con->window, reply);
|
||||||
else free(reply);
|
else free(reply);
|
||||||
|
@ -1094,7 +1072,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
|
||||||
|
|
||||||
/* Client message are sent to the root window. The only interesting
|
/* Client message are sent to the root window. The only interesting
|
||||||
* client message for us is _NET_WM_STATE, we honour
|
* client message for us is _NET_WM_STATE, we honour
|
||||||
* _NET_WM_STATE_FULLSCREEN */
|
* _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION */
|
||||||
case XCB_CLIENT_MESSAGE:
|
case XCB_CLIENT_MESSAGE:
|
||||||
handle_client_message((xcb_client_message_event_t*)event);
|
handle_client_message((xcb_client_message_event_t*)event);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -17,14 +17,42 @@
|
||||||
use i3test i3_autostart => 0;
|
use i3test i3_autostart => 0;
|
||||||
use List::Util qw(first);
|
use List::Util qw(first);
|
||||||
|
|
||||||
|
my $_NET_WM_STATE_REMOVE = 0;
|
||||||
|
my $_NET_WM_STATE_ADD = 1;
|
||||||
|
my $_NET_WM_STATE_TOGGLE = 2;
|
||||||
|
|
||||||
|
sub set_urgency {
|
||||||
|
my ($win, $urgent_flag, $type) = @_;
|
||||||
|
if ($type == 1) {
|
||||||
|
$win->add_hint('urgency') if ($urgent_flag);
|
||||||
|
$win->delete_hint('urgency') if (!$urgent_flag);
|
||||||
|
} elsif ($type == 2) {
|
||||||
|
my $msg = pack "CCSLLLLLL",
|
||||||
|
X11::XCB::CLIENT_MESSAGE, # response_type
|
||||||
|
32, # format
|
||||||
|
0, # sequence
|
||||||
|
$win->id, # window
|
||||||
|
$x->atom(name => '_NET_WM_STATE')->id, # message type
|
||||||
|
($urgent_flag ? $_NET_WM_STATE_ADD : $_NET_WM_STATE_REMOVE), # data32[0]
|
||||||
|
$x->atom(name => '_NET_WM_STATE_DEMANDS_ATTENTION')->id, # data32[1]
|
||||||
|
0, # data32[2]
|
||||||
|
0, # data32[3]
|
||||||
|
0; # data32[4]
|
||||||
|
|
||||||
|
$x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my $config = <<EOT;
|
my $config = <<EOT;
|
||||||
# i3 config file (v4)
|
# i3 config file (v4)
|
||||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
|
|
||||||
force_display_urgency_hint 0ms
|
force_display_urgency_hint 0ms
|
||||||
EOT
|
EOT
|
||||||
my $pid = launch_with_config($config);
|
|
||||||
|
|
||||||
|
my $type;
|
||||||
|
for ($type = 1; $type <= 2; $type++) {
|
||||||
|
my $pid = launch_with_config($config);
|
||||||
my $tmp = fresh_workspace;
|
my $tmp = fresh_workspace;
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
@ -44,7 +72,7 @@ is(@urgent, 0, 'no window got the urgent flag');
|
||||||
#####################################################################
|
#####################################################################
|
||||||
# Add the urgency hint, switch to a different workspace and back again
|
# Add the urgency hint, switch to a different workspace and back again
|
||||||
#####################################################################
|
#####################################################################
|
||||||
$top->add_hint('urgency');
|
set_urgency($top, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
my @content = @{get_ws_content($tmp)};
|
my @content = @{get_ws_content($tmp)};
|
||||||
|
@ -61,7 +89,7 @@ cmd '[id="' . $top->id . '"] focus';
|
||||||
@urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
|
@urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
|
||||||
is(@urgent, 0, 'no window got the urgent flag after focusing');
|
is(@urgent, 0, 'no window got the urgent flag after focusing');
|
||||||
|
|
||||||
$top->add_hint('urgency');
|
set_urgency($top, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
@urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
|
@urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
|
||||||
|
@ -76,7 +104,7 @@ ok(!$ws->{urgent}, 'urgent flag not set on workspace');
|
||||||
|
|
||||||
my $otmp = fresh_workspace;
|
my $otmp = fresh_workspace;
|
||||||
|
|
||||||
$top->add_hint('urgency');
|
set_urgency($top, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
$ws = get_ws($tmp);
|
$ws = get_ws($tmp);
|
||||||
|
@ -97,7 +125,7 @@ my $different_window = open_window;
|
||||||
is($x->input_focus, $different_window->id, 'new window focused');
|
is($x->input_focus, $different_window->id, 'new window focused');
|
||||||
|
|
||||||
# Add the urgency hint on the other window.
|
# Add the urgency hint on the other window.
|
||||||
$top->add_hint('urgency');
|
set_urgency($top, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
# Now try to switch to that window and see if focus changes.
|
# Now try to switch to that window and see if focus changes.
|
||||||
|
@ -113,20 +141,20 @@ is($x->input_focus, $top->id, 'urgent window focused');
|
||||||
cmd "workspace $otmp";
|
cmd "workspace $otmp";
|
||||||
is($x->input_focus, $different_window->id, 'new window focused again');
|
is($x->input_focus, $different_window->id, 'new window focused again');
|
||||||
|
|
||||||
$top->add_hint('urgency');
|
set_urgency($top, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
$bottom->add_hint('urgency');
|
set_urgency($bottom, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
cmd '[urgent=latest] focus';
|
cmd '[urgent=latest] focus';
|
||||||
is($x->input_focus, $bottom->id, 'latest urgent window focused');
|
is($x->input_focus, $bottom->id, 'latest urgent window focused');
|
||||||
$bottom->delete_hint('urgency');
|
set_urgency($bottom, 0, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
cmd '[urgent=latest] focus';
|
cmd '[urgent=latest] focus';
|
||||||
is($x->input_focus, $top->id, 'second urgent window focused');
|
is($x->input_focus, $top->id, 'second urgent window focused');
|
||||||
$top->delete_hint('urgency');
|
set_urgency($top, 0, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -137,20 +165,20 @@ sync_with_i3;
|
||||||
cmd "workspace $otmp";
|
cmd "workspace $otmp";
|
||||||
is($x->input_focus, $different_window->id, 'new window focused again');
|
is($x->input_focus, $different_window->id, 'new window focused again');
|
||||||
|
|
||||||
$top->add_hint('urgency');
|
set_urgency($top, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
$bottom->add_hint('urgency');
|
set_urgency($bottom, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
cmd '[urgent=oldest] focus';
|
cmd '[urgent=oldest] focus';
|
||||||
is($x->input_focus, $top->id, 'oldest urgent window focused');
|
is($x->input_focus, $top->id, 'oldest urgent window focused');
|
||||||
$top->delete_hint('urgency');
|
set_urgency($top, 0, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
cmd '[urgent=oldest] focus';
|
cmd '[urgent=oldest] focus';
|
||||||
is($x->input_focus, $bottom->id, 'oldest urgent window focused');
|
is($x->input_focus, $bottom->id, 'oldest urgent window focused');
|
||||||
$bottom->delete_hint('urgency');
|
set_urgency($bottom, 0, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -190,8 +218,8 @@ is($urgent, 0, 'no window got the urgent flag');
|
||||||
|
|
||||||
cmd '[id="' . $win2->id . '"] focus';
|
cmd '[id="' . $win2->id . '"] focus';
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
$win5->add_hint('urgency');
|
set_urgency($win5, 1, $type);
|
||||||
$win6->add_hint('urgency');
|
set_urgency($win6, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
# we should have 5 urgent cons. win5, win6 and their 3 split parents.
|
# we should have 5 urgent cons. win5, win6 and their 3 split parents.
|
||||||
|
@ -224,7 +252,7 @@ my $floating_win = open_floating_window;
|
||||||
# switch away
|
# switch away
|
||||||
fresh_workspace;
|
fresh_workspace;
|
||||||
|
|
||||||
$floating_win->add_hint('urgency');
|
set_urgency($floating_win, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
|
||||||
cmd "workspace $tmp";
|
cmd "workspace $tmp";
|
||||||
|
@ -242,7 +270,7 @@ my $w1 = open_window;
|
||||||
my $w2 = open_window;
|
my $w2 = open_window;
|
||||||
cmd "workspace $ws2";
|
cmd "workspace $ws2";
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
$w1->add_hint('urgency');
|
set_urgency($w1, 1, $type);
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
cmd '[id="' . $w1->id . '"] kill';
|
cmd '[id="' . $w1->id . '"] kill';
|
||||||
sync_with_i3;
|
sync_with_i3;
|
||||||
|
@ -250,6 +278,7 @@ my $w = get_ws($ws1);
|
||||||
is($w->{urgent}, 0, 'Urgent flag no longer set after killing the window ' .
|
is($w->{urgent}, 0, 'Urgent flag no longer set after killing the window ' .
|
||||||
'from another workspace');
|
'from another workspace');
|
||||||
|
|
||||||
|
|
||||||
exit_gracefully($pid);
|
exit_gracefully($pid);
|
||||||
|
}
|
||||||
|
|
||||||
done_testing;
|
done_testing;
|
||||||
|
|
Loading…
Reference in New Issue