diff --git a/ChangeLog b/ChangeLog index c7593ec8d..62c77dbf9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +Sat Jun 19 02:21:08 2004 Soeren Sandmann + + Fix bug 143333, support for update counter spec, and 109362, + schedule compensation events when events are ignored. + + * src/display.c (meta_display_open): Add _NET_WM_SYNC_REQUEST and + _NET_WM_SYNC_REQUEST_COUNTER atoms. Remove the old + METACITY_SYNC_COUNTER stuff. + (meta_display_begin_op): Setup the sync counter + + * src/xprops.c, src/xprops.h, src/window-props.c, src/display.h: + Add new atoms. + + * src/window.c (send_sync_request): new function. + (meta_window_move_resize_internal): send a sync request before + resizing. + (check_move_resize_frequence): Rework logic to also check the SYNC + case. If an event is ignored return the remaining time. + (update_resize_timeout): Timeout that gets called when a + compensation event is scheduled. + (uddate_resize): schedule compensation events when an event is + ignored. + (meta_window_handle_mouse_grap_op_event): When an alarm is + received and sync was turned off, turn it back on. + + * src/window.h (struct MetaWindow) Add some variables + 2004-06-16 Havoc Pennington * configure.in: bump version, add the UNSTABLE note diff --git a/src/display.c b/src/display.c index cd0f686a8..50ff95834 100644 --- a/src/display.c +++ b/src/display.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2002, 2003 Red Hat, Inc. + * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * * This program is free software; you can redistribute it and/or @@ -270,8 +270,8 @@ meta_display_open (const char *name) "_NET_WM_STATE_BELOW", "_NET_STARTUP_ID", "_METACITY_TOGGLE_VERBOSE", - "_METACITY_UPDATE_COUNTER", - "SYNC_COUNTER", + "_NET_WM_SYNC_REQUEST", + "_NET_WM_SYNC_REQUEST_COUNTER", "_GNOME_PANEL_ACTION", "_GNOME_PANEL_ACTION_MAIN_MENU", "_GNOME_PANEL_ACTION_RUN_DIALOG", @@ -323,7 +323,7 @@ meta_display_open (const char *name) display->expected_focus_window = NULL; #ifdef HAVE_XSYNC - display->grab_update_alarm = None; + display->grab_sync_request_alarm = None; #endif /* FIXME copy the checks from GDK probably */ @@ -416,8 +416,8 @@ meta_display_open (const char *name) display->atom_net_wm_state_below = atoms[71]; display->atom_net_startup_id = atoms[72]; display->atom_metacity_toggle_verbose = atoms[73]; - display->atom_metacity_update_counter = atoms[74]; - display->atom_sync_counter = atoms[75]; + display->atom_net_wm_sync_request = atoms[74]; + display->atom_net_wm_sync_request_counter = atoms[75]; display->atom_gnome_panel_action = atoms[76]; display->atom_gnome_panel_action_main_menu = atoms[77]; display->atom_gnome_panel_action_run_dialog = atoms[78]; @@ -1264,7 +1264,7 @@ event_callback (XEvent *event, #ifdef HAVE_XSYNC if (META_DISPLAY_HAS_XSYNC (display) && event->type == (display->xsync_event_base + XSyncAlarmNotify) && - ((XSyncAlarmNotifyEvent*)event)->alarm == display->grab_update_alarm) + ((XSyncAlarmNotifyEvent*)event)->alarm == display->grab_sync_request_alarm) { filter_out_event = TRUE; /* GTK doesn't want to see this really */ @@ -1272,7 +1272,7 @@ event_callback (XEvent *event, display->grab_window != NULL && event->xany.serial >= display->grab_start_serial && grab_op_is_mouse (display->grab_op)) - meta_window_handle_mouse_grab_op_event (display->grab_window, event); + meta_window_handle_mouse_grab_op_event (display->grab_window, event); } #endif /* HAVE_XSYNC */ @@ -2969,10 +2969,16 @@ meta_display_begin_grab_op (MetaDisplay *display, display->grab_last_moveresize_time.tv_usec = 0; display->grab_motion_notify_time = 0; #ifdef HAVE_XSYNC - display->grab_update_alarm = None; + display->grab_sync_request_alarm = None; #endif display->grab_was_cancelled = FALSE; + if (display->grab_resize_timeout_id) + { + g_source_remove (display->grab_resize_timeout_id); + display->grab_resize_timeout_id = 0; + } + if (display->grab_window) { display->grab_initial_window_pos = display->grab_window->rect; @@ -3007,28 +3013,41 @@ meta_display_begin_grab_op (MetaDisplay *display, #ifdef HAVE_XSYNC if (!display->grab_wireframe_active && meta_grab_op_is_resizing (display->grab_op) && - display->grab_window->update_counter != None) + display->grab_window->sync_request_counter != None) { XSyncAlarmAttributes values; + XSyncValue init; - /* trigger when we make a positive transition to a value - * one higher than the current value. - */ - values.trigger.counter = display->grab_window->update_counter; - values.trigger.value_type = XSyncRelative; + meta_error_trap_push_with_return (display); + + /* Set the counter to 0, so we know that the application's + * responses to the client messages will always trigger + * a PositiveTransition + */ + + XSyncIntToValue (&init, 0); + XSyncSetCounter (display->xdisplay, + display->grab_window->sync_request_counter, init); + + display->grab_window->sync_request_serial = 0; + display->grab_window->sync_request_time.tv_sec = 0; + display->grab_window->sync_request_time.tv_usec = 0; + + values.trigger.counter = display->grab_window->sync_request_counter; + values.trigger.value_type = XSyncAbsolute; values.trigger.test_type = XSyncPositiveTransition; - XSyncIntToValue (&values.trigger.wait_value, 1); - + XSyncIntToValue (&values.trigger.wait_value, + display->grab_window->sync_request_serial + 1); + /* After triggering, increment test_value by this. * (NOT wait_value above) */ XSyncIntToValue (&values.delta, 1); - + /* we want events (on by default anyway) */ values.events = True; - meta_error_trap_push_with_return (display); - display->grab_update_alarm = XSyncCreateAlarm (display->xdisplay, + display->grab_sync_request_alarm = XSyncCreateAlarm (display->xdisplay, XSyncCACounter | XSyncCAValueType | XSyncCAValue | @@ -3036,12 +3055,13 @@ meta_display_begin_grab_op (MetaDisplay *display, XSyncCADelta | XSyncCAEvents, &values); + if (meta_error_trap_pop_with_return (display, FALSE) != Success) - display->grab_update_alarm = None; + display->grab_sync_request_alarm = None; meta_topic (META_DEBUG_RESIZING, "Created update alarm 0x%lx\n", - display->grab_update_alarm); + display->grab_sync_request_alarm); } #endif } @@ -3138,10 +3158,11 @@ meta_display_end_grab_op (MetaDisplay *display, } #ifdef HAVE_XSYNC - if (display->grab_update_alarm != None) + if (display->grab_sync_request_alarm != None) { XSyncDestroyAlarm (display->xdisplay, - display->grab_update_alarm); + display->grab_sync_request_alarm); + display->grab_sync_request_alarm = None; } #endif /* HAVE_XSYNC */ @@ -3177,6 +3198,12 @@ meta_display_end_grab_op (MetaDisplay *display, meta_ui_resize_popup_free (display->grab_resize_popup); display->grab_resize_popup = NULL; } + + if (display->grab_resize_timeout_id) + { + g_source_remove (display->grab_resize_timeout_id); + display->grab_resize_timeout_id = 0; + } } static void diff --git a/src/display.h b/src/display.h index 216363755..b49dcc1fd 100644 --- a/src/display.h +++ b/src/display.h @@ -166,8 +166,8 @@ struct _MetaDisplay Atom atom_net_wm_state_below; Atom atom_net_startup_id; Atom atom_metacity_toggle_verbose; - Atom atom_metacity_update_counter; - Atom atom_sync_counter; + Atom atom_net_wm_sync_request; + Atom atom_net_wm_sync_request_counter; Atom atom_gnome_panel_action; Atom atom_gnome_panel_action_main_menu; Atom atom_gnome_panel_action_run_dialog; @@ -258,9 +258,10 @@ struct _MetaDisplay int xkb_base_event_type; #endif #ifdef HAVE_XSYNC - /* alarm monitoring client's _METACITY_UPDATE_COUNTER */ - XSyncAlarm grab_update_alarm; + /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */ + XSyncAlarm grab_sync_request_alarm; #endif + int grab_resize_timeout_id; /* Keybindings stuff */ MetaKeyBinding *screen_bindings; diff --git a/src/window-props.c b/src/window-props.c index 3dae259a6..6f9b4b675 100644 --- a/src/window-props.c +++ b/src/window-props.c @@ -385,7 +385,7 @@ init_update_counter (MetaDisplay *display, MetaPropValue *value) { value->type = META_PROP_VALUE_SYNC_COUNTER; - value->atom = display->atom_metacity_update_counter; + value->atom = display->atom_net_wm_sync_request_counter; } static void @@ -397,9 +397,9 @@ reload_update_counter (MetaWindow *window, #ifdef HAVE_XSYNC XSyncCounter counter = value->v.xcounter; - window->update_counter = counter; - meta_verbose ("Window has _METACITY_UPDATE_COUNTER 0x%lx\n", - window->update_counter); + window->sync_request_counter = counter; + meta_verbose ("Window has _NET_WM_SYNC_REQUEST_COUNTER 0x%lx\n", + window->sync_request_counter); #endif } } @@ -924,7 +924,7 @@ meta_display_init_window_prop_hooks (MetaDisplay *display) hooks[i].reload_func = reload_net_startup_id; ++i; - hooks[i].property = display->atom_metacity_update_counter; + hooks[i].property = display->atom_net_wm_sync_request_counter; hooks[i].init_func = init_update_counter; hooks[i].reload_func = reload_update_counter; ++i; diff --git a/src/window.c b/src/window.c index 4bacd6b37..0d70e974d 100644 --- a/src/window.c +++ b/src/window.c @@ -357,7 +357,10 @@ meta_window_new_with_attrs (MetaDisplay *display, window->workspaces = NULL; #ifdef HAVE_XSYNC - window->update_counter = None; + window->sync_request_counter = None; + window->sync_request_serial = 0; + window->sync_request_time.tv_sec = 0; + window->sync_request_time.tv_usec = 0; #endif window->screen = NULL; @@ -446,6 +449,7 @@ meta_window_new_with_attrs (MetaDisplay *display, window->calc_placement = FALSE; window->shaken_loose = FALSE; window->have_focus_click_grab = FALSE; + window->disable_sync = FALSE; window->unmaps_pending = 0; @@ -528,7 +532,7 @@ meta_window_new_with_attrs (MetaDisplay *display, initial_props[i++] = XA_WM_ICON_NAME; initial_props[i++] = display->atom_net_wm_desktop; initial_props[i++] = display->atom_net_startup_id; - initial_props[i++] = display->atom_metacity_update_counter; + initial_props[i++] = display->atom_net_wm_sync_request_counter; initial_props[i++] = XA_WM_NORMAL_HINTS; initial_props[i++] = display->atom_wm_protocols; initial_props[i++] = XA_WM_HINTS; @@ -2297,6 +2301,36 @@ get_mouse_deltas_for_resize (MetaWindow *window, } } +#ifdef HAVE_XSYNC +static void +send_sync_request (MetaWindow *window) +{ + XSyncValue value; + XClientMessageEvent ev; + + window->sync_request_serial++; + + XSyncIntToValue (&value, window->sync_request_serial); + + ev.type = ClientMessage; + ev.window = window->xwindow; + ev.message_type = window->display->atom_wm_protocols; + ev.format = 32; + ev.data.l[0] = window->display->atom_net_wm_sync_request; + ev.data.l[1] = meta_display_get_current_time (window->display); + ev.data.l[2] = XSyncValueLow32 (value); + ev.data.l[3] = XSyncValueHigh32 (value); + + /* We don't need to trap errors here as we are already + * inside an error_trap_push()/pop() pair. + */ + XSendEvent (window->display->xdisplay, + window->xwindow, False, 0, (XEvent*) &ev); + + g_get_current_time (&window->sync_request_time); +} +#endif + static void meta_window_move_resize_internal (MetaWindow *window, MetaMoveResizeFlags flags, @@ -2627,10 +2661,20 @@ meta_window_move_resize_internal (MetaWindow *window, } meta_error_trap_push (window->display); + + if (window->sync_request_counter != None && + window->display->grab_sync_request_alarm != None && + window->sync_request_time.tv_usec == 0 && + window->sync_request_time.tv_sec == 0) + { + send_sync_request (window); + } + XConfigureWindow (window->display->xdisplay, window->xwindow, mask, &values); + meta_error_trap_pop (window->display, FALSE); } @@ -4364,12 +4408,12 @@ process_property_notify (MetaWindow *window, meta_window_reload_property (window, window->display->atom_net_startup_id); } - else if (event->atom == window->display->atom_metacity_update_counter) + else if (event->atom == window->display->atom_net_wm_sync_request_counter) { - meta_verbose ("Property notify on %s for _METACITY_UPDATE_COUNTER\n", window->desc); + meta_verbose ("Property notify on %s for _NET_WM_SYNC_REQUEST_COUNTER\n", window->desc); meta_window_reload_property (window, - window->display->atom_metacity_update_counter); + window->display->atom_net_wm_sync_request_counter); } return TRUE; @@ -5802,59 +5846,94 @@ meta_window_show_menu (MetaWindow *window, meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp); } -static void -clear_moveresize_time (MetaWindow *window) +static double +timeval_to_ms (const GTimeVal *timeval) { - /* Forces the next update to actually do something */ - window->display->grab_last_moveresize_time.tv_sec = 0; - window->display->grab_last_moveresize_time.tv_usec = 0; + return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0; +} + +static double +time_diff (const GTimeVal *first, + const GTimeVal *second) +{ + double first_ms = timeval_to_ms (first); + double second_ms = timeval_to_ms (second); + + return first_ms - second_ms; } static gboolean -check_moveresize_frequency (MetaWindow *window) +check_moveresize_frequency (MetaWindow *window, + gdouble *remaining) { GTimeVal current_time; - double elapsed; - double max_resizes_per_second; g_get_current_time (¤t_time); - /* use milliseconds, 1000 milliseconds/second */ - elapsed = - ((((double)current_time.tv_sec - window->display->grab_last_moveresize_time.tv_sec) * G_USEC_PER_SEC + - (current_time.tv_usec - window->display->grab_last_moveresize_time.tv_usec))) / 1000.0; - #ifdef HAVE_XSYNC - if (window->display->grab_update_alarm != None) - max_resizes_per_second = 1.0; /* this is max resizes without - * getting any alarms; we resize - * immediately if we get one. - * i.e. this is a timeout for the - * client getting stuck. - */ + if (!window->disable_sync && + window->display->grab_sync_request_alarm != None) + { + if (window->sync_request_time.tv_sec != 0 || + window->sync_request_time.tv_usec != 0) + { + double elapsed = + time_diff (¤t_time, &window->sync_request_time); + + if (elapsed < 1000.0) + { + /* We want to be sure that the timeout happens at + * a time where elapsed will definitely be + * greater than 1000, so we can disable sync + */ + if (remaining) + *remaining = 1000.0 - elapsed + 100; + + return FALSE; + } + else + { + /* We have now waited for more than a second for the + * application to respond to the sync request + */ + window->disable_sync = TRUE; + return TRUE; + } + } + else + { + /* No outstanding sync requests. Go ahead and resize + */ + return TRUE; + } + } else #endif /* HAVE_XSYNC */ - max_resizes_per_second = 20.0; - -#define EPSILON (1e-6) - if (elapsed >= 0.0 && elapsed < (1000.0 / max_resizes_per_second)) { + const double max_resizes_per_second = 25.0; + const double ms_between_resizes = 1000.0 / max_resizes_per_second; + double elapsed; + + elapsed = time_diff (¤t_time, &window->display->grab_last_moveresize_time); + + if (elapsed >= 0.0 && elapsed < ms_between_resizes) + { + meta_topic (META_DEBUG_RESIZING, + "Delaying move/resize as only %g of %g ms elapsed\n", + elapsed, ms_between_resizes); + + if (remaining) + *remaining = (ms_between_resizes - elapsed); + + return FALSE; + } + meta_topic (META_DEBUG_RESIZING, - "Delaying move/resize as only %g of %g seconds elapsed\n", - elapsed / 1000.0, 1.0 / max_resizes_per_second); - return FALSE; + " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n", + elapsed / 1000.0, 1.0 / max_resizes_per_second); + + return TRUE; } - else if (elapsed < (0.0 - EPSILON)) /* handle clock getting set backward */ - clear_moveresize_time (window); - - /* store latest time */ - window->display->grab_last_moveresize_time = current_time; - - meta_topic (META_DEBUG_RESIZING, - " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n", - elapsed / 1000.0, 1.0 / max_resizes_per_second); - - return TRUE; } static void @@ -6005,15 +6084,34 @@ update_move (MetaWindow *window, } } +static void update_resize (MetaWindow *window, + int x, + int y, + gboolean force); + +static gboolean +update_resize_timeout (gpointer data) +{ + MetaWindow *window = data; + + update_resize (window, + window->display->grab_latest_motion_x, + window->display->grab_latest_motion_y, + TRUE); + return FALSE; +} + static void update_resize (MetaWindow *window, - int x, int y) + int x, int y, + gboolean force) { int dx, dy; int new_w, new_h; int gravity; MetaRectangle old; int new_x, new_y; + double remaining; window->display->grab_latest_motion_x = x; window->display->grab_latest_motion_y = y; @@ -6077,8 +6175,28 @@ update_resize (MetaWindow *window, break; } - if (!check_moveresize_frequency (window)) - return; + if (!check_moveresize_frequency (window, &remaining) && !force) + { + /* we are ignoring an event here, so we schedule a + * compensation event when we would otherwise not ignore + * an event. Otherwise we can become stuck if the user never + * generates another event. + */ + if (!window->display->grab_resize_timeout_id) + { + window->display->grab_resize_timeout_id = + g_timeout_add ((int)remaining, update_resize_timeout, window); + } + + return; + } + + /* Remove any scheduled compensation events */ + if (window->display->grab_resize_timeout_id) + { + g_source_remove (window->display->grab_resize_timeout_id); + window->display->grab_resize_timeout_id = 0; + } old = window->rect; @@ -6118,14 +6236,12 @@ update_resize (MetaWindow *window, meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity); } - /* If we don't actually resize the window, we clear the timestamp, - * so we'll quickly try again. Otherwise you get "stuck" because - * the window doesn't increment its _METACITY_UPDATE_COUNTER when - * nothing happens. - */ - if (window->rect.width == old.width && - window->rect.height == old.height) - clear_moveresize_time (window); + /* Store the latest resize time, if we actually resized. */ + if (window->rect.width != old.width && + window->rect.height != old.height) + { + g_get_current_time (&window->display->grab_last_moveresize_time); + } } typedef struct @@ -6216,6 +6332,14 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, "Alarm event received last motion x = %d y = %d\n", window->display->grab_latest_motion_x, window->display->grab_latest_motion_y); + + /* If sync was previously disabled, turn it back on and hope + * the application has come to its senses (maybe it was just + * busy with a pagefault or a long computation). + */ + window->disable_sync = FALSE; + window->sync_request_time.tv_sec = 0; + window->sync_request_time.tv_usec = 0; /* This means we are ready for another configure. */ switch (window->display->grab_op) @@ -6236,12 +6360,11 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, case META_GRAB_OP_KEYBOARD_RESIZING_NE: case META_GRAB_OP_KEYBOARD_RESIZING_SW: case META_GRAB_OP_KEYBOARD_RESIZING_NW: - clear_moveresize_time (window); /* force update to do something */ - /* no pointer round trip here, to keep in sync */ update_resize (window, window->display->grab_latest_motion_x, - window->display->grab_latest_motion_y); + window->display->grab_latest_motion_y, + TRUE); break; default: @@ -6255,16 +6378,17 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, case ButtonRelease: if (meta_grab_op_is_moving (window->display->grab_op)) { - clear_moveresize_time (window); if (event->xbutton.root == window->screen->xroot) update_move (window, event->xbutton.state, event->xbutton.x_root, event->xbutton.y_root); } else if (meta_grab_op_is_resizing (window->display->grab_op)) { - clear_moveresize_time (window); if (event->xbutton.root == window->screen->xroot) - update_resize (window, event->xbutton.x_root, event->xbutton.y_root); + update_resize (window, + event->xbutton.x_root, + event->xbutton.y_root, + TRUE); } meta_display_end_grab_op (window->display, event->xbutton.time); @@ -6291,7 +6415,8 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, event)) update_resize (window, event->xmotion.x_root, - event->xmotion.y_root); + event->xmotion.y_root, + FALSE); } } break; @@ -6311,7 +6436,8 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window, if (event->xcrossing.root == window->screen->xroot) update_resize (window, event->xcrossing.x_root, - event->xcrossing.y_root); + event->xcrossing.y_root, + FALSE); } break; default: diff --git a/src/window.h b/src/window.h index 878626ec3..534b2524c 100644 --- a/src/window.h +++ b/src/window.h @@ -243,10 +243,15 @@ struct _MetaWindow /* if TRUE we have a grab on the focus click buttons */ guint have_focus_click_grab : 1; - + + /* if TRUE, application is buggy and SYNC resizing is turned off */ + guint disable_sync : 1; + #ifdef HAVE_XSYNC /* XSync update counter */ - XSyncCounter update_counter; + XSyncCounter sync_request_counter; + guint sync_request_serial; + GTimeVal sync_request_time; #endif /* Number of UnmapNotify that are caused by us, if diff --git a/src/xprops.c b/src/xprops.c index cc9bd97c2..155424a40 100644 --- a/src/xprops.c +++ b/src/xprops.c @@ -544,7 +544,7 @@ counter_from_results (GetPropertyResults *results, XSyncCounter *counter_p) { if (!validate_or_free_results (results, 32, - results->display->atom_sync_counter, + XA_CARDINAL, TRUE)) return FALSE; @@ -986,7 +986,7 @@ meta_prop_get_values (MetaDisplay *display, values[i].required_type = XA_WM_SIZE_HINTS; break; case META_PROP_VALUE_SYNC_COUNTER: - values[i].required_type = display->atom_sync_counter; + values[i].required_type = XA_CARDINAL; break; } } diff --git a/src/xprops.h b/src/xprops.h index fdf54615e..cb93737b5 100644 --- a/src/xprops.h +++ b/src/xprops.h @@ -145,7 +145,7 @@ typedef enum META_PROP_VALUE_WM_HINTS, META_PROP_VALUE_CLASS_HINT, META_PROP_VALUE_SIZE_HINTS, - META_PROP_VALUE_SYNC_COUNTER + META_PROP_VALUE_SYNC_COUNTER /* comes back as CARDINAL */ } MetaPropValueType; /* used to request/return/store property values */