diff --git a/src/core/screen-private.h b/src/core/screen-private.h index e1a7f0778..56a0be428 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -46,6 +46,7 @@ struct _MetaMonitorInfo int number; MetaRectangle rect; gboolean is_primary; + gboolean in_fullscreen; XID output; /* The primary or first output for this crtc, None if no xrandr */ }; @@ -117,6 +118,7 @@ struct _MetaScreen guint32 wm_cm_timestamp; guint work_area_later; + guint check_fullscreen_later; int rows_of_workspaces; int columns_of_workspaces; @@ -201,6 +203,8 @@ void meta_screen_get_natural_monitor_list (MetaScreen *screen, void meta_screen_update_workspace_layout (MetaScreen *screen); void meta_screen_update_workspace_names (MetaScreen *screen); void meta_screen_queue_workarea_recalc (MetaScreen *screen); +void meta_screen_queue_check_fullscreen (MetaScreen *screen); + Window meta_create_offscreen_window (Display *xdisplay, Window parent, diff --git a/src/core/screen.c b/src/core/screen.c index 938606f7d..a1fd12ced 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -93,6 +93,7 @@ enum STARTUP_SEQUENCE_CHANGED, WORKAREAS_CHANGED, MONITORS_CHANGED, + IN_FULLSCREEN_CHANGED, LAST_SIGNAL }; @@ -247,6 +248,14 @@ meta_screen_class_init (MetaScreenClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 0); + screen_signals[IN_FULLSCREEN_CHANGED] = + g_signal_new ("in-fullscreen-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + g_object_class_install_property (object_class, PROP_N_WORKSPACES, pspec); @@ -480,11 +489,13 @@ reload_monitor_infos (MetaScreen *screen) screen->monitor_infos[0].number = 0; screen->monitor_infos[0].rect = screen->rect; screen->monitor_infos[0].rect.width = screen->rect.width / 2; + screen->monitor_infos[0].in_fullscreen = -1; screen->monitor_infos[1].number = 1; screen->monitor_infos[1].rect = screen->rect; screen->monitor_infos[1].rect.x = screen->rect.width / 2; screen->monitor_infos[1].rect.width = screen->rect.width / 2; + screen->monitor_infos[0].in_fullscreen = -1; } if (screen->n_monitor_infos == 0 && @@ -514,6 +525,7 @@ reload_monitor_infos (MetaScreen *screen) screen->monitor_infos[i].rect.y = infos[i].y_org; screen->monitor_infos[i].rect.width = infos[i].width; screen->monitor_infos[i].rect.height = infos[i].height; + screen->monitor_infos[i].in_fullscreen = -1; meta_topic (META_DEBUG_XINERAMA, "Monitor %d is %d,%d %d x %d\n", @@ -573,6 +585,7 @@ reload_monitor_infos (MetaScreen *screen) screen->monitor_infos[0].number = 0; screen->monitor_infos[0].rect = screen->rect; + screen->monitor_infos[0].in_fullscreen = -1; } filter_mirrored_monitors (screen); @@ -828,6 +841,7 @@ meta_screen_new (MetaDisplay *display, xroot, NoEventMask); screen->work_area_later = 0; + screen->check_fullscreen_later = 0; screen->active_workspace = NULL; screen->workspaces = NULL; @@ -991,6 +1005,8 @@ meta_screen_free (MetaScreen *screen, if (screen->work_area_later != 0) g_source_remove (screen->work_area_later); + if (screen->check_fullscreen_later != 0) + g_source_remove (screen->work_area_later); if (screen->monitor_infos) g_free (screen->monitor_infos); @@ -2997,6 +3013,8 @@ meta_screen_resize (MetaScreen *screen, g_free (old_monitor_infos); g_slist_free (windows); + meta_screen_queue_check_fullscreen (screen); + g_signal_emit (screen, screen_signals[MONITORS_CHANGED], 0); } @@ -3649,3 +3667,142 @@ meta_screen_set_active_workspace_hint (MetaScreen *screen) meta_error_trap_pop (screen->display); } +static gboolean +check_fullscreen_func (gpointer data) +{ + MetaScreen *screen = data; + GSList *windows; + GSList *tmp; + GSList *fullscreen_monitors = NULL; + gboolean in_fullscreen_changed = FALSE; + int i; + + screen->check_fullscreen_later = 0; + + windows = meta_display_list_windows (screen->display, + META_LIST_INCLUDE_OVERRIDE_REDIRECT); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) + { + MetaWindow *window = tmp->data; + gboolean covers_monitors = FALSE; + + if (window->screen != screen || window->hidden) + continue; + + if (window->fullscreen) + /* The checks for determining a fullscreen window's layer are quite + * elaborate, and we do a poor job at keeping it dynamically up-to-date. + * (It depends, for example, on whether the focus window is on the + * same monitor as the fullscreen window.) But because we minimize + * fullscreen windows not in LAYER_FULLSCREEN (see below), if the + * layer is stale here, it's really bad, so just force recomputation for + * here. This is expensive, but hopefully this function won't be + * called too often. + */ + meta_window_update_layer (window); + + if (window->override_redirect) + { + /* We want to handle the case where an application is creating an + * override-redirect window the size of the screen (monitor) and treat + * it similarly to a fullscreen window, though it doesn't have fullscreen + * window management behavior. (Being O-R, it's not managed at all.) + */ + if (meta_window_is_monitor_sized (window)) + covers_monitors = TRUE; + } + else + { + if (window->layer == META_LAYER_FULLSCREEN) + covers_monitors = TRUE; + } + + if (covers_monitors) + { + int *monitors; + gsize n_monitors; + gsize j; + + monitors = meta_window_get_all_monitors (window, &n_monitors); + for (j = 0; j < n_monitors; j++) + { + /* + 1 to avoid NULL */ + gpointer monitor_p = GINT_TO_POINTER(monitors[j] + 1); + if (!g_slist_find (fullscreen_monitors, monitor_p)) + fullscreen_monitors = g_slist_prepend (fullscreen_monitors, monitor_p); + } + + g_free (monitors); + } + + /* If we find a window that is fullscreen but not in the FULLSCREEN + * layer, it means that we've kicked it out of the layer because + * we've focused another window on the same monitor. In this case + * it would be confusing to keep the window fullscreen and visible, + * so minimize it. We can't do the same thing for override-redirect + * windows, so we just hope the application does the right thing. + */ + if (!covers_monitors && window->fullscreen) + { + meta_window_minimize (window); + meta_topic (META_DEBUG_WINDOW_OPS, + "Minimizing %s: was fullscreen but in a lower layer\n", + window->desc); + } + } + + g_slist_free (windows); + + for (i = 0; i < screen->n_monitor_infos; i++) + { + MetaMonitorInfo *info = &screen->monitor_infos[i]; + gboolean in_fullscreen = g_slist_find (fullscreen_monitors, GINT_TO_POINTER (i + 1)) != NULL; + if (in_fullscreen != info->in_fullscreen) + { + info->in_fullscreen = in_fullscreen; + in_fullscreen_changed = TRUE; + } + } + + g_slist_free (fullscreen_monitors); + + if (in_fullscreen_changed) + g_signal_emit (screen, screen_signals[IN_FULLSCREEN_CHANGED], 0, NULL); + + return FALSE; +} + +void +meta_screen_queue_check_fullscreen (MetaScreen *screen) +{ + if (!screen->check_fullscreen_later) + screen->check_fullscreen_later = meta_later_add (META_LATER_CHECK_FULLSCREEN, + check_fullscreen_func, + screen, NULL); +} + +/** + * meta_screen_get_monitor_in_fullscreen: + * @screen: a #MetaScreen + * @monitor: the monitor number + * + * Determines whether there is a fullscreen window obscuring the specified + * monitor. If there is a fullscreen window, the desktop environment will + * typically hide any controls that might obscure the fullscreen window. + * + * You can get notification when this changes by connecting to + * MetaScreen::in-fullscreen-changed. + * + * Returns: %TRUE if there is a fullscreen window covering the specified monitor. + */ +gboolean +meta_screen_get_monitor_in_fullscreen (MetaScreen *screen, + int monitor) +{ + g_return_val_if_fail (META_IS_SCREEN (screen), FALSE); + g_return_val_if_fail (monitor >= 0 && monitor < screen->n_monitor_infos, FALSE); + + /* We use -1 as a flag to mean "not known yet" for notification purposes */ + return screen->monitor_infos[monitor].in_fullscreen == TRUE; +} diff --git a/src/core/stack-tracker.c b/src/core/stack-tracker.c index 2c0c04655..85deca622 100644 --- a/src/core/stack-tracker.c +++ b/src/core/stack-tracker.c @@ -754,7 +754,7 @@ meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker) { if (tracker->sync_stack_later == 0) { - tracker->sync_stack_later = meta_later_add (META_LATER_BEFORE_REDRAW, + tracker->sync_stack_later = meta_later_add (META_LATER_SYNC_STACK, stack_tracker_sync_stack_later, tracker, NULL); } diff --git a/src/core/stack.c b/src/core/stack.c index c5fd30f63..e91094d51 100644 --- a/src/core/stack.c +++ b/src/core/stack.c @@ -395,6 +395,8 @@ get_maximum_layer_in_group (MetaWindow *window) static void compute_layer (MetaWindow *window) { + MetaStackLayer old_layer = window->layer; + window->layer = get_standalone_layer (window); /* We can only do promotion-due-to-group for dialogs and other @@ -430,6 +432,10 @@ compute_layer (MetaWindow *window) meta_topic (META_DEBUG_STACK, "Window %s on layer %u type = %u has_focus = %d\n", window->desc, window->layer, window->type, window->has_focus); + + if (window->layer != old_layer && + (old_layer == META_LAYER_FULLSCREEN || window->layer == META_LAYER_FULLSCREEN)) + meta_screen_queue_check_fullscreen (window->screen); } /* Front of the layer list is the topmost window, diff --git a/src/core/util.c b/src/core/util.c index 2e218760e..92a33e453 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -909,6 +909,9 @@ meta_later_add (MetaLaterType when, later->source = g_idle_add_full (META_PRIORITY_RESIZE, call_idle_later, later, NULL); ensure_later_repaint_func (); break; + case META_LATER_CALC_SHOWING: + case META_LATER_CHECK_FULLSCREEN: + case META_LATER_SYNC_STACK: case META_LATER_BEFORE_REDRAW: ensure_later_repaint_func (); break; diff --git a/src/core/window.c b/src/core/window.c index 58c7c2ebe..bfcb2f390 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1943,6 +1943,8 @@ meta_window_unmanage (MetaWindow *window, meta_prefs_remove_listener (prefs_changed_callback, window); + meta_screen_queue_check_fullscreen (window->screen); + g_signal_emit (window, window_signals[UNMANAGED], 0); g_object_unref (window); @@ -2527,7 +2529,7 @@ meta_window_queue (MetaWindow *window, guint queuebits) const MetaLaterType window_queue_later_when[NUMBER_OF_QUEUES] = { - META_LATER_BEFORE_REDRAW, /* CALC_SHOWING */ + META_LATER_CALC_SHOWING, /* CALC_SHOWING */ META_LATER_RESIZE, /* MOVE_RESIZE */ META_LATER_BEFORE_REDRAW /* UPDATE_ICON */ }; @@ -3225,6 +3227,9 @@ meta_window_show (MetaWindow *window) invalidate_work_areas (window); } + if (did_show) + meta_screen_queue_check_fullscreen (window->screen); + /* * Now that we have shown the window, we no longer want to consider the * initial timestamp in any subsequent deliberations whether to focus this @@ -3356,6 +3361,9 @@ meta_window_hide (MetaWindow *window) not_this_one, timestamp); } + + if (did_hide) + meta_screen_queue_check_fullscreen (window->screen); } static gboolean @@ -4134,6 +4142,9 @@ meta_window_make_fullscreen_internal (MetaWindow *window) recalc_window_features (window); set_net_wm_state (window); + /* For the auto-minimize feature, if we fail to get focus */ + meta_screen_queue_check_fullscreen (window->screen); + g_object_notify (G_OBJECT (window), "fullscreen"); } } @@ -5632,6 +5643,12 @@ meta_window_configure_notify (MetaWindow *window, window->rect.height = event->height; meta_window_update_monitor (window); + /* Whether an override-redirect window is considered fullscreen depends + * on its geometry. + */ + if (window->override_redirect) + meta_screen_queue_check_fullscreen (window->screen); + if (!event->override_redirect && !event->send_event) meta_warning ("Unhandled change of windows override redirect status\n"); diff --git a/src/meta/screen.h b/src/meta/screen.h index de36dba01..8c120fa03 100644 --- a/src/meta/screen.h +++ b/src/meta/screen.h @@ -82,6 +82,9 @@ void meta_screen_get_monitor_geometry (MetaScreen *screen, int monitor, MetaRectangle *geometry); +gboolean meta_screen_get_monitor_in_fullscreen (MetaScreen *screen, + int monitor); + int meta_screen_get_monitor_index_for_rect (MetaScreen *screen, MetaRectangle *rect); diff --git a/src/meta/util.h b/src/meta/util.h index 9e02484b8..be87190b0 100644 --- a/src/meta/util.h +++ b/src/meta/util.h @@ -167,12 +167,18 @@ GPid meta_show_dialog (const char *type, * MetaLaterType: * @META_LATER_RESIZE: call in a resize processing phase that is done * before GTK+ repainting (including window borders) is done. + * @META_LATER_CALC_SHOWING: used by Mutter to compute which windows should be mapped + * @META_LATER_CHECK_FULLSCREEN: used by Mutter to see if there's a fullscreen window + * @META_LATER_SYNC_STACK: used by Mutter to send it's idea of the stacking order to the server * @META_LATER_BEFORE_REDRAW: call before the stage is redrawn * @META_LATER_IDLE: call at a very low priority (can be blocked * by running animations or redrawing applications) **/ typedef enum { META_LATER_RESIZE, + META_LATER_CALC_SHOWING, + META_LATER_CHECK_FULLSCREEN, + META_LATER_SYNC_STACK, META_LATER_BEFORE_REDRAW, META_LATER_IDLE } MetaLaterType;