X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fhandlers.c;h=c217cbbcc80916eec4a1490b8ed4df73e3920921;hb=92000942039fa99d7334ca5099b467b0d3d17792;hp=048d3a3a17992008326f6994bc57a99421ab7202;hpb=fb4ee17b054afa2f85ad64480353f554e5eae2d3;p=i3%2Fi3 diff --git a/src/handlers.c b/src/handlers.c index 048d3a3a..c217cbbc 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1,3 +1,5 @@ +#undef I3__FILE__ +#define I3__FILE__ "handlers.c" /* * vim:ts=4:sw=4:expandtab * @@ -11,6 +13,7 @@ #include "all.h" #include +#include #include #include #include @@ -55,10 +58,11 @@ bool event_is_ignored(const int sequence, const int response_type) { event = SLIST_NEXT(event, ignore_events); SLIST_REMOVE(&ignore_events, save, Ignore_Event, ignore_events); free(save); - } else event = SLIST_NEXT(event, ignore_events); + } else + event = SLIST_NEXT(event, ignore_events); } - SLIST_FOREACH(event, &ignore_events, ignore_events) { + SLIST_FOREACH (event, &ignore_events, ignore_events) { if (event->sequence != sequence) continue; @@ -156,10 +160,10 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { } /* see if the user entered the window on a certain window decoration */ - int layout = (enter_child ? con->parent->layout : con->layout); + layout_t layout = (enter_child ? con->parent->layout : con->layout); if (layout == L_DEFAULT) { Con *child; - TAILQ_FOREACH(child, &(con->nodes_head), nodes) + TAILQ_FOREACH (child, &(con->nodes_head), nodes) if (rect_contains(child->deco_rect, event->event_x, event->event_y)) { LOG("using child %p / %s instead!\n", child, child->name); con = child; @@ -180,6 +184,10 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { if (config.disable_focus_follows_mouse) return; + /* if this container is already focused, there is nothing to do. */ + if (con == focused) + return; + /* Get the currently focused workspace to check if the focus change also * involves changing workspaces. If so, we need to call workspace_show() to * correctly update state and send the IPC event. */ @@ -201,7 +209,6 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { * */ static void handle_motion_notify(xcb_motion_notify_event_t *event) { - last_timestamp = event->time; /* Skip events where the pointer was over a child window, we are only @@ -211,6 +218,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) { Con *con; if ((con = con_by_frame_id(event->event)) == NULL) { + DLOG("MotionNotify for an unknown container, checking if it crosses screen boundaries.\n"); check_crossing_screen_boundary(event->root_x, event->root_y); return; } @@ -223,7 +231,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) { /* see over which rect the user is */ Con *current; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { if (!rect_contains(current->deco_rect, event->event_x, event->event_y)) continue; @@ -290,7 +298,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { Con *con; DLOG("window 0x%08x wants to be at %dx%d with %dx%d\n", - event->window, event->x, event->y, event->width, event->height); + event->window, event->x, event->y, event->width, event->height); /* For unmanaged windows, we just execute the configure request. As soon as * it gets mapped, we will take over anyways. */ @@ -300,12 +308,13 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { uint32_t mask = 0; uint32_t values[7]; int c = 0; -#define COPY_MASK_MEMBER(mask_member, event_member) do { \ - if (event->value_mask & mask_member) { \ - mask |= mask_member; \ - values[c++] = event->event_member; \ - } \ -} while (0) +#define COPY_MASK_MEMBER(mask_member, event_member) \ + do { \ + if (event->value_mask & mask_member) { \ + mask |= mask_member; \ + values[c++] = event->event_member; \ + } \ + } while (0) COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_X, x); COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_Y, y); @@ -322,9 +331,21 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { } DLOG("Configure request!\n"); - if (con_is_floating(con) && con_is_leaf(con)) { + + Con *workspace = con_get_workspace(con), + *fullscreen = NULL; + + /* There might not be a corresponding workspace for dock cons, therefore we + * have to be careful here. */ + if (workspace) { + fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT); + if (!fullscreen) + fullscreen = con_get_fullscreen_con(workspace, CF_GLOBAL); + } + + if (fullscreen != con && con_is_floating(con) && con_is_leaf(con)) { /* find the height for the decorations */ - int deco_height = config.font.height + 5; + int deco_height = con->deco_rect.height; /* we actually need to apply the size/position changes to the *parent* * container */ Rect bsr = con_border_style_rect(con); @@ -334,6 +355,11 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { } Con *floatingcon = con->parent; + if (strcmp(con_get_workspace(floatingcon)->name, "__i3_scratch") == 0) { + DLOG("This is a scratchpad container, ignoring ConfigureRequest\n"); + return; + } + Rect newrect = floatingcon->rect; if (event->value_mask & XCB_CONFIG_WINDOW_X) { @@ -402,8 +428,23 @@ int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_n static void handle_screen_change(xcb_generic_event_t *e) { DLOG("RandR screen change\n"); + /* The geometry of the root window is used for “fullscreen global” and + * changes when new outputs are added. */ + xcb_get_geometry_cookie_t cookie = xcb_get_geometry(conn, root); + xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(conn, cookie, NULL); + if (reply == NULL) { + ELOG("Could not get geometry of the root window, exiting\n"); + exit(1); + } + DLOG("root geometry reply: (%d, %d) %d x %d\n", reply->x, reply->y, reply->width, reply->height); + + croot->rect.width = reply->width; + croot->rect.height = reply->height; + randr_query_outputs(); + scratchpad_fix_resolution(); + ipc_send_event("output", I3_IPC_EVENT_OUTPUT, "{\"change\":\"unspecified\"}"); return; @@ -492,20 +533,38 @@ static void handle_destroy_notify_event(xcb_destroy_notify_event_t *event) { handle_unmap_notify_event(&unmap); } +static bool window_name_changed(i3Window *window, char *old_name) { + if ((old_name == NULL) && (window->name == NULL)) + return false; + + /* Either the old or the new one is NULL, but not both. */ + if ((old_name == NULL) ^ (window->name == NULL)) + return true; + + return (strcmp(old_name, i3string_as_utf8(window->name)) != 0); +} + /* * Called when a window changes its title * */ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { + xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; + char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); + window_update_name(con->window, prop, false); x_push_changes(croot); + if (window_name_changed(con->window, old_name)) + ipc_send_window_event("title", con); + + FREE(old_name); + return true; } @@ -515,15 +574,22 @@ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t * */ static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { + xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; + char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); + window_update_name_legacy(con->window, prop, false); x_push_changes(croot); + if (window_name_changed(con->window, old_name)) + ipc_send_window_event("title", con); + + FREE(old_name); + return true; } @@ -592,15 +658,15 @@ static void handle_expose_event(xcb_expose_event_t *event) { static void handle_client_message(xcb_client_message_event_t *event) { /* If this is a startup notification ClientMessage, the library will handle * it and call our monitor_event() callback. */ - if (sn_xcb_display_process_event(sndisplay, (xcb_generic_event_t*)event)) + if (sn_xcb_display_process_event(sndisplay, (xcb_generic_event_t *)event)) return; LOG("ClientMessage for window 0x%08x\n", event->window); if (event->type == A__NET_WM_STATE) { - if (event->format != 32 || event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN) { - DLOG("atom in clientmessage is %d, fullscreen is %d\n", - event->data.data32[1], A__NET_WM_STATE_FULLSCREEN); - DLOG("not about fullscreen atom\n"); + if (event->format != 32 || + (event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN && + event->data.data32[1] != A__NET_WM_STATE_DEMANDS_ATTENTION)) { + DLOG("Unknown atom in clientmessage of type %d\n", event->data.data32[1]); return; } @@ -610,24 +676,77 @@ static void handle_client_message(xcb_client_message_event_t *event) { return; } - /* Check if the fullscreen state should be toggled */ - if ((con->fullscreen_mode != CF_NONE && - (event->data.data32[0] == _NET_WM_STATE_REMOVE || - event->data.data32[0] == _NET_WM_STATE_TOGGLE)) || - (con->fullscreen_mode == CF_NONE && - (event->data.data32[0] == _NET_WM_STATE_ADD || - event->data.data32[0] == _NET_WM_STATE_TOGGLE))) { - DLOG("toggling fullscreen\n"); - con_toggle_fullscreen(con, CF_OUTPUT); + if (event->data.data32[1] == A__NET_WM_STATE_FULLSCREEN) { + /* Check if the fullscreen state should be toggled */ + if ((con->fullscreen_mode != CF_NONE && + (event->data.data32[0] == _NET_WM_STATE_REMOVE || + event->data.data32[0] == _NET_WM_STATE_TOGGLE)) || + (con->fullscreen_mode == CF_NONE && + (event->data.data32[0] == _NET_WM_STATE_ADD || + event->data.data32[0] == _NET_WM_STATE_TOGGLE))) { + DLOG("toggling fullscreen\n"); + 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(); + } else if (event->type == A__NET_ACTIVE_WINDOW) { + if (event->format != 32) + return; + + DLOG("_NET_ACTIVE_WINDOW: Window 0x%08x should be activated\n", event->window); + + Con *con = con_by_window_id(event->window); + if (con == NULL) { + DLOG("Could not get window for client message\n"); + return; + } + + Con *ws = con_get_workspace(con); + + if (ws == NULL) { + DLOG("Window is not being managed, ignoring _NET_ACTIVE_WINDOW\n"); + return; + } + + if (con_is_internal(ws)) { + DLOG("Workspace is internal, ignoring _NET_ACTIVE_WINDOW\n"); + return; + } + + /* data32[0] indicates the source of the request (application or pager) */ + if (event->data.data32[0] == 2) { + /* Always focus the con if it is from a pager, because this is most + * likely from some user action */ + DLOG("This request came from a pager. Focusing con = %p\n", con); + workspace_show(ws); + con_focus(con); + } else { + /* If the request is from an application, only focus if the + * workspace is visible. Otherwise set the urgency hint. */ + if (workspace_is_visible(ws)) { + DLOG("Request to focus con on a visible workspace. Focusing con = %p\n", con); + workspace_show(ws); + con_focus(con); + } else { + DLOG("Request to focus con on a hidden workspace. Setting urgent con = %p\n", con); + con_set_urgency(con, true); + } } tree_render(); - x_push_changes(croot); } else if (event->type == A_I3_SYNC) { - DLOG("i3 sync, yay\n"); xcb_window_t window = event->data.data32[0]; uint32_t rnd = event->data.data32[1]; - DLOG("Sending random value %d back to X11 window 0x%08x\n", rnd, window); + DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window); void *reply = scalloc(32); xcb_client_message_event_t *ev = reply; @@ -639,9 +758,39 @@ static void handle_client_message(xcb_client_message_event_t *event) { ev->data.data32[0] = window; ev->data.data32[1] = rnd; - xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char*)ev); + xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev); xcb_flush(conn); free(reply); + } else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) { + /* + * A client can request an estimate for the frame size which the window + * manager will put around it before actually mapping its window. Java + * does this (as of openjdk-7). + * + * Note that the calculation below is not entirely accurate — once you + * set a different border type, it’s off. We _could_ request all the + * window properties (which have to be set up at this point according + * to EWMH), but that seems rather elaborate. The standard explicitly + * says the application must cope with an estimate that is not entirely + * accurate. + */ + DLOG("_NET_REQUEST_FRAME_EXTENTS for window 0x%08x\n", event->window); + + /* The reply data: approximate frame size */ + Rect r = { + config.default_border_width, /* left */ + config.default_border_width, /* right */ + config.font.height + 5, /* top */ + config.default_border_width /* bottom */ + }; + xcb_change_property( + conn, + XCB_PROP_MODE_REPLACE, + event->window, + A__NET_FRAME_EXTENTS, + XCB_ATOM_CARDINAL, 32, 4, + &r); + xcb_flush(conn); } else { DLOG("unhandled clientmessage\n"); return; @@ -666,7 +815,7 @@ int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_wi * */ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { + xcb_atom_t name, xcb_get_property_reply_t *reply) { Con *con = con_by_window_id(window); if (con == NULL) { DLOG("Received WM_NORMAL_HINTS for unknown client\n"); @@ -675,7 +824,7 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat xcb_size_hints_t size_hints; - //CLIENT_LOG(client); + //CLIENT_LOG(client); /* If the hints were already in this event, use them, if not, request them */ if (reply != NULL) @@ -750,21 +899,18 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat goto render_and_return; /* Check if we need to set proportional_* variables using the correct ratio */ + double aspect_ratio = 0.0; if ((width / height) < min_aspect) { - if (con->proportional_width != width || - con->proportional_height != (width / min_aspect)) { - con->proportional_width = width; - con->proportional_height = width / min_aspect; - changed = true; - } + aspect_ratio = min_aspect; } else if ((width / height) > max_aspect) { - if (con->proportional_width != width || - con->proportional_height != (width / max_aspect)) { - con->proportional_width = width; - con->proportional_height = width / max_aspect; - changed = true; - } - } else goto render_and_return; + aspect_ratio = max_aspect; + } else + goto render_and_return; + + if (fabs(con->aspect_ratio - aspect_ratio) > DBL_EPSILON) { + con->aspect_ratio = aspect_ratio; + changed = true; + } render_and_return: if (changed) @@ -778,54 +924,20 @@ render_and_return: * */ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { + xcb_atom_t name, xcb_get_property_reply_t *reply) { Con *con = con_by_window_id(window); if (con == NULL) { DLOG("Received WM_HINTS for unknown client\n"); return false; } - xcb_icccm_wm_hints_t hints; - + bool urgency_hint; if (reply == NULL) - if (!(reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL))) - return false; - - if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply)) - 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 */ - con->urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0); - //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; - } - } - 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); - + reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL); + window_update_hints(con->window, reply, &urgency_hint); + con_set_urgency(con, urgency_hint); tree_render(); -end: - if (con->window) - window_update_hints(con->window, reply); - else free(reply); return true; } @@ -837,7 +949,7 @@ end: * */ static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { + xcb_atom_t name, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { @@ -847,7 +959,8 @@ static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t sta if (prop == NULL) { prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, - false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32), NULL); + false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32), + NULL); if (prop == NULL) return false; } @@ -863,14 +976,15 @@ static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t sta * */ static bool handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { + xcb_atom_t name, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; if (prop == NULL) { prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, - false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32), NULL); + false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32), + NULL); if (prop == NULL) return false; } @@ -942,14 +1056,13 @@ struct property_handler_t { }; static struct property_handler_t property_handlers[] = { - { 0, 128, handle_windowname_change }, - { 0, UINT_MAX, handle_hints }, - { 0, 128, handle_windowname_change_legacy }, - { 0, UINT_MAX, handle_normal_hints }, - { 0, UINT_MAX, handle_clientleader_change }, - { 0, UINT_MAX, handle_transient_for }, - { 0, 128, handle_windowrole_change } -}; + {0, 128, handle_windowname_change}, + {0, UINT_MAX, handle_hints}, + {0, 128, handle_windowname_change_legacy}, + {0, UINT_MAX, handle_normal_hints}, + {0, UINT_MAX, handle_clientleader_change}, + {0, UINT_MAX, handle_transient_for}, + {0, 128, handle_windowrole_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) /* @@ -958,7 +1071,6 @@ static struct property_handler_t property_handlers[] = { * */ void property_handlers_init(void) { - sn_monitor_context_new(sndisplay, conn_screen, startup_monitor_event, NULL, NULL); property_handlers[0].atom = A__NET_WM_NAME; @@ -974,7 +1086,7 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) struct property_handler_t *handler = NULL; xcb_get_property_reply_t *propr = NULL; - for (int c = 0; c < sizeof(property_handlers) / sizeof(struct property_handler_t); c++) { + for (size_t c = 0; c < sizeof(property_handlers) / sizeof(struct property_handler_t); c++) { if (property_handlers[c].atom != atom) continue; @@ -1011,61 +1123,62 @@ void handle_event(int type, xcb_generic_event_t *event) { switch (type) { case XCB_KEY_PRESS: - handle_key_press((xcb_key_press_event_t*)event); + case XCB_KEY_RELEASE: + handle_key_press((xcb_key_press_event_t *)event); break; case XCB_BUTTON_PRESS: - handle_button_press((xcb_button_press_event_t*)event); + handle_button_press((xcb_button_press_event_t *)event); break; case XCB_MAP_REQUEST: - handle_map_request((xcb_map_request_event_t*)event); + handle_map_request((xcb_map_request_event_t *)event); break; case XCB_UNMAP_NOTIFY: - handle_unmap_notify_event((xcb_unmap_notify_event_t*)event); + handle_unmap_notify_event((xcb_unmap_notify_event_t *)event); break; case XCB_DESTROY_NOTIFY: - handle_destroy_notify_event((xcb_destroy_notify_event_t*)event); + handle_destroy_notify_event((xcb_destroy_notify_event_t *)event); break; case XCB_EXPOSE: - handle_expose_event((xcb_expose_event_t*)event); + handle_expose_event((xcb_expose_event_t *)event); break; case XCB_MOTION_NOTIFY: - handle_motion_notify((xcb_motion_notify_event_t*)event); + handle_motion_notify((xcb_motion_notify_event_t *)event); break; /* Enter window = user moved his mouse over the window */ case XCB_ENTER_NOTIFY: - handle_enter_notify((xcb_enter_notify_event_t*)event); + handle_enter_notify((xcb_enter_notify_event_t *)event); break; /* Client message are sent to the root window. The only interesting * 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: - handle_client_message((xcb_client_message_event_t*)event); + handle_client_message((xcb_client_message_event_t *)event); break; /* Configure request = window tried to change size on its own */ case XCB_CONFIGURE_REQUEST: - handle_configure_request((xcb_configure_request_event_t*)event); + handle_configure_request((xcb_configure_request_event_t *)event); break; /* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */ case XCB_MAPPING_NOTIFY: - handle_mapping_notify((xcb_mapping_notify_event_t*)event); + handle_mapping_notify((xcb_mapping_notify_event_t *)event); break; case XCB_FOCUS_IN: - handle_focus_in((xcb_focus_in_event_t*)event); + handle_focus_in((xcb_focus_in_event_t *)event); break; case XCB_PROPERTY_NOTIFY: { - xcb_property_notify_event_t *e = (xcb_property_notify_event_t*)event; + xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)event; last_timestamp = e->time; property_notify(e->state, e->window, e->atom); break;