diff --git a/ChangeLog b/ChangeLog index 99ab90d8b..0db6a5244 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2002-01-03 Havoc Pennington + + * src/workspace.c (meta_workspace_activate): focus top window when + switching to a new workspace + + * src/util.c (meta_topic): start putting verbose output in + categories + + * src/window.c (meta_window_shade): focus frame after we queue + the calc_showing so the maps/unmaps have already happened. + + * src/display.c (meta_display_get_current_time): add the "get time + of current event" function and call it occasionally. + + * src/window.c (meta_window_free): if we have focus, call + meta_screen_focus_top_window(). + (meta_window_minimize): ditto + (meta_window_delete): ditto + + * src/screen.c (meta_screen_ensure_tab_popup): fix memory leak - + didn't free tab list + (meta_screen_focus_top_window): new function to use when we unmap + or unmanage a focused window + + * src/stack.c (meta_stack_get_default_focus_window): function used + in meta_screen_focus_top_window + 2001-12-21 Havoc Pennington * src/frame.c (meta_window_ensure_frame): add a server grab diff --git a/src/display.c b/src/display.c index d1218932e..ecebc2684 100644 --- a/src/display.c +++ b/src/display.c @@ -45,7 +45,8 @@ static gboolean event_callback (XEvent *event, gpointer data); static Window event_get_modified_window (MetaDisplay *display, XEvent *event); - +static guint32 event_get_time (MetaDisplay *display, + XEvent *event); @@ -296,6 +297,7 @@ meta_display_open (const char *name) display->is_double_click = FALSE; display->last_ignored_unmap_serial = 0; + display->current_time = CurrentTime; display->grab_op = META_GRAB_OP_NONE; display->grab_window = NULL; @@ -620,6 +622,13 @@ grab_op_is_keyboard (MetaGrabOp op) } } +/* Get time of current event, or CurrentTime if none. */ +guint32 +meta_display_get_current_time (MetaDisplay *display) +{ + return display->current_time; +} + static gboolean event_callback (XEvent *event, gpointer data) @@ -633,6 +642,8 @@ event_callback (XEvent *event, if (dump_events) meta_spew_event (display, event); + + display->current_time = event_get_time (display, event); /* mark double click events, kind of a hack, oh well. */ if (event->type == ButtonPress) @@ -727,6 +738,9 @@ event_callback (XEvent *event, ((event->xbutton.state & grab_mask) != 0)) meta_window_raise (window); + meta_topic (META_DEBUG_FOCUS, + "Focusing %s due to button 1 press (display.c)\n", + window->desc); meta_window_focus (window, event->xbutton.time); if (!frame_was_receiver && @@ -805,7 +819,12 @@ event_callback (XEvent *event, case META_FOCUS_MODE_MOUSE: if (window->type != META_WINDOW_DOCK && window->type != META_WINDOW_DESKTOP) - meta_window_focus (window, event->xcrossing.time); + { + meta_topic (META_DEBUG_FOCUS, + "Focusing %s due to enter notify\n", + window->desc); + meta_window_focus (window, event->xcrossing.time); + } break; case META_FOCUS_MODE_CLICK: break; @@ -828,10 +847,14 @@ event_callback (XEvent *event, * focused window. */ if (window->has_focus) - XSetInputFocus (display->xdisplay, - PointerRoot, - RevertToPointerRoot, - event->xcrossing.time); + { + meta_verbose ("Unsetting focus from %s due to LeaveNotify\n", + window->desc); + XSetInputFocus (display->xdisplay, + PointerRoot, + RevertToPointerRoot, + event->xcrossing.time); + } break; case META_FOCUS_MODE_SLOPPY: case META_FOCUS_MODE_CLICK: @@ -1032,6 +1055,7 @@ event_callback (XEvent *event, break; } + display->current_time = CurrentTime; return FALSE; } @@ -1109,6 +1133,63 @@ event_get_modified_window (MetaDisplay *display, } } +static guint32 +event_get_time (MetaDisplay *display, + XEvent *event) +{ + switch (event->type) + { + case KeyPress: + case KeyRelease: + return event->xkey.time; + + case ButtonPress: + case ButtonRelease: + return event->xbutton.time; + + case MotionNotify: + return event->xmotion.time; + + case PropertyNotify: + return event->xproperty.time; + + case SelectionClear: + case SelectionRequest: + case SelectionNotify: + return event->xselection.time; + + case EnterNotify: + case LeaveNotify: + return event->xcrossing.time; + + case FocusIn: + case FocusOut: + case KeymapNotify: + case Expose: + case GraphicsExpose: + case NoExpose: + case MapNotify: + case UnmapNotify: + case VisibilityNotify: + case ResizeRequest: + case ColormapNotify: + case ClientMessage: + case CreateNotify: + case DestroyNotify: + case MapRequest: + case ReparentNotify: + case ConfigureNotify: + case ConfigureRequest: + case GravityNotify: + case CirculateNotify: + case CirculateRequest: + case MappingNotify: + default: + return CurrentTime; + } +} + + static const char* focus_detail (int d) { diff --git a/src/display.h b/src/display.h index dc8164ac5..2f85be5a5 100644 --- a/src/display.h +++ b/src/display.h @@ -130,6 +130,8 @@ struct _MetaDisplay guint is_double_click : 1; unsigned long last_ignored_unmap_serial; + + guint32 current_time; /* current window operation */ MetaGrabOp grab_op; @@ -209,4 +211,6 @@ void meta_display_update_active_window_hint (MetaDisplay *display); void meta_display_show_desktop (MetaDisplay *display); void meta_display_unshow_desktop (MetaDisplay *display); +guint32 meta_display_get_current_time (MetaDisplay *display); + #endif diff --git a/src/frames.c b/src/frames.c index 91becc1b9..0222562c4 100644 --- a/src/frames.c +++ b/src/frames.c @@ -1032,6 +1032,9 @@ meta_frames_button_press_event (GtkWidget *widget, { meta_core_user_raise (gdk_display, frame->xwindow); + meta_topic (META_DEBUG_FOCUS, + "Focusing frame 0x%lx due to button 1 press\n", + frame->xwindow); meta_core_user_focus (gdk_display, frame->xwindow, event->time); diff --git a/src/keybindings.c b/src/keybindings.c index e8c550c18..992a338aa 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -355,7 +355,11 @@ meta_window_grab_all_keys (MetaWindow *window) /* Make sure the window is focused, otherwise the grab * won't do a lot of good. */ - meta_window_focus (window, CurrentTime); + meta_topic (META_DEBUG_FOCUS, + "Focusing %s because we're grabbing all its keys\n", + window->desc); + meta_window_focus (window, + meta_display_get_current_time (window->display)); grabwindow = window->frame ? window->frame->xwindow : window->xwindow; @@ -376,11 +380,11 @@ meta_window_grab_all_keys (MetaWindow *window) * presses */ meta_error_trap_push (window->display); - /* FIXME CurrentTime bogus */ + XGrabKeyboard (window->display->xdisplay, grabwindow, True, GrabModeAsync, GrabModeAsync, - CurrentTime); + meta_display_get_current_time (window->display)); result = meta_error_trap_pop (window->display); if (result != Success) @@ -413,8 +417,9 @@ meta_window_ungrab_all_keys (MetaWindow *window) XUngrabKey (window->display->xdisplay, AnyKey, AnyModifier, grabwindow); - /* FIXME CurrentTime bogus */ - XUngrabKeyboard (window->display->xdisplay, CurrentTime); + + XUngrabKeyboard (window->display->xdisplay, + meta_display_get_current_time (window->display)); meta_error_trap_pop (window->display); window->grab_on_frame = FALSE; @@ -714,6 +719,8 @@ process_tab_grab (MetaDisplay *display, meta_verbose ("Activating target window\n"); + meta_topic (META_DEBUG_FOCUS, "Activating %s due to tab popup selection\n", + target_window->desc); meta_window_activate (target_window, event->xkey.time); return TRUE; /* we already ended the grab */ @@ -1072,6 +1079,9 @@ handle_focus_previous (MetaDisplay *display, if (window) { meta_window_raise (window); + meta_topic (META_DEBUG_FOCUS, + "Focusing %s due to 'focus previous' keybinding\n", + window->desc); meta_window_focus (window, event->xkey.time); } } diff --git a/src/screen.c b/src/screen.c index 3584248fc..6731af869 100644 --- a/src/screen.c +++ b/src/screen.c @@ -640,5 +640,36 @@ meta_screen_ensure_tab_popup (MetaScreen *screen) screen->tab_popup = meta_ui_tab_popup_new (entries); g_free (entries); + g_slist_free (tab_list); + /* don't show tab popup, since proper window isn't selected yet */ } + +/* Focus top window on active workspace */ +void +meta_screen_focus_top_window (MetaScreen *screen, + MetaWindow *not_this_one) +{ + MetaWindow *window; + + if (not_this_one) + meta_topic (META_DEBUG_FOCUS, + "Focusing top window excluding %s\n", not_this_one->desc); + + window = meta_stack_get_default_focus_window (screen->stack, + screen->active_workspace, + not_this_one); + + /* FIXME I'm a loser on the CurrentTime front */ + if (window) + { + meta_topic (META_DEBUG_FOCUS, + "Focusing top window %s\n", window->desc); + + meta_window_focus (window, meta_display_get_current_time (screen->display)); + } + else + { + meta_topic (META_DEBUG_FOCUS, "No top window to focus found\n"); + } +} diff --git a/src/screen.h b/src/screen.h index 688fb4a7b..bd085d8bf 100644 --- a/src/screen.h +++ b/src/screen.h @@ -68,6 +68,9 @@ void meta_screen_set_cursor (MetaScreen *scree void meta_screen_ensure_tab_popup (MetaScreen *screen); +void meta_screen_focus_top_window (MetaScreen *screen, + MetaWindow *not_this_one); + #endif diff --git a/src/stack.c b/src/stack.c index f586577f6..8f0397192 100644 --- a/src/stack.c +++ b/src/stack.c @@ -869,8 +869,62 @@ meta_stack_get_below (MetaStack *stack, return link->next->data; else return find_prev_below_layer (stack, window->layer); +} + +MetaWindow* +meta_stack_get_default_focus_window (MetaStack *stack, + MetaWorkspace *workspace, + MetaWindow *not_this_one) +{ + /* FIXME if stack is frozen this is kind of broken. */ + + /* Find the topmost, focusable, mapped, window. */ + + MetaWindow *topmost_dock; + int layer = META_LAYER_LAST; + + topmost_dock = NULL; + + --layer; + while (layer >= 0) + { + GList *link; + + g_assert (layer >= 0 && layer < META_LAYER_LAST); + + /* top of this layer is at the front of the list */ + link = stack->layers[layer]; + + while (link) + { + MetaWindow *window = link->data; + + if (window && + window != not_this_one && + (workspace == NULL || + meta_window_visible_on_workspace (window, workspace))) + { + if (topmost_dock == NULL && + window->type == META_WINDOW_DOCK) + topmost_dock = window; + else + return window; + } + + link = link->next; + } + + --layer; + } + + /* If we didn't find a window to focus, we use the topmost dock. + * Note that we already tried the desktop - so we prefer focusing + * desktop to focusing the dock. + */ + return topmost_dock; } + #define IN_TAB_CHAIN(w) ((w)->type != META_WINDOW_DOCK && (w)->type != META_WINDOW_DESKTOP) #define GET_XWINDOW(stack, i) (g_array_index ((stack)->windows, \ Window, (i))) diff --git a/src/stack.h b/src/stack.h index c1de17846..505c63b77 100644 --- a/src/stack.h +++ b/src/stack.h @@ -95,6 +95,9 @@ MetaWindow* meta_stack_get_above (MetaStack *stack, MetaWindow *window); MetaWindow* meta_stack_get_below (MetaStack *stack, MetaWindow *window); +MetaWindow* meta_stack_get_default_focus_window (MetaStack *stack, + MetaWorkspace *workspace, + MetaWindow *not_this_one); MetaWindow* meta_stack_get_tab_next (MetaStack *stack, MetaWorkspace *workspace, diff --git a/src/util.c b/src/util.c index e0f43df1e..eddd8ad8b 100644 --- a/src/util.c +++ b/src/util.c @@ -167,6 +167,48 @@ meta_verbose (const char *format, ...) g_free (str); } +static const char* +topic_name (MetaDebugTopic topic) +{ + switch (topic) + { + case META_DEBUG_FOCUS: + return "FOCUS"; + break; + } + + return "Window manager"; +} + +void +meta_topic (MetaDebugTopic topic, + const char *format, + ...) +{ + va_list args; + gchar *str; + FILE *out; + + g_return_if_fail (format != NULL); + + if (!is_verbose) + return; + + va_start (args, format); + str = g_strdup_vprintf (format, args); + va_end (args); + + out = logfile ? logfile : stderr; + + if (no_prefix == 0) + fprintf (out, "%s: ", topic_name (topic)); + fputs (str, out); + + fflush (out); + + g_free (str); +} + void meta_bug (const char *format, ...) { diff --git a/src/util.h b/src/util.h index f56b5f195..cd71129f3 100644 --- a/src/util.h +++ b/src/util.h @@ -43,6 +43,16 @@ void meta_warning (const char *format, void meta_fatal (const char *format, ...) G_GNUC_PRINTF (1, 2); +typedef enum +{ + META_DEBUG_FOCUS + +} MetaDebugTopic; + +void meta_topic (MetaDebugTopic topic, + const char *format, + ...) G_GNUC_PRINTF (2, 3); + void meta_push_no_msg_prefix (void); void meta_pop_no_msg_prefix (void); diff --git a/src/window.c b/src/window.c index 3825dbf8d..4d04a3120 100644 --- a/src/window.c +++ b/src/window.c @@ -656,9 +656,30 @@ meta_window_free (MetaWindow *window) meta_verbose ("Unmanaging 0x%lx\n", window->xwindow); window->unmanaging = TRUE; - + + /* If we have the focus, focus some other window. + * This is done first, so that if the unmap causes + * an EnterNotify the EnterNotify will have final say + * on what gets focused, maintaining sloppy focus + * invariants. + */ + if (window->has_focus) + { + meta_topic (META_DEBUG_FOCUS, + "Focusing top window since we're unmanaging %s\n", + window->desc); + meta_screen_focus_top_window (window->screen, window); + } + else + { + meta_topic (META_DEBUG_FOCUS, + "Unmanaging window %s which doesn't currently have focus\n", + window->desc); + } + if (window->display->grab_window == window) - meta_display_end_grab_op (window->display, CurrentTime); + meta_display_end_grab_op (window->display, + meta_display_get_current_time (window->display)); if (window->display->focus_window == window) window->display->focus_window = NULL; @@ -1041,7 +1062,7 @@ meta_window_show (MetaWindow *window) if (window->shaded) { if (window->mapped) - { + { meta_verbose ("%s actually needs unmap\n", window->desc); window->mapped = FALSE; window->unmaps_pending += 1; @@ -1087,9 +1108,11 @@ meta_window_show (MetaWindow *window) if (parent && parent->has_focus) { - meta_verbose ("Focusing transient window '%s' since parent had focus\n", - window->desc); - meta_window_focus (window, CurrentTime); /* FIXME CurrentTime */ + meta_topic (META_DEBUG_FOCUS, + "Focusing transient window '%s' since parent had focus\n", + window->desc); + meta_window_focus (window, + meta_display_get_current_time (window->display)); } } } @@ -1131,6 +1154,19 @@ meta_window_minimize (MetaWindow *window) { window->minimized = TRUE; meta_window_queue_calc_showing (window); + if (window->has_focus) + { + meta_topic (META_DEBUG_FOCUS, + "Focusing top window due to minimization of focus window %s\n", + window->desc); + meta_screen_focus_top_window (window->screen, window); + } + else + { + meta_topic (META_DEBUG_FOCUS, + "Minimizing window %s which doesn't have the focus\n", + window->desc); + } } } @@ -1218,12 +1254,19 @@ meta_window_shade (MetaWindow *window) } window->shaded = TRUE; - - meta_window_focus (window, CurrentTime); meta_window_queue_move_resize (window); meta_window_queue_calc_showing (window); + /* After queuing the calc showing, since _focus flushes it, + * and we need to focus the frame + */ + meta_topic (META_DEBUG_FOCUS, + "Re-focusing window %s after shading it\n", + window->desc); + meta_window_focus (window, + meta_display_get_current_time (window->display)); + set_net_wm_state (window); } } @@ -1237,9 +1280,12 @@ meta_window_unshade (MetaWindow *window) window->shaded = FALSE; meta_window_queue_move_resize (window); meta_window_queue_calc_showing (window); + /* focus the window */ - /* FIXME CurrentTime is bogus */ - meta_window_focus (window, CurrentTime); + meta_topic (META_DEBUG_FOCUS, + "Focusing window %s after unshading it\n", + window->desc); + meta_window_focus (window, meta_display_get_current_time (window->display)); set_net_wm_state (window); } @@ -1259,6 +1305,9 @@ meta_window_activate (MetaWindow *window, meta_window_unminimize (window); meta_window_raise (window); + meta_topic (META_DEBUG_FOCUS, + "Focusing window %s due to activation\n", + window->desc); meta_window_focus (window, timestamp); } @@ -1962,6 +2011,25 @@ meta_window_delete (MetaWindow *window, window->desc); XKillClient (window->display->xdisplay, window->xwindow); } + + if (window->has_focus) + { + /* This is unfortunately going to result in weirdness + * if the window doesn't respond to the delete event. + * I don't know how to avoid that though. + */ + meta_topic (META_DEBUG_FOCUS, + "Focusing top window because focus window %s was deleted/killed\n", + window->desc); + meta_screen_focus_top_window (window->screen, window); + } + else + { + meta_topic (META_DEBUG_FOCUS, + "Window %s was deleted/killed but didn't have focus\n", + window->desc); + } + meta_error_trap_pop (window->display); } @@ -1969,9 +2037,10 @@ void meta_window_focus (MetaWindow *window, Time timestamp) { - meta_verbose ("Setting input focus to window %s, input: %d take_focus: %d\n", - window->desc, window->input, window->take_focus); - + meta_topic (META_DEBUG_FOCUS, + "Setting input focus to window %s, input: %d take_focus: %d\n", + window->desc, window->input, window->take_focus); + if (window->display->grab_window && window->display->grab_window->all_keys_grabbed) { @@ -2303,7 +2372,7 @@ meta_window_client_message (MetaWindow *window, * in this message, CurrentTime here is sort of * bogus. But it rarely matters most likely. */ - meta_window_delete (window, CurrentTime); + meta_window_delete (window, meta_display_get_current_time (window->display)); return TRUE; } @@ -2538,7 +2607,7 @@ meta_window_client_message (MetaWindow *window, op, FALSE, button, 0, - CurrentTime, + meta_display_get_current_time (window->display), x_root, y_root); } @@ -2549,9 +2618,10 @@ meta_window_client_message (MetaWindow *window, else if (event->xclient.message_type == display->atom_net_active_window) { - meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s'", window->desc); + meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating", + window->desc); - meta_window_activate (window, CurrentTime); + meta_window_activate (window, meta_display_get_current_time (window->display)); return TRUE; } @@ -2586,12 +2656,20 @@ meta_window_notify_focus (MetaWindow *window, * and prev_focus_window gets confused from what the * user expects once a keybinding is used. */ + meta_topic (META_DEBUG_FOCUS, + "Focus %s event received\n", + event->type == FocusIn ? "in" : + event->type == FocusOut ? "out" : + event->type == UnmapNotify ? "unmap" : + "???"); + if ((event->type == FocusIn || event->type == FocusOut) && (event->xfocus.mode == NotifyGrab || event->xfocus.mode == NotifyUngrab)) { - meta_verbose ("Ignoring focus event generated by a grab\n"); + meta_topic (META_DEBUG_FOCUS, + "Ignoring focus event generated by a grab\n"); return TRUE; } @@ -2602,11 +2680,13 @@ meta_window_notify_focus (MetaWindow *window, if (window == window->display->prev_focus_window && window->display->focus_window != NULL) { - meta_verbose ("%s is now the previous focus window due to another window focused in\n", - window->display->focus_window->desc); + meta_topic (META_DEBUG_FOCUS, + "%s is now the previous focus window due to another window focused in\n", + window->display->focus_window->desc); window->display->prev_focus_window = window->display->focus_window; } - meta_verbose ("New focus window %s\n", window->desc); + meta_topic (META_DEBUG_FOCUS, + "New focus window %s\n", window->desc); window->display->focus_window = window; } window->has_focus = TRUE; @@ -2618,10 +2698,15 @@ meta_window_notify_focus (MetaWindow *window, { if (window == window->display->focus_window) { - meta_verbose ("%s is now the previous focus window due to being focused out or unmapped\n", - window->desc); + meta_topic (META_DEBUG_FOCUS, + "%s is now the previous focus window due to being focused out or unmapped\n", + window->desc); + window->display->prev_focus_window = window; + meta_topic (META_DEBUG_FOCUS, + "Clearing focus window (was %s)\n", window->desc); + window->display->focus_window = NULL; } window->has_focus = FALSE; @@ -4894,7 +4979,7 @@ menu_callback (MetaWindowMenu *menu, switch (op) { case META_MENU_OP_DELETE: - meta_window_delete (window, CurrentTime); + meta_window_delete (window, meta_display_get_current_time (window->display)); break; case META_MENU_OP_MINIMIZE: @@ -4948,7 +5033,7 @@ menu_callback (MetaWindowMenu *menu, window, META_GRAB_OP_KEYBOARD_MOVING, FALSE, 0, 0, - CurrentTime, + meta_display_get_current_time (window->display), 0, 0); break; diff --git a/src/workspace.c b/src/workspace.c index 20eb11ec3..1b65915e7 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -189,6 +189,12 @@ meta_workspace_activate (MetaWorkspace *workspace) meta_workspace_queue_calc_showing (old); meta_workspace_queue_calc_showing (workspace); + + /* in mouse focus modes, this will probably get undone by an EnterNotify, + * but that's OK + */ + meta_topic (META_DEBUG_FOCUS, "Focusing top window on new workspace\n"); + meta_screen_focus_top_window (workspace->screen, NULL); } int