Compare commits
	
		
			31 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | c7b8f26cad | ||
|   | 8747b97ba3 | ||
|   | d20078574e | ||
|   | 26bd4fde5c | ||
|   | 2af49e503f | ||
|   | 6ea6af6eb4 | ||
|   | 10df80762c | ||
|   | f86032d700 | ||
|   | a8eb33f6fd | ||
|   | bd19de9429 | ||
|   | 2ca2838548 | ||
|   | df8234c5e3 | ||
|   | d03ffd801e | ||
|   | 7a4c808e43 | ||
|   | 4f1d62170b | ||
|   | e10804727d | ||
|   | e430e051b7 | ||
|   | 696d9d2fa9 | ||
|   | f6dd081acd | ||
|   | eddd6f8e9b | ||
|   | dfa4c7d670 | ||
|   | a487d4dd01 | ||
|   | c2ecdd0524 | ||
|   | 50b9042ac2 | ||
|   | f5e75de330 | ||
|   | 1ffe1eae4d | ||
|   | 970a446bd8 | ||
|   | 8880dffbdb | ||
|   | 5b6621811c | ||
|   | c2a9ccb7e2 | ||
|   | 4608cb6027 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -62,6 +62,7 @@ mutter-message | ||||
| mutter-window-demo | ||||
| focus-window | ||||
| test-attached | ||||
| test-focus | ||||
| test-gravity | ||||
| test-resizing | ||||
| test-size-hints | ||||
|   | ||||
							
								
								
									
										27
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								NEWS
									
									
									
									
									
								
							| @@ -1,3 +1,30 @@ | ||||
| 3.9.2 | ||||
| ===== | ||||
| * Add meta_window_can_close() function [Jasper; #699269] | ||||
| * Add support for string-array preferences [Florian; #700223] | ||||
| * Fix a potential race condition with _NET_WM_MOVERESIZE [Jasper; #699777] | ||||
| * Fix shade window action [Stef; #693714] | ||||
| * Remove overlay_group [Giovanni; #700735] | ||||
| * Improve tracking of the focus window [Dan, Jasper; #647706] | ||||
| * Add API to freeze/unfreeze the keyboard [Rui; #697001] | ||||
| * Grab and emit a signal when XK_ISO_Next_Group is pressed [Rui; #697002] | ||||
| * Misc bug fixes and cleanups [Dieter, Jasper, Rui; #699636, #700735, #697000] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Rui Matos, Florian Müllner, Jasper St. Pierre, | ||||
|   Dieter Verfaillie, Stef Walter, Dan Winship | ||||
|  | ||||
| Translations: | ||||
|   Kjartan Maraas [nb], Ján Kyselica [sk] | ||||
|  | ||||
| 3.9.1 | ||||
| ===== | ||||
| * Fix miscellaneous memory leaks [Pavel; #698710] | ||||
| * Misc fixes and cleanups [Stef, Simon; #698179, #697758] | ||||
|  | ||||
| Contributors: | ||||
|   Simon McVittie, Pavel Vasin, Stef Walter | ||||
|  | ||||
| 3.8.1 | ||||
| ===== | ||||
| * Fix crash when getting default font [Bastien; #696814] | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| AC_PREREQ(2.50) | ||||
|  | ||||
| m4_define([mutter_major_version], [3]) | ||||
| m4_define([mutter_minor_version], [8]) | ||||
| m4_define([mutter_micro_version], [1]) | ||||
| m4_define([mutter_minor_version], [9]) | ||||
| m4_define([mutter_micro_version], [2]) | ||||
|  | ||||
| m4_define([mutter_version], | ||||
|           [mutter_major_version.mutter_minor_version.mutter_micro_version]) | ||||
|   | ||||
							
								
								
									
										36
									
								
								po/nb.po
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								po/nb.po
									
									
									
									
									
								
							| @@ -4,10 +4,10 @@ | ||||
| # | ||||
| msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: mutter 3.8.x\n" | ||||
| "Project-Id-Version: mutter 3.9.x\n" | ||||
| "Report-Msgid-Bugs-To: \n" | ||||
| "POT-Creation-Date: 2013-04-03 14:10+0200\n" | ||||
| "PO-Revision-Date: 2013-04-03 14:11+0200\n" | ||||
| "POT-Creation-Date: 2013-05-28 09:47+0200\n" | ||||
| "PO-Revision-Date: 2013-05-28 09:48+0200\n" | ||||
| "Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n" | ||||
| "Language-Team: Norwegian bokmål <i18n-no@lister.ping.uio.no>\n" | ||||
| "Language: \n" | ||||
| @@ -205,14 +205,14 @@ msgstr "Visning delt til høyre" | ||||
|  | ||||
| #. This probably means that a non-WM compositor like xcompmgr is running; | ||||
| #. * we have no way to get it to exit | ||||
| #: ../src/compositor/compositor.c:568 | ||||
| #: ../src/compositor/compositor.c:571 | ||||
| #, c-format | ||||
| msgid "" | ||||
| "Another compositing manager is already running on screen %i on display \"%s" | ||||
| "\"." | ||||
| msgstr "En annen compositing manager kjører skjerm %i på display «%s»." | ||||
|  | ||||
| #: ../src/compositor/meta-background.c:1064 | ||||
| #: ../src/compositor/meta-background.c:1076 | ||||
| msgid "background texture could not be created from file" | ||||
| msgstr "bakgrunnstekstur kunne ikke lages fra fil" | ||||
|  | ||||
| @@ -250,17 +250,17 @@ msgstr "_Vent" | ||||
| msgid "_Force Quit" | ||||
| msgstr "_Tvungen nedstenging" | ||||
|  | ||||
| #: ../src/core/display.c:401 | ||||
| #: ../src/core/display.c:421 | ||||
| #, c-format | ||||
| msgid "Missing %s extension required for compositing" | ||||
| msgstr "Mangler utvidelsen %s som kreves for komposittfunksjon" | ||||
|  | ||||
| #: ../src/core/display.c:493 | ||||
| #: ../src/core/display.c:513 | ||||
| #, c-format | ||||
| msgid "Failed to open X Window System display '%s'\n" | ||||
| msgstr "Feil under åpning av X Window System skjerm «%s»\n" | ||||
|  | ||||
| #: ../src/core/keybindings.c:935 | ||||
| #: ../src/core/keybindings.c:1138 | ||||
| #, c-format | ||||
| msgid "" | ||||
| "Some other program is already using the key %s with modifiers %x as a " | ||||
| @@ -269,7 +269,7 @@ msgstr "" | ||||
| "Et annet program bruker allerede nøkkelen %s med modifikatorer %x som " | ||||
| "binding\n" | ||||
|  | ||||
| #: ../src/core/keybindings.c:1135 | ||||
| #: ../src/core/keybindings.c:1335 | ||||
| #, c-format | ||||
| msgid "\"%s\" is not a valid accelerator\n" | ||||
| msgstr "«%s» er ikke en gyldig aksellerator\n" | ||||
| @@ -333,7 +333,7 @@ msgstr "Skriv versjonsnummer" | ||||
| msgid "Mutter plugin to use" | ||||
| msgstr "Mutter-tillegg som skal brukes" | ||||
|  | ||||
| #: ../src/core/prefs.c:1095 | ||||
| #: ../src/core/prefs.c:1210 | ||||
| msgid "" | ||||
| "Workarounds for broken applications disabled. Some applications may not " | ||||
| "behave properly.\n" | ||||
| @@ -341,12 +341,12 @@ msgstr "" | ||||
| "Funksjonalitet for å gå rundt ødelagte programmer er deaktivert. Noen " | ||||
| "programmer vil kanskje ikke oppføre seg korrekt.\n" | ||||
|  | ||||
| #: ../src/core/prefs.c:1170 | ||||
| #: ../src/core/prefs.c:1285 | ||||
| #, c-format | ||||
| msgid "Could not parse font description \"%s\" from GSettings key %s\n" | ||||
| msgstr "Kunne ikke tolke skriftbeskrivelsen «%s» fra GSettings-nøkkel %s\n" | ||||
|  | ||||
| #: ../src/core/prefs.c:1236 | ||||
| #: ../src/core/prefs.c:1351 | ||||
| #, c-format | ||||
| msgid "" | ||||
| "\"%s\" found in configuration database is not a valid value for mouse button " | ||||
| @@ -355,7 +355,7 @@ msgstr "" | ||||
| "«%s» funnet i konfigurasjonsdatabasen er ikke en gyldig verdi for endring av " | ||||
| "musknapp\n" | ||||
|  | ||||
| #: ../src/core/prefs.c:1788 | ||||
| #: ../src/core/prefs.c:1928 | ||||
| #, c-format | ||||
| msgid "" | ||||
| "\"%s\" found in configuration database is not a valid value for keybinding " | ||||
| @@ -364,7 +364,7 @@ msgstr "" | ||||
| "«%s» funnet i konfigurasjonsdatabasen er ikke en gyldig verdi for " | ||||
| "tastaturbinding «%s»\n" | ||||
|  | ||||
| #: ../src/core/prefs.c:1887 | ||||
| #: ../src/core/prefs.c:2018 | ||||
| #, c-format | ||||
| msgid "Workspace %d" | ||||
| msgstr "Arbeidsområde %d" | ||||
| @@ -492,7 +492,7 @@ msgid "Window manager error: " | ||||
| msgstr "Feil i vindushåndterer: " | ||||
|  | ||||
| #. first time through | ||||
| #: ../src/core/window.c:7596 | ||||
| #: ../src/core/window.c:7505 | ||||
| #, c-format | ||||
| msgid "" | ||||
| "Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER " | ||||
| @@ -508,7 +508,7 @@ msgstr "" | ||||
| #. * MWM but not WM_NORMAL_HINTS are basically broken. We complain | ||||
| #. * about these apps but make them work. | ||||
| #. | ||||
| #: ../src/core/window.c:8320 | ||||
| #: ../src/core/window.c:8229 | ||||
| #, c-format | ||||
| msgid "" | ||||
| "Window %s sets an MWM hint indicating it isn't resizable, but sets min size " | ||||
| @@ -684,7 +684,9 @@ msgstr "Maksimer vinduer automatisk hvis de er nesten like store som skjermen" | ||||
| msgid "" | ||||
| "If enabled, new windows that are initially the size of the monitor " | ||||
| "automatically get maximized." | ||||
| msgstr "Nye vinduer som i utgangspunktet er samme størrelse som skjermen vil automatisk bli maksimert hvis denne slås på." | ||||
| msgstr "" | ||||
| "Nye vinduer som i utgangspunktet er samme størrelse som skjermen vil " | ||||
| "automatisk bli maksimert hvis denne slås på." | ||||
|  | ||||
| #: ../src/org.gnome.mutter.gschema.xml.in.h:19 | ||||
| msgid "Select window from tab popup" | ||||
|   | ||||
| @@ -53,17 +53,15 @@ | ||||
|  * | ||||
|  * # Containers # | ||||
|  * | ||||
|  * There's three containers in the stage that can be used to place actors, here | ||||
|  * There's two containers in the stage that are used to place window actors, here | ||||
|  * are listed in the order in which they are painted: | ||||
|  * | ||||
|  * - window group, accessible with meta_get_window_group_for_screen() | ||||
|  * - top window group, accessible with meta_get_top_window_group_for_screen() | ||||
|  * - overlay group, accessible with meta_get_overlay_group_for_screen() | ||||
|  * | ||||
|  * Mutter will place actors representing windows in the window group, except for | ||||
|  * override-redirect windows (ie. popups and menus) which will be placed in the | ||||
|  * top window group. Mutter won't put any actors in the overlay group, but it's | ||||
|  * intended for compositors to place there panel, dashes, status bars, etc. | ||||
|  * top window group. | ||||
|  */ | ||||
|  | ||||
| #include <config.h> | ||||
| @@ -255,23 +253,6 @@ meta_get_stage_for_screen (MetaScreen *screen) | ||||
|   return info->stage; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_get_overlay_group_for_screen: | ||||
|  * @screen: a #MetaScreen | ||||
|  * | ||||
|  * Returns: (transfer none): The overlay group corresponding to @screen | ||||
|  */ | ||||
| ClutterActor * | ||||
| meta_get_overlay_group_for_screen (MetaScreen *screen) | ||||
| { | ||||
|   MetaCompScreen *info = meta_screen_get_compositor_data (screen); | ||||
|  | ||||
|   if (!info) | ||||
|     return NULL; | ||||
|  | ||||
|   return info->overlay_group; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_get_window_group_for_screen: | ||||
|  * @screen: a #MetaScreen | ||||
| @@ -388,6 +369,46 @@ meta_empty_stage_input_region (MetaScreen *screen) | ||||
|   meta_set_stage_input_region (screen, region); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_focus_stage_window (MetaScreen *screen, | ||||
|                          guint32     timestamp) | ||||
| { | ||||
|   ClutterStage *stage; | ||||
|   Window window; | ||||
|  | ||||
|   stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen)); | ||||
|   if (!stage) | ||||
|     return; | ||||
|  | ||||
|   window = clutter_x11_get_stage_window (stage); | ||||
|  | ||||
|   if (window == None) | ||||
|     return; | ||||
|  | ||||
|   meta_display_set_input_focus_xwindow (screen->display, | ||||
|                                         screen, | ||||
|                                         window, | ||||
|                                         timestamp); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_stage_is_focused (MetaScreen *screen) | ||||
| { | ||||
|   ClutterStage *stage; | ||||
|   Window window; | ||||
|  | ||||
|   stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen)); | ||||
|   if (!stage) | ||||
|     return FALSE; | ||||
|  | ||||
|   window = clutter_x11_get_stage_window (stage); | ||||
|  | ||||
|   if (window == None) | ||||
|     return FALSE; | ||||
|  | ||||
|   return (screen->display->focus_xwindow == window); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_begin_modal_for_plugin (MetaScreen       *screen, | ||||
|                              MetaPlugin       *plugin, | ||||
| @@ -649,11 +670,9 @@ meta_compositor_manage_screen (MetaCompositor *compositor, | ||||
|  | ||||
|   info->window_group = meta_window_group_new (screen); | ||||
|   info->top_window_group = meta_window_group_new (screen); | ||||
|   info->overlay_group = clutter_actor_new (); | ||||
|  | ||||
|   clutter_actor_add_child (info->stage, info->window_group); | ||||
|   clutter_actor_add_child (info->stage, info->top_window_group); | ||||
|   clutter_actor_add_child (info->stage, info->overlay_group); | ||||
|  | ||||
|   info->plugin_mgr = meta_plugin_manager_new (screen); | ||||
|  | ||||
| @@ -684,8 +703,6 @@ meta_compositor_manage_screen (MetaCompositor *compositor, | ||||
|       info->pending_input_region = None; | ||||
|     } | ||||
|  | ||||
|   clutter_actor_show (info->overlay_group); | ||||
|  | ||||
|   /* Map overlay window before redirecting windows offscreen so we catch their | ||||
|    * contents until we show the stage. | ||||
|    */ | ||||
|   | ||||
| @@ -472,6 +472,17 @@ meta_background_dispose (GObject *object) | ||||
|   G_OBJECT_CLASS (meta_background_parent_class)->dispose (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_background_finalize (GObject *object) | ||||
| { | ||||
|   MetaBackground        *self = META_BACKGROUND (object); | ||||
|   MetaBackgroundPrivate *priv = self->priv; | ||||
|  | ||||
|   g_free (priv->filename); | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_background_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| ensure_pipeline (MetaBackground *self) | ||||
| { | ||||
| @@ -643,6 +654,7 @@ meta_background_class_init (MetaBackgroundClass *klass) | ||||
|   g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate)); | ||||
|  | ||||
|   object_class->dispose = meta_background_dispose; | ||||
|   object_class->finalize = meta_background_finalize; | ||||
|   object_class->set_property = meta_background_set_property; | ||||
|   object_class->get_property = meta_background_get_property; | ||||
|  | ||||
|   | ||||
| @@ -1645,7 +1645,7 @@ meta_window_actor_get_obscured_region (MetaWindowActor *self) | ||||
| { | ||||
|   MetaWindowActorPrivate *priv = self->priv; | ||||
|  | ||||
|   if (priv->back_pixmap && priv->opacity == 0xff) | ||||
|   if (priv->back_pixmap && priv->opacity == 0xff && !priv->window->shaded) | ||||
|     return priv->opaque_region; | ||||
|   else | ||||
|     return NULL; | ||||
| @@ -1993,73 +1993,6 @@ meta_window_actor_sync_visibility (MetaWindowActor *self) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #define TAU (2*M_PI) | ||||
|  | ||||
| static void | ||||
| install_corners (MetaWindow       *window, | ||||
|                  MetaFrameBorders *borders, | ||||
|                  cairo_t          *cr) | ||||
| { | ||||
|   float top_left, top_right, bottom_left, bottom_right; | ||||
|   int x, y; | ||||
|   MetaRectangle outer; | ||||
|  | ||||
|   meta_frame_get_corner_radiuses (window->frame, | ||||
|                                   &top_left, | ||||
|                                   &top_right, | ||||
|                                   &bottom_left, | ||||
|                                   &bottom_right); | ||||
|  | ||||
|   meta_window_get_outer_rect (window, &outer); | ||||
|  | ||||
|   /* top left */ | ||||
|   x = borders->invisible.left; | ||||
|   y = borders->invisible.top; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x + top_left, | ||||
|              y + top_left, | ||||
|              top_left, | ||||
|              2 * TAU / 4, | ||||
|              3 * TAU / 4); | ||||
|  | ||||
|   /* top right */ | ||||
|   x = borders->invisible.left + outer.width - top_right; | ||||
|   y = borders->invisible.top; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x, | ||||
|              y + top_right, | ||||
|              top_right, | ||||
|              3 * TAU / 4, | ||||
|              4 * TAU / 4); | ||||
|  | ||||
|   /* bottom right */ | ||||
|   x = borders->invisible.left + outer.width - bottom_right; | ||||
|   y = borders->invisible.top + outer.height - bottom_right; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x, | ||||
|              y, | ||||
|              bottom_right, | ||||
|              0 * TAU / 4, | ||||
|              1 * TAU / 4); | ||||
|  | ||||
|   /* bottom left */ | ||||
|   x = borders->invisible.left; | ||||
|   y = borders->invisible.top + outer.height - bottom_left; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x + bottom_left, | ||||
|              y, | ||||
|              bottom_left, | ||||
|              1 * TAU / 4, | ||||
|              2 * TAU / 4); | ||||
|  | ||||
|   cairo_set_source_rgba (cr, 1, 1, 1, 1); | ||||
|   cairo_fill (cr); | ||||
| } | ||||
|  | ||||
| static cairo_region_t * | ||||
| scan_visible_region (guchar         *mask_data, | ||||
|                      int             stride, | ||||
| @@ -2099,7 +2032,6 @@ scan_visible_region (guchar         *mask_data, | ||||
|  | ||||
| static void | ||||
| build_and_scan_frame_mask (MetaWindowActor       *self, | ||||
|                            MetaFrameBorders      *borders, | ||||
|                            cairo_rectangle_int_t *client_area, | ||||
|                            cairo_region_t        *shape_region) | ||||
| { | ||||
| @@ -2145,7 +2077,7 @@ build_and_scan_frame_mask (MetaWindowActor       *self, | ||||
|       gdk_cairo_region (cr, frame_paint_region); | ||||
|       cairo_clip (cr); | ||||
|  | ||||
|       install_corners (priv->window, borders, cr); | ||||
|       meta_frame_get_mask (priv->window->frame, cr); | ||||
|  | ||||
|       cairo_surface_flush (surface); | ||||
|       scanned_region = scan_visible_region (mask_data, stride, frame_paint_region); | ||||
| @@ -2214,7 +2146,10 @@ check_needs_reshape (MetaWindowActor *self) | ||||
|   client_area.x = borders.total.left; | ||||
|   client_area.y = borders.total.top; | ||||
|   client_area.width = priv->window->rect.width; | ||||
|   client_area.height = priv->window->rect.height; | ||||
|   if (priv->window->shaded) | ||||
|     client_area.height = 0; | ||||
|   else | ||||
|     client_area.height = priv->window->rect.height; | ||||
|  | ||||
|   meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL); | ||||
|   g_clear_pointer (&priv->shape_region, cairo_region_destroy); | ||||
| @@ -2308,7 +2243,7 @@ check_needs_reshape (MetaWindowActor *self) | ||||
|        * and scans the mask looking for all opaque pixels, | ||||
|        * adding it to region. | ||||
|        */ | ||||
|       build_and_scan_frame_mask (self, &borders, &client_area, region); | ||||
|       build_and_scan_frame_mask (self, &client_area, region); | ||||
|     } | ||||
|  | ||||
|   priv->shape_region = region; | ||||
|   | ||||
| @@ -59,6 +59,8 @@ struct _MetaBarrierPrivate | ||||
|   PointerBarrier xbarrier; | ||||
| }; | ||||
|  | ||||
| static void meta_barrier_event_unref (MetaBarrierEvent *event); | ||||
|  | ||||
| static void | ||||
| meta_barrier_get_property (GObject    *object, | ||||
|                            guint       prop_id, | ||||
| @@ -359,6 +361,8 @@ meta_barrier_fire_event (MetaBarrier    *barrier, | ||||
|     default: | ||||
|       g_assert_not_reached (); | ||||
|     } | ||||
|  | ||||
|   meta_barrier_event_unref (event); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
|   | ||||
| @@ -103,19 +103,20 @@ struct _MetaDisplay | ||||
| #include <meta/atomnames.h> | ||||
| #undef item | ||||
|  | ||||
|   /* This is the actual window from focus events, | ||||
|    * not the one we last set | ||||
|   /* The window and serial of the most recent FocusIn event. */ | ||||
|   Window server_focus_window; | ||||
|   gulong server_focus_serial; | ||||
|  | ||||
|   /* Our best guess as to the "currently" focused window (that is, the | ||||
|    * window that we expect will be focused at the point when the X | ||||
|    * server processes our next request), and the serial of the request | ||||
|    * or event that caused this. | ||||
|    */ | ||||
|   MetaWindow *focus_window; | ||||
|  | ||||
|   /* window we are expecting a FocusIn event for or the current focus | ||||
|    * window if we are not expecting any FocusIn/FocusOut events; not | ||||
|    * perfect because applications can call XSetInputFocus directly. | ||||
|    * (It could also be messed up if a timestamp later than current | ||||
|    * time is sent to meta_display_set_input_focus_window, though that | ||||
|    * would be a programming error).  See bug 154598 for more info. | ||||
|    */ | ||||
|   MetaWindow *expected_focus_window; | ||||
|   /* For windows we've focused that don't necessarily have an X window, | ||||
|    * like the no_focus_window or the stage X window. */ | ||||
|   Window focus_xwindow; | ||||
|   gulong focus_serial; | ||||
|  | ||||
|   /* last timestamp passed to XSetInputFocus */ | ||||
|   guint32 last_focus_time; | ||||
| @@ -239,6 +240,8 @@ struct _MetaDisplay | ||||
|   unsigned int meta_mask; | ||||
|   MetaKeyCombo overlay_key_combo; | ||||
|   gboolean overlay_key_only_pressed; | ||||
|   MetaKeyCombo *iso_next_group_combos; | ||||
|   int n_iso_next_group_combos; | ||||
|    | ||||
|   /* Monitor cache */ | ||||
|   unsigned int monitor_cache_invalidated : 1; | ||||
| @@ -458,6 +461,7 @@ void meta_display_overlay_key_activate (MetaDisplay *display); | ||||
| void meta_display_accelerator_activate (MetaDisplay *display, | ||||
|                                         guint        action, | ||||
|                                         guint        deviceid); | ||||
| gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display); | ||||
|  | ||||
| /* In above-tab-keycode.c */ | ||||
| guint meta_display_get_above_tab_keycode (MetaDisplay *display); | ||||
| @@ -467,4 +471,9 @@ gboolean meta_display_process_barrier_event (MetaDisplay    *display, | ||||
|                                              XIBarrierEvent *event); | ||||
| #endif /* HAVE_XI23 */ | ||||
|  | ||||
| void meta_display_set_input_focus_xwindow (MetaDisplay *display, | ||||
|                                            MetaScreen  *screen, | ||||
|                                            Window       window, | ||||
|                                            guint32      timestamp); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -139,6 +139,7 @@ enum | ||||
| { | ||||
|   OVERLAY_KEY, | ||||
|   ACCELERATOR_ACTIVATED, | ||||
|   MODIFIERS_ACCELERATOR_ACTIVATED, | ||||
|   FOCUS_WINDOW, | ||||
|   WINDOW_CREATED, | ||||
|   WINDOW_DEMANDS_ATTENTION, | ||||
| @@ -255,6 +256,25 @@ meta_display_class_init (MetaDisplayClass *klass) | ||||
|                   NULL, NULL, NULL, | ||||
|                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); | ||||
|  | ||||
|   /** | ||||
|    * MetaDisplay::modifiers-accelerator-activated: | ||||
|    * @display: the #MetaDisplay instance | ||||
|    * | ||||
|    * The ::modifiers-accelerator-activated signal will be emitted when | ||||
|    * a special modifiers-only keybinding is activated. | ||||
|    * | ||||
|    * Returns: %TRUE means that the keyboard device should remain | ||||
|    *    frozen and %FALSE for the default behavior of unfreezing the | ||||
|    *    keyboard. | ||||
|    */ | ||||
|   display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] = | ||||
|     g_signal_new ("modifiers-accelerator-activated", | ||||
|                   G_TYPE_FROM_CLASS (klass), | ||||
|                   G_SIGNAL_RUN_LAST, | ||||
|                   0, | ||||
|                   g_signal_accumulator_first_wins, NULL, NULL, | ||||
|                   G_TYPE_BOOLEAN, 0); | ||||
|  | ||||
|   display_signals[WINDOW_CREATED] = | ||||
|     g_signal_new ("window-created", | ||||
|                   G_TYPE_FROM_CLASS (klass), | ||||
| @@ -519,7 +539,9 @@ meta_display_open (void) | ||||
|   the_display->autoraise_timeout_id = 0; | ||||
|   the_display->autoraise_window = NULL; | ||||
|   the_display->focus_window = NULL; | ||||
|   the_display->expected_focus_window = NULL; | ||||
|   the_display->focus_serial = 0; | ||||
|   the_display->server_focus_window = None; | ||||
|   the_display->server_focus_serial = 0; | ||||
|   the_display->grab_old_window_stacking = NULL; | ||||
|  | ||||
|   the_display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */ | ||||
| @@ -1632,12 +1654,12 @@ meta_display_mouse_mode_focus (MetaDisplay *display, | ||||
|        * alternative mechanism works great. | ||||
|        */ | ||||
|       if (meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE && | ||||
|           display->expected_focus_window != NULL) | ||||
|           display->focus_window != NULL) | ||||
|         { | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "Unsetting focus from %s due to mouse entering " | ||||
|                       "the DESKTOP window\n", | ||||
|                       display->expected_focus_window->desc); | ||||
|                       display->focus_window->desc); | ||||
|           meta_display_focus_the_no_focus_window (display, | ||||
|                                                   window->screen, | ||||
|                                                   timestamp); | ||||
| @@ -1851,6 +1873,251 @@ get_input_event (MetaDisplay *display, | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| static void | ||||
| update_focus_window (MetaDisplay *display, | ||||
|                      MetaWindow  *window, | ||||
|                      Window       xwindow, | ||||
|                      gulong       serial) | ||||
| { | ||||
|   display->focus_serial = serial; | ||||
|  | ||||
|   if (display->focus_xwindow == xwindow) | ||||
|     return; | ||||
|  | ||||
|   if (display->focus_window) | ||||
|     { | ||||
|       MetaWindow *previous; | ||||
|  | ||||
|       meta_topic (META_DEBUG_FOCUS, | ||||
|                   "%s is now the previous focus window due to being focused out or unmapped\n", | ||||
|                   display->focus_window->desc); | ||||
|  | ||||
|       /* Make sure that signals handlers invoked by | ||||
|        * meta_window_set_focused_internal() don't see | ||||
|        * display->focus_window->has_focus == FALSE | ||||
|        */ | ||||
|       previous = display->focus_window; | ||||
|       display->focus_window = NULL; | ||||
|       display->focus_xwindow = None; | ||||
|  | ||||
|       meta_window_set_focused_internal (previous, FALSE); | ||||
|     } | ||||
|  | ||||
|   display->focus_window = window; | ||||
|   display->focus_xwindow = xwindow; | ||||
|  | ||||
|   if (display->focus_window) | ||||
|     { | ||||
|       meta_topic (META_DEBUG_FOCUS, "* Focus --> %s with serial %lu\n", | ||||
|                   display->focus_window->desc, serial); | ||||
|       meta_window_set_focused_internal (display->focus_window, TRUE); | ||||
|     } | ||||
|   else | ||||
|     meta_topic (META_DEBUG_FOCUS, "* Focus --> NULL with serial %lu\n", serial); | ||||
|  | ||||
|   g_object_notify (G_OBJECT (display), "focus-window"); | ||||
|   meta_display_update_active_window_hint (display); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| timestamp_too_old (MetaDisplay *display, | ||||
|                    guint32     *timestamp) | ||||
| { | ||||
|   /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow | ||||
|    * us to sanity check the timestamp here and ensure it doesn't correspond to | ||||
|    * a future time (though we would want to rename to | ||||
|    * timestamp_too_old_or_in_future). | ||||
|    */ | ||||
|  | ||||
|   if (*timestamp == CurrentTime) | ||||
|     { | ||||
|       *timestamp = meta_display_get_current_time_roundtrip (display); | ||||
|       return FALSE; | ||||
|     } | ||||
|   else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time)) | ||||
|     { | ||||
|       if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time)) | ||||
|         return TRUE; | ||||
|       else | ||||
|         { | ||||
|           *timestamp = display->last_focus_time; | ||||
|           return FALSE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| request_xserver_input_focus_change (MetaDisplay *display, | ||||
|                                     MetaScreen  *screen, | ||||
|                                     Window       xwindow, | ||||
|                                     guint32      timestamp) | ||||
| { | ||||
|   MetaWindow *meta_window; | ||||
|   gulong serial; | ||||
|  | ||||
|   if (timestamp_too_old (display, ×tamp)) | ||||
|     return; | ||||
|  | ||||
|   meta_window = meta_display_lookup_x_window (display, xwindow); | ||||
|  | ||||
|   meta_error_trap_push (display); | ||||
|  | ||||
|   /* In order for mutter to know that the focus request succeeded, we track | ||||
|    * the serial of the "focus request" we made, but if we take the serial | ||||
|    * of the XSetInputFocus request, then there's no way to determine the | ||||
|    * difference between focus events as a result of the SetInputFocus and | ||||
|    * focus events that other clients send around the same time. Ensure that | ||||
|    * we know which is which by making two requests that the server will | ||||
|    * process at the same time. | ||||
|    */ | ||||
|   meta_display_grab (display); | ||||
|  | ||||
|   XSetInputFocus (display->xdisplay, | ||||
|                   xwindow, | ||||
|                   RevertToPointerRoot, | ||||
|                   timestamp); | ||||
|  | ||||
|   serial = XNextRequest (display->xdisplay); | ||||
|  | ||||
|   { | ||||
|     unsigned long data[1] = { 0 }; | ||||
|  | ||||
|     XChangeProperty (display->xdisplay, display->timestamp_pinging_window, | ||||
|                      display->atom__MUTTER_FOCUS_SET, | ||||
|                      XA_CARDINAL, | ||||
|                      32, PropModeReplace, (guchar*) data, 1); | ||||
|   } | ||||
|  | ||||
|   meta_display_ungrab (display); | ||||
|  | ||||
|   update_focus_window (display, | ||||
|                        meta_window, | ||||
|                        xwindow, | ||||
|                        serial); | ||||
|  | ||||
|   meta_error_trap_pop (display); | ||||
|  | ||||
|   display->last_focus_time = timestamp; | ||||
|   display->active_screen = screen; | ||||
|  | ||||
|   if (meta_window == NULL || meta_window != display->autoraise_window) | ||||
|     meta_display_remove_autoraise_callback (display); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_window_focus_event (MetaDisplay  *display, | ||||
|                            MetaWindow   *window, | ||||
|                            XIEnterEvent *event, | ||||
|                            unsigned long serial) | ||||
| { | ||||
|   MetaWindow *focus_window; | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
|   const char *window_type; | ||||
|  | ||||
|   /* Note the event can be on either the window or the frame, | ||||
|    * we focus the frame for shaded windows | ||||
|    */ | ||||
|   if (window) | ||||
|     { | ||||
|       if (event->event == window->xwindow) | ||||
|         window_type = "client window"; | ||||
|       else if (window->frame && event->event == window->frame->xwindow) | ||||
|         window_type = "frame window"; | ||||
|       else | ||||
|         window_type = "unknown client window"; | ||||
|     } | ||||
|   else if (meta_display_xwindow_is_a_no_focus_window (display, event->event)) | ||||
|     window_type = "no_focus_window"; | ||||
|   else if (meta_display_screen_for_root (display, event->event)) | ||||
|     window_type = "root window"; | ||||
|   else | ||||
|     window_type = "unknown window"; | ||||
|  | ||||
|   meta_topic (META_DEBUG_FOCUS, | ||||
|               "Focus %s event received on %s 0x%lx (%s) " | ||||
|               "mode %s detail %s serial %lu\n", | ||||
|               event->evtype == XI_FocusIn ? "in" : | ||||
|               event->evtype == XI_FocusOut ? "out" : | ||||
|               "???", | ||||
|               window ? window->desc : "", | ||||
|               event->event, window_type, | ||||
|               meta_event_mode_to_string (event->mode), | ||||
|               meta_event_detail_to_string (event->mode), | ||||
|               event->serial); | ||||
| #endif | ||||
|  | ||||
|   /* FIXME our pointer tracking is broken; see how | ||||
|    * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c | ||||
|    * for how to handle it the correct way.  In brief you need to track | ||||
|    * pointer focus and regular focus, and handle EnterNotify in | ||||
|    * PointerRoot mode with no window manager.  However as noted above, | ||||
|    * accurate focus tracking will break things because we want to keep | ||||
|    * windows "focused" when using keybindings on them, and also we | ||||
|    * sometimes "focus" a window by focusing its frame or | ||||
|    * no_focus_window; so this all needs rethinking massively. | ||||
|    * | ||||
|    * My suggestion is to change it so that we clearly separate | ||||
|    * actual keyboard focus tracking using the xterm algorithm, | ||||
|    * and mutter's "pretend" focus window, and go through all | ||||
|    * the code and decide which one should be used in each place; | ||||
|    * a hard bit is deciding on a policy for that. | ||||
|    * | ||||
|    * http://bugzilla.gnome.org/show_bug.cgi?id=90382 | ||||
|    */ | ||||
|  | ||||
|   /* We ignore grabs, though this is questionable. It may be better to | ||||
|    * increase the intelligence of the focus window tracking. | ||||
|    * | ||||
|    * The problem is that keybindings for windows are done with | ||||
|    * XGrabKey, which means focus_window disappears and the front of | ||||
|    * the MRU list gets confused from what the user expects once a | ||||
|    * keybinding is used. | ||||
|    */ | ||||
|  | ||||
|   if (event->mode == XINotifyGrab || | ||||
|       event->mode == XINotifyUngrab || | ||||
|       /* From WindowMaker, ignore all funky pointer root events */ | ||||
|       event->detail > XINotifyNonlinearVirtual) | ||||
|     { | ||||
|       meta_topic (META_DEBUG_FOCUS, | ||||
|                   "Ignoring focus event generated by a grab or other weirdness\n"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   if (event->evtype == XI_FocusIn) | ||||
|     { | ||||
|       display->server_focus_window = event->event; | ||||
|       display->server_focus_serial = serial; | ||||
|       focus_window = window; | ||||
|     } | ||||
|   else if (event->evtype == XI_FocusOut) | ||||
|     { | ||||
|       if (event->detail == XINotifyInferior) | ||||
|         { | ||||
|           /* This event means the client moved focus to a subwindow */ | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "Ignoring focus out with NotifyInferior\n"); | ||||
|           return; | ||||
|         } | ||||
|  | ||||
|       display->server_focus_window = None; | ||||
|       display->server_focus_serial = serial; | ||||
|       focus_window = NULL; | ||||
|     } | ||||
|   else | ||||
|     g_return_if_reached (); | ||||
|  | ||||
|   if (display->server_focus_serial >= display->focus_serial) | ||||
|     { | ||||
|       update_focus_window (display, | ||||
|                            focus_window, | ||||
|                            focus_window ? focus_window->xwindow : None, | ||||
|                            display->server_focus_serial); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * event_callback: | ||||
|  * @event: The event that just happened | ||||
| @@ -1893,7 +2160,19 @@ event_callback (XEvent   *event, | ||||
|   filter_out_event = FALSE; | ||||
|   display->current_time = event_get_time (display, event); | ||||
|   display->monitor_cache_invalidated = TRUE; | ||||
|    | ||||
|  | ||||
|   if (event->xany.serial > display->focus_serial && | ||||
|       display->focus_window && | ||||
|       display->focus_window->xwindow != display->server_focus_window) | ||||
|     { | ||||
|       meta_topic (META_DEBUG_FOCUS, "Earlier attempt to focus %s failed\n", | ||||
|                   display->focus_window->desc); | ||||
|       update_focus_window (display, | ||||
|                            meta_display_lookup_x_window (display, display->server_focus_window), | ||||
|                            display->server_focus_window, | ||||
|                            display->server_focus_serial); | ||||
|     } | ||||
|  | ||||
|   modified = event_get_modified_window (display, event); | ||||
|  | ||||
|   input_event = get_input_event (display, event); | ||||
| @@ -2350,40 +2629,19 @@ event_callback (XEvent   *event, | ||||
|           break; | ||||
|         case XI_FocusIn: | ||||
|         case XI_FocusOut: | ||||
|           if (window) | ||||
|             { | ||||
|               meta_window_notify_focus (window, enter_event); | ||||
|             } | ||||
|           else if (meta_display_xwindow_is_a_no_focus_window (display, | ||||
|                                                               enter_event->event)) | ||||
|             { | ||||
|               meta_topic (META_DEBUG_FOCUS, | ||||
|                           "Focus %s event received on no_focus_window 0x%lx " | ||||
|                           "mode %s detail %s\n", | ||||
|                           enter_event->evtype == XI_FocusIn ? "in" : | ||||
|                           enter_event->evtype == XI_FocusOut ? "out" : | ||||
|                           "???", | ||||
|                           enter_event->event, | ||||
|                           meta_event_mode_to_string (enter_event->mode), | ||||
|                           meta_event_detail_to_string (enter_event->detail)); | ||||
|             } | ||||
|           else | ||||
|           /* libXi does not properly copy the serial to the XIEnterEvent, so pull it | ||||
|            * from the parent XAnyEvent. | ||||
|            * See: https://bugs.freedesktop.org/show_bug.cgi?id=64687 | ||||
|            */ | ||||
|           handle_window_focus_event (display, window, enter_event, event->xany.serial); | ||||
|           if (!window) | ||||
|             { | ||||
|               /* Check if the window is a root window. */ | ||||
|               MetaScreen *screen = | ||||
|                 meta_display_screen_for_root(display, | ||||
|                                              enter_event->event); | ||||
|               if (screen == NULL) | ||||
|                 break; | ||||
|  | ||||
|               meta_topic (META_DEBUG_FOCUS, | ||||
|                           "Focus %s event received on root window 0x%lx " | ||||
|                           "mode %s detail %s\n", | ||||
|                           enter_event->evtype == XI_FocusIn ? "in" : | ||||
|                           enter_event->evtype == XI_FocusOut ? "out" : | ||||
|                           "???", | ||||
|                           enter_event->event, | ||||
|                           meta_event_mode_to_string (enter_event->mode), | ||||
|                           meta_event_detail_to_string (enter_event->detail)); | ||||
|            | ||||
|               if (enter_event->evtype == XI_FocusIn && | ||||
|                   enter_event->mode == XINotifyDetailNone) | ||||
| @@ -2521,13 +2779,6 @@ event_callback (XEvent   *event, | ||||
|                                   window->unmaps_pending); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|               /* Unfocus on UnmapNotify, do this after the possible | ||||
|                * window_free above so that window_free can see if window->has_focus | ||||
|                * and move focus to another window | ||||
|                */ | ||||
|               if (window) | ||||
|                 meta_window_lost_focus (window); | ||||
|             } | ||||
|           break; | ||||
|         case MapNotify: | ||||
| @@ -5561,98 +5812,74 @@ sanity_check_timestamps (MetaDisplay *display, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| timestamp_too_old (MetaDisplay *display, | ||||
|                    MetaWindow  *window, | ||||
|                    guint32     *timestamp) | ||||
| { | ||||
|   /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow | ||||
|    * us to sanity check the timestamp here and ensure it doesn't correspond to | ||||
|    * a future time (though we would want to rename to  | ||||
|    * timestamp_too_old_or_in_future). | ||||
|    */ | ||||
|  | ||||
|   if (*timestamp == CurrentTime) | ||||
|     { | ||||
|       meta_warning ("Got a request to focus %s with a timestamp of 0.  This " | ||||
|                     "shouldn't happen!\n", | ||||
|                     window ? window->desc : "the no_focus_window"); | ||||
|       *timestamp = meta_display_get_current_time_roundtrip (display); | ||||
|       return FALSE; | ||||
|     } | ||||
|   else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time)) | ||||
|     { | ||||
|       if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time)) | ||||
|         { | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "Ignoring focus request for %s since %u " | ||||
|                       "is less than %u and %u.\n", | ||||
|                       window ? window->desc : "the no_focus_window", | ||||
|                       *timestamp, | ||||
|                       display->last_user_time, | ||||
|                       display->last_focus_time); | ||||
|           return TRUE; | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "Received focus request for %s which is newer than most " | ||||
|                       "recent user_time, but less recent than " | ||||
|                       "last_focus_time (%u < %u < %u); adjusting " | ||||
|                       "accordingly.  (See bug 167358)\n", | ||||
|                       window ? window->desc : "the no_focus_window", | ||||
|                       display->last_user_time, | ||||
|                       *timestamp, | ||||
|                       display->last_focus_time); | ||||
|           *timestamp = display->last_focus_time; | ||||
|           return FALSE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_set_input_focus_window (MetaDisplay *display,  | ||||
|                                      MetaWindow  *window, | ||||
|                                      gboolean     focus_frame, | ||||
|                                      guint32      timestamp) | ||||
| { | ||||
|   if (timestamp_too_old (display, window, ×tamp)) | ||||
|     return; | ||||
|  | ||||
|   meta_error_trap_push (display); | ||||
|   XSetInputFocus (display->xdisplay, | ||||
|                   focus_frame ? window->frame->xwindow : window->xwindow, | ||||
|                   RevertToPointerRoot, | ||||
|                   timestamp); | ||||
|   meta_error_trap_pop (display); | ||||
|  | ||||
|   display->expected_focus_window = window; | ||||
|   display->last_focus_time = timestamp; | ||||
|   display->active_screen = window->screen; | ||||
|  | ||||
|   if (window != display->autoraise_window) | ||||
|     meta_display_remove_autoraise_callback (window->display); | ||||
|   request_xserver_input_focus_change (display, | ||||
|                                       window->screen, | ||||
|                                       focus_frame ? window->frame->xwindow : window->xwindow, | ||||
|                                       timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_focus_the_no_focus_window (MetaDisplay *display,  | ||||
| meta_display_request_take_focus (MetaDisplay *display, | ||||
|                                  MetaWindow  *window, | ||||
|                                  guint32      timestamp) | ||||
| { | ||||
|   if (timestamp_too_old (display, ×tamp)) | ||||
|     return; | ||||
|  | ||||
|   meta_topic (META_DEBUG_FOCUS, "WM_TAKE_FOCUS(%s, %u)\n", | ||||
|               window->desc, timestamp); | ||||
|  | ||||
|   if (window != display->focus_window) | ||||
|     { | ||||
|       /* The "Globally Active Input" window case, where the window | ||||
|        * doesn't want us to call XSetInputFocus on it, but does | ||||
|        * want us to send a WM_TAKE_FOCUS. | ||||
|        * | ||||
|        * We can't just set display->focus_window to @window, since we | ||||
|        * we don't know when (or even if) the window will actually take | ||||
|        * focus, so we could end up being wrong for arbitrarily long. | ||||
|        * But we also can't leave it set to the current window, or else | ||||
|        * bug #597352 would come back. So we focus the no_focus_window | ||||
|        * now (and set display->focus_window to that), send the | ||||
|        * WM_TAKE_FOCUS, and then just forget about @window | ||||
|        * until/unless we get a FocusIn. | ||||
|        */ | ||||
|       meta_display_focus_the_no_focus_window (display, | ||||
|                                               window->screen, | ||||
|                                               timestamp); | ||||
|     } | ||||
|   meta_window_send_icccm_message (window, | ||||
|                                   display->atom_WM_TAKE_FOCUS, | ||||
|                                   timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_set_input_focus_xwindow (MetaDisplay *display, | ||||
|                                       MetaScreen  *screen, | ||||
|                                       Window       window, | ||||
|                                       guint32      timestamp) | ||||
| { | ||||
|   request_xserver_input_focus_change (display, | ||||
|                                       screen, | ||||
|                                       window, | ||||
|                                       timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_focus_the_no_focus_window (MetaDisplay *display, | ||||
|                                         MetaScreen  *screen, | ||||
|                                         guint32      timestamp) | ||||
| { | ||||
|   if (timestamp_too_old (display, NULL, ×tamp)) | ||||
|     return; | ||||
|  | ||||
|   XSetInputFocus (display->xdisplay, | ||||
|                   screen->no_focus_window, | ||||
|                   RevertToPointerRoot, | ||||
|                   timestamp); | ||||
|   display->expected_focus_window = NULL; | ||||
|   display->last_focus_time = timestamp; | ||||
|   display->active_screen = screen; | ||||
|  | ||||
|   meta_display_remove_autoraise_callback (display); | ||||
|   request_xserver_input_focus_change (display, | ||||
|                                       screen, | ||||
|                                       screen->no_focus_window, | ||||
|                                       timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -5681,6 +5908,16 @@ meta_display_accelerator_activate (MetaDisplay *display, | ||||
|                  0, action, deviceid); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_display_modifiers_accelerator_activate (MetaDisplay *display) | ||||
| { | ||||
|   gboolean freeze; | ||||
|  | ||||
|   g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0, &freeze); | ||||
|  | ||||
|   return freeze; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_get_compositor_version (MetaDisplay *display, | ||||
|                                      int         *major, | ||||
| @@ -5762,11 +5999,9 @@ meta_display_has_shape (MetaDisplay *display) | ||||
|  * meta_display_get_focus_window: | ||||
|  * @display: a #MetaDisplay | ||||
|  * | ||||
|  * Get the window that, according to events received from X server, | ||||
|  * currently has the input focus. We may have already sent a request | ||||
|  * to the X server to move the focus window elsewhere. (The | ||||
|  * expected_focus_window records where we've last set the input | ||||
|  * focus.) | ||||
|  * Get our best guess as to the "currently" focused window (that is, | ||||
|  * the window that we expect will be focused at the point when the X | ||||
|  * server processes our next request). | ||||
|  * | ||||
|  * Return Value: (transfer none): The current focus window | ||||
|  */ | ||||
|   | ||||
| @@ -332,19 +332,6 @@ meta_frame_calc_borders (MetaFrame        *frame, | ||||
|                                borders); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_frame_get_corner_radiuses (MetaFrame *frame, | ||||
|                                 float     *top_left, | ||||
|                                 float     *top_right, | ||||
|                                 float     *bottom_left, | ||||
|                                 float     *bottom_right) | ||||
| { | ||||
|   meta_ui_get_corner_radiuses (frame->window->screen->ui, | ||||
|                                frame->xwindow, | ||||
|                                top_left, top_right, | ||||
|                                bottom_left, bottom_right); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_frame_sync_to_window (MetaFrame *frame, | ||||
|                            int        resize_gravity, | ||||
| @@ -400,6 +387,14 @@ meta_frame_get_frame_bounds (MetaFrame *frame) | ||||
|                                    frame->rect.height); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_frame_get_mask (MetaFrame                    *frame, | ||||
|                      cairo_t                      *cr) | ||||
| { | ||||
|   meta_ui_get_frame_mask (frame->window->screen->ui, frame->xwindow, | ||||
|                           frame->rect.width, frame->rect.height, cr); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_frame_queue_draw (MetaFrame *frame) | ||||
| { | ||||
|   | ||||
| @@ -63,12 +63,6 @@ Window         meta_frame_get_xwindow (MetaFrame *frame); | ||||
| void meta_frame_calc_borders      (MetaFrame        *frame, | ||||
|                                    MetaFrameBorders *borders); | ||||
|  | ||||
| void meta_frame_get_corner_radiuses (MetaFrame *frame, | ||||
|                                      float     *top_left, | ||||
|                                      float     *top_right, | ||||
|                                      float     *bottom_left, | ||||
|                                      float     *bottom_right); | ||||
|  | ||||
| gboolean meta_frame_sync_to_window (MetaFrame         *frame, | ||||
|                                     int                gravity, | ||||
|                                     gboolean           need_move, | ||||
| @@ -76,6 +70,9 @@ gboolean meta_frame_sync_to_window (MetaFrame         *frame, | ||||
|  | ||||
| cairo_region_t *meta_frame_get_frame_bounds (MetaFrame *frame); | ||||
|  | ||||
| void meta_frame_get_mask (MetaFrame *frame, | ||||
|                           cairo_t   *cr); | ||||
|  | ||||
| void meta_frame_set_screen_cursor (MetaFrame	*frame, | ||||
| 				   MetaCursor	cursor); | ||||
|  | ||||
|   | ||||
| @@ -302,6 +302,172 @@ reload_modmap (MetaDisplay *display) | ||||
|               display->meta_mask); | ||||
| } | ||||
|  | ||||
| /* Original code from gdk_x11_keymap_get_entries_for_keyval() in | ||||
|  * gdkkeys-x11.c */ | ||||
| static int | ||||
| get_keycodes_for_keysym (MetaDisplay  *display, | ||||
|                          int           keysym, | ||||
|                          int         **keycodes) | ||||
| { | ||||
|   GArray *retval; | ||||
|   int n_keycodes; | ||||
|   int keycode; | ||||
|  | ||||
|   retval = g_array_new (FALSE, FALSE, sizeof (int)); | ||||
|  | ||||
|   keycode = display->min_keycode; | ||||
|   while (keycode <= display->max_keycode) | ||||
|     { | ||||
|       const KeySym *syms = display->keymap + (keycode - display->min_keycode) * display->keysyms_per_keycode; | ||||
|       int i = 0; | ||||
|  | ||||
|       while (i < display->keysyms_per_keycode) | ||||
|         { | ||||
|           if (syms[i] == (unsigned int)keysym) | ||||
|             g_array_append_val (retval, keycode); | ||||
|  | ||||
|           ++i; | ||||
|         } | ||||
|  | ||||
|       ++keycode; | ||||
|     } | ||||
|  | ||||
|   n_keycodes = retval->len; | ||||
|   *keycodes = (int*) g_array_free (retval, n_keycodes == 0 ? TRUE : FALSE); | ||||
|  | ||||
|   return n_keycodes; | ||||
| } | ||||
|  | ||||
| static void | ||||
| reload_iso_next_group_combos (MetaDisplay *display) | ||||
| { | ||||
|   const char *iso_next_group_option; | ||||
|   MetaKeyCombo *combos; | ||||
|   int *keycodes; | ||||
|   int n_keycodes; | ||||
|   int n_combos; | ||||
|   int i; | ||||
|  | ||||
|   g_clear_pointer (&display->iso_next_group_combos, g_free); | ||||
|   display->n_iso_next_group_combos = 0; | ||||
|  | ||||
|   iso_next_group_option = meta_prefs_get_iso_next_group_option (); | ||||
|   if (iso_next_group_option == NULL) | ||||
|     return; | ||||
|  | ||||
|   n_keycodes = get_keycodes_for_keysym (display, XK_ISO_Next_Group, &keycodes); | ||||
|  | ||||
|   if (g_str_equal (iso_next_group_option, "toggle") || | ||||
|       g_str_equal (iso_next_group_option, "lalt_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "lwin_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "rwin_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "lshift_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "rshift_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "lctrl_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "rctrl_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "sclk_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "menu_toggle") || | ||||
|       g_str_equal (iso_next_group_option, "caps_toggle")) | ||||
|     { | ||||
|       n_combos = n_keycodes; | ||||
|       combos = g_new (MetaKeyCombo, n_combos); | ||||
|  | ||||
|       for (i = 0; i < n_keycodes; ++i) | ||||
|         { | ||||
|           combos[i].keysym = XK_ISO_Next_Group; | ||||
|           combos[i].keycode = keycodes[i]; | ||||
|           combos[i].modifiers = 0; | ||||
|         } | ||||
|     } | ||||
|   else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") || | ||||
|            g_str_equal (iso_next_group_option, "shifts_toggle")) | ||||
|     { | ||||
|       n_combos = n_keycodes; | ||||
|       combos = g_new (MetaKeyCombo, n_combos); | ||||
|  | ||||
|       for (i = 0; i < n_keycodes; ++i) | ||||
|         { | ||||
|           combos[i].keysym = XK_ISO_Next_Group; | ||||
|           combos[i].keycode = keycodes[i]; | ||||
|           combos[i].modifiers = ShiftMask; | ||||
|         } | ||||
|     } | ||||
|   else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") || | ||||
|            g_str_equal (iso_next_group_option, "alt_space_toggle")) | ||||
|     { | ||||
|       n_combos = n_keycodes; | ||||
|       combos = g_new (MetaKeyCombo, n_combos); | ||||
|  | ||||
|       for (i = 0; i < n_keycodes; ++i) | ||||
|         { | ||||
|           combos[i].keysym = XK_ISO_Next_Group; | ||||
|           combos[i].keycode = keycodes[i]; | ||||
|           combos[i].modifiers = Mod1Mask; | ||||
|         } | ||||
|     } | ||||
|   else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") || | ||||
|            g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") || | ||||
|            g_str_equal (iso_next_group_option, "rctrl_rshift_toggle")) | ||||
|     { | ||||
|       n_combos = n_keycodes * 2; | ||||
|       combos = g_new (MetaKeyCombo, n_combos); | ||||
|  | ||||
|       for (i = 0; i < n_keycodes; ++i) | ||||
|         { | ||||
|           combos[i].keysym = XK_ISO_Next_Group; | ||||
|           combos[i].keycode = keycodes[i]; | ||||
|           combos[i].modifiers = ShiftMask; | ||||
|  | ||||
|           combos[i + n_keycodes].keysym = XK_ISO_Next_Group; | ||||
|           combos[i + n_keycodes].keycode = keycodes[i]; | ||||
|           combos[i + n_keycodes].modifiers = ControlMask; | ||||
|         } | ||||
|     } | ||||
|   else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle")) | ||||
|     { | ||||
|       n_combos = n_keycodes * 2; | ||||
|       combos = g_new (MetaKeyCombo, n_combos); | ||||
|  | ||||
|       for (i = 0; i < n_keycodes; ++i) | ||||
|         { | ||||
|           combos[i].keysym = XK_ISO_Next_Group; | ||||
|           combos[i].keycode = keycodes[i]; | ||||
|           combos[i].modifiers = Mod1Mask; | ||||
|  | ||||
|           combos[i + n_keycodes].keysym = XK_ISO_Next_Group; | ||||
|           combos[i + n_keycodes].keycode = keycodes[i]; | ||||
|           combos[i + n_keycodes].modifiers = ControlMask; | ||||
|         } | ||||
|     } | ||||
|   else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") || | ||||
|            g_str_equal (iso_next_group_option, "lalt_lshift_toggle")) | ||||
|     { | ||||
|       n_combos = n_keycodes * 2; | ||||
|       combos = g_new (MetaKeyCombo, n_combos); | ||||
|  | ||||
|       for (i = 0; i < n_keycodes; ++i) | ||||
|         { | ||||
|           combos[i].keysym = XK_ISO_Next_Group; | ||||
|           combos[i].keycode = keycodes[i]; | ||||
|           combos[i].modifiers = Mod1Mask; | ||||
|  | ||||
|           combos[i + n_keycodes].keysym = XK_ISO_Next_Group; | ||||
|           combos[i + n_keycodes].keycode = keycodes[i]; | ||||
|           combos[i + n_keycodes].modifiers = ShiftMask; | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       n_combos = 0; | ||||
|       combos = NULL; | ||||
|     } | ||||
|  | ||||
|   g_free (keycodes); | ||||
|  | ||||
|   display->n_iso_next_group_combos = n_combos; | ||||
|   display->iso_next_group_combos = combos; | ||||
| } | ||||
|  | ||||
| static guint | ||||
| keysym_to_keycode (MetaDisplay *display, | ||||
|                    guint        keysym) | ||||
| @@ -328,6 +494,8 @@ reload_keycodes (MetaDisplay *display) | ||||
|       display->overlay_key_combo.keycode = 0; | ||||
|     } | ||||
|  | ||||
|   reload_iso_next_group_combos (display); | ||||
|  | ||||
|   if (display->key_bindings) | ||||
|     { | ||||
|       int i; | ||||
| @@ -1026,6 +1194,22 @@ meta_screen_change_keygrabs (MetaScreen *screen, | ||||
|                          display->overlay_key_combo.keycode, | ||||
|                          display->overlay_key_combo.modifiers); | ||||
|  | ||||
|   if (display->iso_next_group_combos) | ||||
|     { | ||||
|       int i = 0; | ||||
|       while (i < display->n_iso_next_group_combos) | ||||
|         { | ||||
|           if (display->iso_next_group_combos[i].keycode != 0) | ||||
|             { | ||||
|               meta_change_keygrab (display, screen->xroot, grab, | ||||
|                                    display->iso_next_group_combos[i].keysym, | ||||
|                                    display->iso_next_group_combos[i].keycode, | ||||
|                                    display->iso_next_group_combos[i].modifiers); | ||||
|             } | ||||
|           ++i; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   change_binding_keygrabs (screen->display->key_bindings, | ||||
|                            screen->display->n_key_bindings, | ||||
|                            screen->display, screen->xroot, | ||||
| @@ -1264,7 +1448,8 @@ grab_status_to_string (int status) | ||||
| static gboolean | ||||
| grab_keyboard (MetaDisplay *display, | ||||
|                Window       xwindow, | ||||
|                guint32      timestamp) | ||||
|                guint32      timestamp, | ||||
|                int          grab_mode) | ||||
| { | ||||
|   int result; | ||||
|   int grab_status; | ||||
| @@ -1280,12 +1465,21 @@ grab_keyboard (MetaDisplay *display, | ||||
|    */ | ||||
|   meta_error_trap_push_with_return (display); | ||||
|  | ||||
|   /* Strictly, we only need to set grab_mode on the keyboard device | ||||
|    * while the pointer should always be XIGrabModeAsync. Unfortunately | ||||
|    * there is a bug in the X server, only fixed (link below) in 1.15, | ||||
|    * which swaps these arguments for keyboard devices. As such, we set | ||||
|    * both the device and the paired device mode which works around | ||||
|    * that bug and also works on fixed X servers. | ||||
|    * | ||||
|    * http://cgit.freedesktop.org/xorg/xserver/commit/?id=9003399708936481083424b4ff8f18a16b88b7b3 | ||||
|    */ | ||||
|   grab_status = XIGrabDevice (display->xdisplay, | ||||
|                               META_VIRTUAL_CORE_KEYBOARD_ID, | ||||
|                               xwindow, | ||||
|                               timestamp, | ||||
|                               None, | ||||
|                               XIGrabModeAsync, XIGrabModeAsync, | ||||
|                               grab_mode, grab_mode, | ||||
|                               True, /* owner_events */ | ||||
|                               &mask); | ||||
|  | ||||
| @@ -1339,7 +1533,7 @@ meta_screen_grab_all_keys (MetaScreen *screen, guint32 timestamp) | ||||
|  | ||||
|   meta_topic (META_DEBUG_KEYBINDINGS, | ||||
|               "Grabbing all keys on RootWindow\n"); | ||||
|   retval = grab_keyboard (screen->display, screen->xroot, timestamp); | ||||
|   retval = grab_keyboard (screen->display, screen->xroot, timestamp, XIGrabModeAsync); | ||||
|   if (retval) | ||||
|     { | ||||
|       screen->all_keys_grabbed = TRUE; | ||||
| @@ -1392,7 +1586,7 @@ meta_window_grab_all_keys (MetaWindow  *window, | ||||
|  | ||||
|   meta_topic (META_DEBUG_KEYBINDINGS, | ||||
|               "Grabbing all keys on window %s\n", window->desc); | ||||
|   retval = grab_keyboard (window->display, grabwindow, timestamp); | ||||
|   retval = grab_keyboard (window->display, grabwindow, timestamp, XIGrabModeAsync); | ||||
|   if (retval) | ||||
|     { | ||||
|       window->keys_grabbed = FALSE; | ||||
| @@ -1419,6 +1613,32 @@ meta_window_ungrab_all_keys (MetaWindow *window, guint32 timestamp) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_freeze_keyboard (MetaDisplay *display, Window window, guint32 timestamp) | ||||
| { | ||||
|   grab_keyboard (display, window, timestamp, XIGrabModeSync); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp) | ||||
| { | ||||
|   ungrab_keyboard (display, timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp) | ||||
| { | ||||
|   meta_error_trap_push (display); | ||||
|   XIAllowEvents (display->xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, | ||||
|                  XIAsyncDevice, timestamp); | ||||
|   /* We shouldn't need to unfreeze the pointer device here, however we | ||||
|    * have to, due to the workaround we do in grab_keyboard(). | ||||
|    */ | ||||
|   XIAllowEvents (display->xdisplay, META_VIRTUAL_CORE_POINTER_ID, | ||||
|                  XIAsyncDevice, timestamp); | ||||
|   meta_error_trap_pop (display); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| is_modifier (MetaDisplay *display, | ||||
|              unsigned int keycode) | ||||
| @@ -1765,6 +1985,41 @@ process_overlay_key (MetaDisplay *display, | ||||
|     return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| process_iso_next_group (MetaDisplay *display, | ||||
|                         MetaScreen *screen, | ||||
|                         XIDeviceEvent *event, | ||||
|                         KeySym keysym) | ||||
| { | ||||
|   gboolean activate; | ||||
|   unsigned int mods; | ||||
|   int i; | ||||
|  | ||||
|   if (event->evtype != XI_KeyPress) | ||||
|     return FALSE; | ||||
|  | ||||
|   activate = FALSE; | ||||
|   mods = (event->mods.effective & 0xff & ~(display->ignored_modifier_mask)); | ||||
|  | ||||
|   for (i = 0; i < display->n_iso_next_group_combos; ++i) | ||||
|     { | ||||
|       if (event->detail == (int)display->iso_next_group_combos[i].keycode && | ||||
|           mods == display->iso_next_group_combos[i].modifiers) | ||||
|         { | ||||
|           /* If the signal handler returns TRUE the keyboard will | ||||
|              remain frozen. It's the signal handler's responsibility | ||||
|              to unfreeze it. */ | ||||
|           if (!meta_display_modifiers_accelerator_activate (display)) | ||||
|             XIAllowEvents (display->xdisplay, event->deviceid, | ||||
|                            XIAsyncDevice, event->time); | ||||
|           activate = TRUE; | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   return activate; | ||||
| } | ||||
|  | ||||
| /* Handle a key event. May be called recursively: some key events cause | ||||
|  * grabs to be ended and then need to be processed again in their own | ||||
|  * right. This cannot cause infinite recursion because we never call | ||||
| @@ -1839,6 +2094,10 @@ meta_display_process_key_event (MetaDisplay   *display, | ||||
|       handled = process_overlay_key (display, screen, event, keysym); | ||||
|       if (handled) | ||||
|         return TRUE; | ||||
|  | ||||
|       handled = process_iso_next_group (display, screen, event, keysym); | ||||
|       if (handled) | ||||
|         return TRUE; | ||||
|     } | ||||
|  | ||||
|   XIAllowEvents (display->xdisplay, event->deviceid, | ||||
| @@ -4532,6 +4791,12 @@ meta_display_init_keys (MetaDisplay *display) | ||||
|  | ||||
|   g_hash_table_insert (key_handlers, g_strdup ("overlay-key"), handler); | ||||
|  | ||||
|   handler = g_new0 (MetaKeyHandler, 1); | ||||
|   handler->name = g_strdup ("iso-next-group"); | ||||
|   handler->flags = META_KEY_BINDING_BUILTIN; | ||||
|  | ||||
|   g_hash_table_insert (key_handlers, g_strdup ("iso-next-group"), handler); | ||||
|  | ||||
|   handler = g_new0 (MetaKeyHandler, 1); | ||||
|   handler->name = g_strdup ("external-grab"); | ||||
|   handler->func = handle_external_grab; | ||||
|   | ||||
							
								
								
									
										279
									
								
								src/core/prefs.c
									
									
									
									
									
								
							
							
						
						
									
										279
									
								
								src/core/prefs.c
									
									
									
									
									
								
							| @@ -55,6 +55,7 @@ | ||||
| #define KEY_GNOME_ANIMATIONS "enable-animations" | ||||
| #define KEY_GNOME_CURSOR_THEME "cursor-theme" | ||||
| #define KEY_GNOME_CURSOR_SIZE "cursor-size" | ||||
| #define KEY_XKB_OPTIONS "xkb-options" | ||||
|  | ||||
| #define KEY_OVERLAY_KEY "overlay-key" | ||||
| #define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary" | ||||
| @@ -65,6 +66,7 @@ | ||||
| #define SCHEMA_GENERAL         "org.gnome.desktop.wm.preferences" | ||||
| #define SCHEMA_MUTTER          "org.gnome.mutter" | ||||
| #define SCHEMA_INTERFACE       "org.gnome.desktop.interface" | ||||
| #define SCHEMA_INPUT_SOURCES   "org.gnome.desktop.input-sources" | ||||
|  | ||||
| #define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s)) | ||||
|  | ||||
| @@ -115,6 +117,7 @@ static gboolean workspaces_only_on_primary = FALSE; | ||||
|  | ||||
| static gboolean no_tab_popup = FALSE; | ||||
|  | ||||
| static char *iso_next_group_option = NULL; | ||||
|  | ||||
| static void handle_preference_update_enum (GSettings *settings, | ||||
|                                            gchar     *key); | ||||
| @@ -122,7 +125,6 @@ static gboolean update_binding         (MetaKeyPref *binding, | ||||
|                                         gchar      **strokes); | ||||
| static gboolean update_key_binding     (const char  *key, | ||||
|                                         gchar      **strokes); | ||||
| static gboolean update_workspace_names (void); | ||||
|  | ||||
| static void settings_changed (GSettings      *settings, | ||||
|                               gchar          *key, | ||||
| @@ -140,11 +142,11 @@ static gboolean theme_name_handler (GVariant*, gpointer*, gpointer); | ||||
| static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer); | ||||
| static gboolean button_layout_handler (GVariant*, gpointer*, gpointer); | ||||
| static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer); | ||||
| static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer); | ||||
|  | ||||
| static void     do_override               (char *key, char *schema); | ||||
|  | ||||
| static void     init_bindings             (void); | ||||
| static void     init_workspace_names      (void); | ||||
|  | ||||
|  | ||||
| typedef struct | ||||
| @@ -198,6 +200,13 @@ typedef struct | ||||
|   gchar **target; | ||||
| } MetaStringPreference; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   MetaBasePreference base; | ||||
|   GSettingsGetMapping handler; | ||||
|   gchar ***target; | ||||
| } MetaStringArrayPreference; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   MetaBasePreference base; | ||||
| @@ -436,6 +445,27 @@ static MetaStringPreference preferences_string[] = | ||||
|     { { NULL, 0, 0 }, NULL }, | ||||
|   }; | ||||
|  | ||||
| static MetaStringArrayPreference preferences_string_array[] = | ||||
|   { | ||||
|     { | ||||
|       { KEY_WORKSPACE_NAMES, | ||||
|         SCHEMA_GENERAL, | ||||
|         META_PREF_KEYBINDINGS, | ||||
|       }, | ||||
|       NULL, | ||||
|       &workspace_names, | ||||
|     }, | ||||
|     { | ||||
|       { KEY_XKB_OPTIONS, | ||||
|         SCHEMA_INPUT_SOURCES, | ||||
|         META_PREF_KEYBINDINGS, | ||||
|       }, | ||||
|       iso_next_group_handler, | ||||
|       NULL, | ||||
|     }, | ||||
|     { { NULL, 0, 0 }, NULL }, | ||||
|   }; | ||||
|  | ||||
| static MetaIntPreference preferences_int[] = | ||||
|   { | ||||
|     { | ||||
| @@ -555,6 +585,42 @@ handle_preference_init_string (void) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_preference_init_string_array (void) | ||||
| { | ||||
|   MetaStringArrayPreference *cursor = preferences_string_array; | ||||
|  | ||||
|   while (cursor->base.key != NULL) | ||||
|     { | ||||
|       char **value; | ||||
|  | ||||
|       /* Complex keys have a mapping function to check validity */ | ||||
|       if (cursor->handler) | ||||
|         { | ||||
|           if (cursor->target) | ||||
|             meta_bug ("%s has both a target and a handler\n", cursor->base.key); | ||||
|  | ||||
|           g_settings_get_mapped (SETTINGS (cursor->base.schema), | ||||
|                                  cursor->base.key, cursor->handler, NULL); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           if (!cursor->target) | ||||
|             meta_bug ("%s must have handler or target\n", cursor->base.key); | ||||
|  | ||||
|           if (*(cursor->target)) | ||||
|             g_strfreev (*(cursor->target)); | ||||
|  | ||||
|           value = g_settings_get_strv (SETTINGS (cursor->base.schema), | ||||
|                                        cursor->base.key); | ||||
|  | ||||
|           *(cursor->target) = value; | ||||
|         } | ||||
|  | ||||
|       ++cursor; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_preference_init_int (void) | ||||
| { | ||||
| @@ -673,6 +739,56 @@ handle_preference_update_string (GSettings *settings, | ||||
|     queue_changed (cursor->base.pref); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_preference_update_string_array (GSettings *settings, | ||||
|                                        gchar *key) | ||||
| { | ||||
|   MetaStringArrayPreference *cursor = preferences_string_array; | ||||
|   gboolean inform_listeners = FALSE; | ||||
|  | ||||
|   while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) | ||||
|     ++cursor; | ||||
|  | ||||
|   if (cursor->base.key==NULL) | ||||
|     /* Didn't recognise that key. */ | ||||
|     return; | ||||
|  | ||||
|   /* Complex keys have a mapping function to check validity */ | ||||
|   if (cursor->handler) | ||||
|     { | ||||
|       if (cursor->target) | ||||
|         meta_bug ("%s has both a target and a handler\n", cursor->base.key); | ||||
|  | ||||
|       g_settings_get_mapped (SETTINGS (cursor->base.schema), | ||||
|                              cursor->base.key, cursor->handler, NULL); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       char **values, **previous; | ||||
|       int n_values, n_previous, i; | ||||
|  | ||||
|       if (!cursor->target) | ||||
|         meta_bug ("%s must have handler or target\n", cursor->base.key); | ||||
|  | ||||
|       values = g_settings_get_strv (SETTINGS (cursor->base.schema), | ||||
|                                     cursor->base.key); | ||||
|       n_values = g_strv_length (values); | ||||
|       previous = *(cursor->target); | ||||
|       n_previous = previous ? g_strv_length (previous) : 0; | ||||
|  | ||||
|       inform_listeners = n_previous != n_values; | ||||
|       for (i = 0; i < n_values && !inform_listeners; i++) | ||||
|         inform_listeners = g_strcmp0 (values[i], previous[i]) != 0; | ||||
|  | ||||
|       if (*(cursor->target)) | ||||
|         g_strfreev (*(cursor->target)); | ||||
|       *(cursor->target) = values; | ||||
|     } | ||||
|  | ||||
|   if (inform_listeners) | ||||
|     queue_changed (cursor->base.pref); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_preference_update_int (GSettings *settings, | ||||
|                               gchar *key) | ||||
| @@ -857,6 +973,11 @@ meta_prefs_init (void) | ||||
|                     G_CALLBACK (settings_changed), NULL); | ||||
|   g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings); | ||||
|  | ||||
|   settings = g_settings_new (SCHEMA_INPUT_SOURCES); | ||||
|   g_signal_connect (settings, "changed::" KEY_XKB_OPTIONS, | ||||
|                     G_CALLBACK (settings_changed), NULL); | ||||
|   g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INPUT_SOURCES), settings); | ||||
|  | ||||
|  | ||||
|   for (tmp = overridden_keys; tmp; tmp = tmp->next) | ||||
|     { | ||||
| @@ -869,10 +990,10 @@ meta_prefs_init (void) | ||||
|   handle_preference_init_enum (); | ||||
|   handle_preference_init_bool (); | ||||
|   handle_preference_init_string (); | ||||
|   handle_preference_init_string_array (); | ||||
|   handle_preference_init_int (); | ||||
|  | ||||
|   init_bindings (); | ||||
|   init_workspace_names (); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| @@ -1020,14 +1141,6 @@ settings_changed (GSettings *settings, | ||||
|   MetaEnumPreference *cursor; | ||||
|   gboolean found_enum; | ||||
|  | ||||
|   /* String array, handled separately */ | ||||
|   if (strcmp (key, KEY_WORKSPACE_NAMES) == 0) | ||||
|     { | ||||
|       if (update_workspace_names ()) | ||||
|         queue_changed (META_PREF_WORKSPACE_NAMES); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   value = g_settings_get_value (settings, key); | ||||
|   type = g_variant_get_type (value); | ||||
|  | ||||
| @@ -1035,6 +1148,8 @@ settings_changed (GSettings *settings, | ||||
|     handle_preference_update_bool (settings, key); | ||||
|   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) | ||||
|     handle_preference_update_int (settings, key); | ||||
|   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY)) | ||||
|     handle_preference_update_string_array (settings, key); | ||||
|   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) | ||||
|     { | ||||
|       cursor = preferences_enum; | ||||
| @@ -1554,6 +1669,39 @@ overlay_key_handler (GVariant *value, | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| iso_next_group_handler (GVariant *value, | ||||
|                         gpointer *result, | ||||
|                         gpointer  data) | ||||
| { | ||||
|   const char **xkb_options, **p; | ||||
|   const char *option = NULL; | ||||
|   gboolean changed = FALSE; | ||||
|  | ||||
|   *result = NULL; /* ignored */ | ||||
|   xkb_options = g_variant_get_strv (value, NULL); | ||||
|  | ||||
|   for (p = xkb_options; p && *p; ++p) | ||||
|     if (g_str_has_prefix (*p, "grp:")) | ||||
|       { | ||||
|         option = (*p + 4); | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|   changed = (g_strcmp0 (option, iso_next_group_option) != 0); | ||||
|  | ||||
|   if (changed) | ||||
|     { | ||||
|       g_free (iso_next_group_option); | ||||
|       iso_next_group_option = g_strdup (option); | ||||
|       queue_changed (META_PREF_KEYBINDINGS); | ||||
|     } | ||||
|  | ||||
|   g_free (xkb_options); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| const PangoFontDescription* | ||||
| meta_prefs_get_titlebar_font (void) | ||||
| { | ||||
| @@ -1708,12 +1856,13 @@ meta_prefs_set_num_workspaces (int n_workspaces) | ||||
| { | ||||
|   MetaBasePreference *pref; | ||||
|  | ||||
|   find_pref (preferences_int, sizeof(MetaIntPreference), | ||||
|              KEY_NUM_WORKSPACES, &pref); | ||||
|  | ||||
|   g_settings_set_int (SETTINGS (pref->schema), | ||||
|                       KEY_NUM_WORKSPACES, | ||||
|                       n_workspaces); | ||||
|   if (find_pref (preferences_int, sizeof(MetaIntPreference), | ||||
|                  KEY_NUM_WORKSPACES, &pref)) | ||||
|     { | ||||
|       g_settings_set_int (SETTINGS (pref->schema), | ||||
|                           KEY_NUM_WORKSPACES, | ||||
|                           n_workspaces); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static GHashTable *key_bindings; | ||||
| @@ -1747,20 +1896,15 @@ init_bindings (void) | ||||
|   g_hash_table_insert (key_bindings, g_strdup ("overlay-key"), pref); | ||||
| } | ||||
|  | ||||
| static void | ||||
| init_workspace_names (void) | ||||
| { | ||||
|   update_workspace_names (); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| update_binding (MetaKeyPref *binding, | ||||
|                 gchar      **strokes) | ||||
| { | ||||
|   GSList *old_bindings, *a, *b; | ||||
|   gboolean changed; | ||||
|   unsigned int keysym; | ||||
|   unsigned int keycode; | ||||
|   MetaVirtualModifier mods; | ||||
|   gboolean changed = FALSE; | ||||
|   MetaKeyCombo *combo; | ||||
|   int i; | ||||
|  | ||||
| @@ -1768,13 +1912,9 @@ update_binding (MetaKeyPref *binding, | ||||
|               "Binding \"%s\" has new GSettings value\n", | ||||
|               binding->name); | ||||
|  | ||||
|   /* Okay, so, we're about to provide a new list of key combos for this | ||||
|    * action. Delete any pre-existing list. | ||||
|    */ | ||||
|   g_slist_foreach (binding->bindings, (GFunc) g_free, NULL); | ||||
|   g_slist_free (binding->bindings); | ||||
|   old_bindings = binding->bindings; | ||||
|   binding->bindings = NULL; | ||||
|    | ||||
|  | ||||
|   for (i = 0; strokes && strokes[i]; i++) | ||||
|     { | ||||
|       keysym = 0; | ||||
| @@ -1809,8 +1949,6 @@ update_binding (MetaKeyPref *binding, | ||||
|            * Changing the key in response to a modification could lead to cyclic calls. */ | ||||
|           continue; | ||||
|         } | ||||
|    | ||||
|       changed = TRUE; | ||||
|  | ||||
|       combo = g_malloc0 (sizeof (MetaKeyCombo)); | ||||
|       combo->keysym = keysym; | ||||
| @@ -1825,6 +1963,34 @@ update_binding (MetaKeyPref *binding, | ||||
|  | ||||
|   binding->bindings = g_slist_reverse (binding->bindings); | ||||
|  | ||||
|   a = old_bindings; | ||||
|   b = binding->bindings; | ||||
|   while (TRUE) | ||||
|     { | ||||
|       if ((!a && b) || (a && !b)) | ||||
|         { | ||||
|           changed = TRUE; | ||||
|           break; | ||||
|         } | ||||
|       else if (!a && !b) | ||||
|         { | ||||
|           changed = FALSE; | ||||
|           break; | ||||
|         } | ||||
|       else if (memcmp (a->data, b->data, sizeof (MetaKeyCombo)) != 0) | ||||
|         { | ||||
|           changed = TRUE; | ||||
|           break; | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           a = a->next; | ||||
|           b = b->next; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   g_slist_free_full (old_bindings, g_free); | ||||
|  | ||||
|   return changed; | ||||
| } | ||||
|  | ||||
| @@ -1840,41 +2006,6 @@ update_key_binding (const char *key, | ||||
|     return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| update_workspace_names (void) | ||||
| { | ||||
|   int i; | ||||
|   char **names; | ||||
|   int n_workspace_names, n_names; | ||||
|   gboolean changed = FALSE; | ||||
|  | ||||
|   names = g_settings_get_strv (SETTINGS (SCHEMA_GENERAL), KEY_WORKSPACE_NAMES); | ||||
|   n_names = g_strv_length (names); | ||||
|   n_workspace_names = workspace_names ? g_strv_length (workspace_names) : 0; | ||||
|  | ||||
|   for (i = 0; i < n_names; i++) | ||||
|     if (n_workspace_names < i + 1 || !workspace_names[i] || | ||||
|         g_strcmp0 (names[i], workspace_names[i]) != 0) | ||||
|       { | ||||
|         changed = TRUE; | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|   if (n_workspace_names != n_names) | ||||
|     changed = TRUE; | ||||
|  | ||||
|   if (changed) | ||||
|     { | ||||
|       if (workspace_names) | ||||
|         g_strfreev (workspace_names); | ||||
|       workspace_names = names; | ||||
|     } | ||||
|   else | ||||
|     g_strfreev (names); | ||||
|  | ||||
|   return changed; | ||||
| } | ||||
|  | ||||
| const char* | ||||
| meta_prefs_get_workspace_name (int i) | ||||
| { | ||||
| @@ -2074,6 +2205,12 @@ meta_prefs_get_overlay_binding (MetaKeyCombo *combo) | ||||
|   *combo = overlay_key_combo; | ||||
| } | ||||
|  | ||||
| const char * | ||||
| meta_prefs_get_iso_next_group_option (void) | ||||
| { | ||||
|   return iso_next_group_option; | ||||
| } | ||||
|  | ||||
| GDesktopTitlebarAction | ||||
| meta_prefs_get_action_double_click_titlebar (void) | ||||
| { | ||||
| @@ -2216,9 +2353,11 @@ meta_prefs_set_no_tab_popup (gboolean whether) | ||||
| { | ||||
|   MetaBasePreference *pref; | ||||
|  | ||||
|   find_pref (preferences_bool, sizeof(MetaBoolPreference), | ||||
|              KEY_NO_TAB_POPUP, &pref); | ||||
|   g_settings_set_boolean (SETTINGS (pref->schema), KEY_NO_TAB_POPUP, whether); | ||||
|   if (find_pref (preferences_bool, sizeof(MetaBoolPreference), | ||||
|                  KEY_NO_TAB_POPUP, &pref)) | ||||
|     { | ||||
|       g_settings_set_boolean (SETTINGS (pref->schema), KEY_NO_TAB_POPUP, whether); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int | ||||
|   | ||||
| @@ -278,7 +278,7 @@ static gboolean | ||||
| is_focused_foreach (MetaWindow *window, | ||||
|                     void       *data) | ||||
| { | ||||
|   if (window == window->display->expected_focus_window) | ||||
|   if (window->has_focus) | ||||
|     { | ||||
|       *((gboolean*) data) = TRUE; | ||||
|       return FALSE; | ||||
| @@ -335,11 +335,11 @@ get_standalone_layer (MetaWindow *window) | ||||
|         layer = META_LAYER_BOTTOM; | ||||
|       else if (window->fullscreen && | ||||
|                (focused_transient || | ||||
|                 window == window->display->expected_focus_window || | ||||
|                 window->display->expected_focus_window == NULL || | ||||
|                 (window->display->expected_focus_window != NULL && | ||||
|                 window == window->display->focus_window || | ||||
|                 window->display->focus_window == NULL || | ||||
|                 (window->display->focus_window != NULL && | ||||
|                  windows_on_different_monitor (window, | ||||
|                                                window->display->expected_focus_window)))) | ||||
|                                                window->display->focus_window)))) | ||||
|         layer = META_LAYER_FULLSCREEN; | ||||
|       else if (window->wm_state_above && !META_WINDOW_MAXIMIZED (window)) | ||||
|         layer = META_LAYER_TOP; | ||||
|   | ||||
| @@ -277,7 +277,7 @@ struct _MetaWindow | ||||
|   /* EWHH demands attention flag */ | ||||
|   guint wm_state_demands_attention : 1; | ||||
|    | ||||
|   /* this flag tracks receipt of focus_in focus_out */ | ||||
|   /* TRUE iff window == window->display->focus_window */ | ||||
|   guint has_focus : 1; | ||||
|  | ||||
|   /* Have we placed this window? */ | ||||
| @@ -590,9 +590,8 @@ gboolean meta_window_property_notify   (MetaWindow *window, | ||||
|                                         XEvent     *event); | ||||
| gboolean meta_window_client_message    (MetaWindow *window, | ||||
|                                         XEvent     *event); | ||||
| gboolean meta_window_notify_focus      (MetaWindow *window, | ||||
|                                         XIEnterEvent *event); | ||||
| void     meta_window_lost_focus        (MetaWindow *window); | ||||
| void     meta_window_set_focused_internal (MetaWindow *window, | ||||
|                                            gboolean    focused); | ||||
|  | ||||
| void     meta_window_set_current_workspace_hint (MetaWindow *window); | ||||
|  | ||||
|   | ||||
| @@ -253,6 +253,8 @@ meta_window_finalize (GObject *object) | ||||
|   g_free (window->gtk_window_object_path); | ||||
|   g_free (window->gtk_app_menu_object_path); | ||||
|   g_free (window->gtk_menubar_object_path); | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_window_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -1752,16 +1754,6 @@ meta_window_unmanage (MetaWindow  *window, | ||||
|                                            window, | ||||
|                                            timestamp); | ||||
|     } | ||||
|   else if (window->display->expected_focus_window == window) | ||||
|     { | ||||
|       meta_topic (META_DEBUG_FOCUS, | ||||
|                   "Focusing default window since expected focus window freed %s\n", | ||||
|                   window->desc); | ||||
|       window->display->expected_focus_window = NULL; | ||||
|       meta_workspace_focus_default_window (window->screen->active_workspace, | ||||
|                                            window, | ||||
|                                            timestamp); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       meta_topic (META_DEBUG_FOCUS, | ||||
| @@ -1769,6 +1761,8 @@ meta_window_unmanage (MetaWindow  *window, | ||||
|                   window->desc); | ||||
|     } | ||||
|  | ||||
|   g_assert (window->display->focus_window != window); | ||||
|  | ||||
|   if (window->struts) | ||||
|     { | ||||
|       meta_free_gslist_and_elements (window->struts); | ||||
| @@ -1791,12 +1785,6 @@ meta_window_unmanage (MetaWindow  *window, | ||||
|  | ||||
|   g_assert (window->display->grab_window != window); | ||||
|  | ||||
|   if (window->display->focus_window == window) | ||||
|     { | ||||
|       window->display->focus_window = NULL; | ||||
|       g_object_notify (G_OBJECT (window->display), "focus-window"); | ||||
|     } | ||||
|  | ||||
|   if (window->maximized_horizontally || window->maximized_vertically) | ||||
|     unmaximize_window_before_freeing (window); | ||||
|  | ||||
| @@ -3330,14 +3318,7 @@ meta_window_hide (MetaWindow *window) | ||||
|       invalidate_work_areas (window); | ||||
|     } | ||||
|  | ||||
|   /* The check on expected_focus_window is a temporary workaround for | ||||
|    *  https://bugzilla.gnome.org/show_bug.cgi?id=597352 | ||||
|    * We may have already switched away from this window but not yet | ||||
|    * gotten FocusIn/FocusOut events. A more complete comprehensive | ||||
|    * fix for these type of issues is described in the bug. | ||||
|    */ | ||||
|   if (window->has_focus && | ||||
|       window == window->display->expected_focus_window) | ||||
|   if (window->has_focus) | ||||
|     { | ||||
|       MetaWindow *not_this_one = NULL; | ||||
|       MetaWorkspace *my_workspace = meta_window_get_workspace (window); | ||||
| @@ -5973,10 +5954,10 @@ meta_window_focus (MetaWindow  *window, | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "Sending WM_TAKE_FOCUS to %s since take_focus = true\n", | ||||
|                       window->desc); | ||||
|           meta_window_send_icccm_message (window, | ||||
|                                           window->display->atom_WM_TAKE_FOCUS, | ||||
|                                           timestamp); | ||||
|           window->display->expected_focus_window = window; | ||||
|  | ||||
|           meta_display_request_take_focus (window->display, | ||||
|                                            window, | ||||
|                                            timestamp); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -6516,7 +6497,7 @@ meta_window_configure_request (MetaWindow *window, | ||||
|   if (event->xconfigurerequest.value_mask & CWStackMode) | ||||
|     { | ||||
|       MetaWindow *active_window; | ||||
|       active_window = window->display->expected_focus_window; | ||||
|       active_window = window->display->focus_window; | ||||
|       if (meta_prefs_get_disable_workarounds ()) | ||||
|         { | ||||
|           meta_topic (META_DEBUG_STACK, | ||||
| @@ -6624,6 +6605,41 @@ meta_window_change_workspace_by_index (MetaWindow *window, | ||||
| #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10 | ||||
| #define _NET_WM_MOVERESIZE_CANCEL           11 | ||||
|  | ||||
| static int | ||||
| query_pressed_buttons (MetaWindow *window) | ||||
| { | ||||
|   double x, y, query_root_x, query_root_y; | ||||
|   Window root, child; | ||||
|   XIButtonState buttons; | ||||
|   XIModifierState mods; | ||||
|   XIGroupState group; | ||||
|   int button = 0; | ||||
|  | ||||
|   meta_error_trap_push (window->display); | ||||
|   XIQueryPointer (window->display->xdisplay, | ||||
|                   META_VIRTUAL_CORE_POINTER_ID, | ||||
|                   window->xwindow, | ||||
|                   &root, &child, | ||||
|                   &query_root_x, &query_root_y, | ||||
|                   &x, &y, | ||||
|                   &buttons, &mods, &group); | ||||
|  | ||||
|   if (meta_error_trap_pop_with_return (window->display) != Success) | ||||
|     goto out; | ||||
|  | ||||
|   if (XIMaskIsSet (buttons.mask, Button1)) | ||||
|     button |= 1 << 1; | ||||
|   if (XIMaskIsSet (buttons.mask, Button2)) | ||||
|     button |= 1 << 2; | ||||
|   if (XIMaskIsSet (buttons.mask, Button3)) | ||||
|     button |= 1 << 3; | ||||
|  | ||||
|   free (buttons.mask); | ||||
|  | ||||
|  out: | ||||
|   return button; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_client_message (MetaWindow *window, | ||||
|                             XEvent     *event) | ||||
| @@ -6982,56 +6998,58 @@ meta_window_client_message (MetaWindow *window, | ||||
|                 (op != META_GRAB_OP_MOVING && | ||||
|                  op != META_GRAB_OP_KEYBOARD_MOVING)))) | ||||
|         { | ||||
|           /* | ||||
|            * the button SHOULD already be included in the message | ||||
|            */ | ||||
|           int button_mask; | ||||
|  | ||||
|           meta_topic (META_DEBUG_WINDOW_OPS, | ||||
|                       "Beginning move/resize with button = %d\n", button); | ||||
|           meta_display_begin_grab_op (window->display, | ||||
|                                       window->screen, | ||||
|                                       window, | ||||
|                                       op, | ||||
|                                       FALSE, | ||||
|                                       frame_action, | ||||
|                                       button, 0, | ||||
|                                       timestamp, | ||||
|                                       x_root, | ||||
|                                       y_root); | ||||
|  | ||||
|           button_mask = query_pressed_buttons (window); | ||||
|  | ||||
|           if (button == 0) | ||||
|             { | ||||
|               double x, y, query_root_x, query_root_y; | ||||
|               Window root, child; | ||||
|               XIButtonState buttons; | ||||
|               XIModifierState mods; | ||||
|               XIGroupState group; | ||||
|  | ||||
|               /* The race conditions in this _NET_WM_MOVERESIZE thing | ||||
|                * are mind-boggling | ||||
|               /* | ||||
|                * the button SHOULD already be included in the message | ||||
|                */ | ||||
|               meta_error_trap_push (window->display); | ||||
|               XIQueryPointer (window->display->xdisplay, | ||||
|                               META_VIRTUAL_CORE_POINTER_ID, | ||||
|                               window->xwindow, | ||||
|                               &root, &child, | ||||
|                               &query_root_x, &query_root_y, | ||||
|                               &x, &y, | ||||
|                               &buttons, &mods, &group); | ||||
|               meta_error_trap_pop (window->display); | ||||
|  | ||||
|               if (XIMaskIsSet (buttons.mask, Button1)) | ||||
|               if ((button_mask & (1 << 1)) != 0) | ||||
|                 button = 1; | ||||
|               else if (XIMaskIsSet (buttons.mask, Button2)) | ||||
|               else if ((button_mask & (1 << 2)) != 0) | ||||
|                 button = 2; | ||||
|               else if (XIMaskIsSet (buttons.mask, Button3)) | ||||
|               else if ((button_mask & (1 << 3)) != 0) | ||||
|                 button = 3; | ||||
|  | ||||
|               if (button != 0) | ||||
|                 window->display->grab_button = button; | ||||
|               else | ||||
|                 button = 0; | ||||
|  | ||||
|               free (buttons.mask); | ||||
|                 meta_display_end_grab_op (window->display, | ||||
|                                           timestamp); | ||||
|             } | ||||
|  | ||||
|           if (button != 0) | ||||
|           else | ||||
|             { | ||||
|               meta_topic (META_DEBUG_WINDOW_OPS, | ||||
|                           "Beginning move/resize with button = %d\n", button); | ||||
|               meta_display_begin_grab_op (window->display, | ||||
|                                           window->screen, | ||||
|                                           window, | ||||
|                                           op, | ||||
|                                           FALSE, | ||||
|                                           frame_action, | ||||
|                                           button, 0, | ||||
|                                           timestamp, | ||||
|                                           x_root, | ||||
|                                           y_root); | ||||
|               /* There is a potential race here. If the user presses and | ||||
|                * releases their mouse button very fast, it's possible for | ||||
|                * both the ButtonPress and ButtonRelease to be sent to the | ||||
|                * client before it can get a chance to send _NET_WM_MOVERESIZE | ||||
|                * to us. When that happens, we'll become stuck in a grab | ||||
|                * state, as we haven't received a ButtonRelease to cancel the | ||||
|                * grab. | ||||
|                * | ||||
|                * We can solve this by querying after we take the explicit | ||||
|                * pointer grab -- if the button isn't pressed, we cancel the | ||||
|                * drag immediately. | ||||
|                */ | ||||
|  | ||||
|               if ((button_mask & (1 << button)) == 0) | ||||
|                 meta_display_end_grab_op (window->display, timestamp); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -7158,8 +7176,7 @@ meta_window_propagate_focus_appearance (MetaWindow *window, | ||||
|           parent->attached_focus_window = NULL; | ||||
|         } | ||||
|  | ||||
|       if (child_focus_state_changed && !parent->has_focus && | ||||
|           parent != window->display->expected_focus_window) | ||||
|       if (child_focus_state_changed && !parent->has_focus) | ||||
|         { | ||||
|           meta_window_appears_focused_changed (parent); | ||||
|         } | ||||
| @@ -7170,23 +7187,85 @@ meta_window_propagate_focus_appearance (MetaWindow *window, | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_lost_focus (MetaWindow *window) | ||||
| meta_window_set_focused_internal (MetaWindow *window, | ||||
|                                   gboolean    focused) | ||||
| { | ||||
|   if (window == window->display->focus_window) | ||||
|   if (focused) | ||||
|     { | ||||
|       meta_topic (META_DEBUG_FOCUS, | ||||
|                   "%s is now the previous focus window due to being focused out or unmapped\n", | ||||
|                   window->desc); | ||||
|       window->has_focus = TRUE; | ||||
|       if (window->override_redirect) | ||||
|         return; | ||||
|  | ||||
|       meta_topic (META_DEBUG_FOCUS, | ||||
|                   "* Focus --> NULL (was %s)\n", window->desc); | ||||
|       /* Move to the front of the focusing workspace's MRU list. | ||||
|        * We should only be "removing" it from the MRU list if it's | ||||
|        * not already there.  Note that it's possible that we might | ||||
|        * be processing this FocusIn after we've changed to a | ||||
|        * different workspace; we should therefore update the MRU | ||||
|        * list only if the window is actually on the active | ||||
|        * workspace. | ||||
|        */ | ||||
|       if (window->screen->active_workspace && | ||||
|           meta_window_located_on_workspace (window, | ||||
|                                             window->screen->active_workspace)) | ||||
|         { | ||||
|           GList* link; | ||||
|           link = g_list_find (window->screen->active_workspace->mru_list, | ||||
|                               window); | ||||
|           g_assert (link); | ||||
|  | ||||
|           window->screen->active_workspace->mru_list = | ||||
|             g_list_remove_link (window->screen->active_workspace->mru_list, | ||||
|                                 link); | ||||
|           g_list_free (link); | ||||
|  | ||||
|           window->screen->active_workspace->mru_list = | ||||
|             g_list_prepend (window->screen->active_workspace->mru_list, | ||||
|                             window); | ||||
|         } | ||||
|  | ||||
|       if (window->frame) | ||||
|         meta_frame_queue_draw (window->frame); | ||||
|  | ||||
|       meta_error_trap_push (window->display); | ||||
|       XInstallColormap (window->display->xdisplay, | ||||
|                         window->colormap); | ||||
|       meta_error_trap_pop (window->display); | ||||
|  | ||||
|       /* move into FOCUSED_WINDOW layer */ | ||||
|       meta_window_update_layer (window); | ||||
|  | ||||
|       /* Ungrab click to focus button since the sync grab can interfere | ||||
|        * with some things you might do inside the focused window, by | ||||
|        * causing the client to get funky enter/leave events. | ||||
|        * | ||||
|        * The reason we usually have a passive grab on the window is | ||||
|        * so that we can intercept clicks and raise the window in | ||||
|        * response. For click-to-focus we don't need that since the | ||||
|        * focused window is already raised. When raise_on_click is | ||||
|        * FALSE we also don't need that since we don't do anything | ||||
|        * when the window is clicked. | ||||
|        * | ||||
|        * There is dicussion in bugs 102209, 115072, and 461577 | ||||
|        */ | ||||
|       if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || | ||||
|           !meta_prefs_get_raise_on_click()) | ||||
|         meta_display_ungrab_focus_window_button (window->display, window); | ||||
|  | ||||
|       g_signal_emit (window, window_signals[FOCUS], 0); | ||||
|  | ||||
|       if (!window->attached_focus_window) | ||||
|         meta_window_appears_focused_changed (window); | ||||
|  | ||||
|       meta_window_propagate_focus_appearance (window, TRUE); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       window->has_focus = FALSE; | ||||
|       if (window->override_redirect) | ||||
|         return; | ||||
|  | ||||
|       meta_window_propagate_focus_appearance (window, FALSE); | ||||
|  | ||||
|       window->display->focus_window = NULL; | ||||
|       g_object_notify (G_OBJECT (window->display), "focus-window"); | ||||
|       window->has_focus = FALSE; | ||||
|  | ||||
|       if (!window->attached_focus_window) | ||||
|         meta_window_appears_focused_changed (window); | ||||
|  | ||||
| @@ -7205,176 +7284,6 @@ meta_window_lost_focus (MetaWindow *window) | ||||
|     } | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_notify_focus (MetaWindow   *window, | ||||
|                           XIEnterEvent *event) | ||||
| { | ||||
|   /* note the event can be on either the window or the frame, | ||||
|    * we focus the frame for shaded windows | ||||
|    */ | ||||
|  | ||||
|   /* The event can be FocusIn, FocusOut, or UnmapNotify. | ||||
|    * On UnmapNotify we have to pretend it's focus out, | ||||
|    * because we won't get a focus out if it occurs, apparently. | ||||
|    */ | ||||
|  | ||||
|   /* We ignore grabs, though this is questionable. | ||||
|    * It may be better to increase the intelligence of | ||||
|    * the focus window tracking. | ||||
|    * | ||||
|    * The problem is that keybindings for windows are done with | ||||
|    * XGrabKey, which means focus_window disappears and the front of | ||||
|    * the MRU list gets confused from what the user expects once a | ||||
|    * keybinding is used. | ||||
|    */ | ||||
|   meta_topic (META_DEBUG_FOCUS, | ||||
|               "Focus %s event received on %s 0x%lx (%s) " | ||||
|               "mode %s detail %s\n", | ||||
|               event->evtype == XI_FocusIn ? "in" : | ||||
|               event->evtype == XI_FocusOut ? "out" : | ||||
|               "???", | ||||
|               window->desc, event->event, | ||||
|               event->event == window->xwindow ? | ||||
|               "client window" : | ||||
|               (window->frame && event->event == window->frame->xwindow) ? | ||||
|               "frame window" : | ||||
|               "unknown window", | ||||
|               meta_event_mode_to_string (event->mode), | ||||
|               meta_event_detail_to_string (event->detail)); | ||||
|  | ||||
|   /* FIXME our pointer tracking is broken; see how | ||||
|    * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c | ||||
|    * handle it for the correct way.  In brief you need to track | ||||
|    * pointer focus and regular focus, and handle EnterNotify in | ||||
|    * PointerRoot mode with no window manager.  However as noted above, | ||||
|    * accurate focus tracking will break things because we want to keep | ||||
|    * windows "focused" when using keybindings on them, and also we | ||||
|    * sometimes "focus" a window by focusing its frame or | ||||
|    * no_focus_window; so this all needs rethinking massively. | ||||
|    * | ||||
|    * My suggestion is to change it so that we clearly separate | ||||
|    * actual keyboard focus tracking using the xterm algorithm, | ||||
|    * and mutter's "pretend" focus window, and go through all | ||||
|    * the code and decide which one should be used in each place; | ||||
|    * a hard bit is deciding on a policy for that. | ||||
|    * | ||||
|    * http://bugzilla.gnome.org/show_bug.cgi?id=90382 | ||||
|    */ | ||||
|  | ||||
|   if ((event->evtype == XI_FocusIn || | ||||
|        event->evtype == XI_FocusOut) && | ||||
|       (event->mode == NotifyGrab || | ||||
|        event->mode == NotifyUngrab || | ||||
|        /* From WindowMaker, ignore all funky pointer root events */ | ||||
|        event->detail > NotifyNonlinearVirtual)) | ||||
|     { | ||||
|       meta_topic (META_DEBUG_FOCUS, | ||||
|                   "Ignoring focus event generated by a grab or other weirdness\n"); | ||||
|       return TRUE; | ||||
|     } | ||||
|  | ||||
|   if (event->evtype == XI_FocusIn) | ||||
|     { | ||||
|       if (window->override_redirect) | ||||
|         { | ||||
|           window->display->focus_window = NULL; | ||||
|           g_object_notify (G_OBJECT (window->display), "focus-window"); | ||||
|           return FALSE; | ||||
|         } | ||||
|  | ||||
|       if (window != window->display->focus_window) | ||||
|         { | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "* Focus --> %s\n", window->desc); | ||||
|           window->display->focus_window = window; | ||||
|           window->has_focus = TRUE; | ||||
|  | ||||
|           /* Move to the front of the focusing workspace's MRU list. | ||||
|            * We should only be "removing" it from the MRU list if it's | ||||
|            * not already there.  Note that it's possible that we might | ||||
|            * be processing this FocusIn after we've changed to a | ||||
|            * different workspace; we should therefore update the MRU | ||||
|            * list only if the window is actually on the active | ||||
|            * workspace. | ||||
|            */ | ||||
|           if (window->screen->active_workspace && | ||||
|               meta_window_located_on_workspace (window, | ||||
|                                                 window->screen->active_workspace)) | ||||
|             { | ||||
|               GList* link; | ||||
|               link = g_list_find (window->screen->active_workspace->mru_list, | ||||
|                                   window); | ||||
|               g_assert (link); | ||||
|  | ||||
|               window->screen->active_workspace->mru_list = | ||||
|                 g_list_remove_link (window->screen->active_workspace->mru_list, | ||||
|                                     link); | ||||
|               g_list_free (link); | ||||
|  | ||||
|               window->screen->active_workspace->mru_list = | ||||
|                 g_list_prepend (window->screen->active_workspace->mru_list, | ||||
|                                 window); | ||||
|             } | ||||
|  | ||||
|           if (window->frame) | ||||
|             meta_frame_queue_draw (window->frame); | ||||
|  | ||||
|           meta_error_trap_push (window->display); | ||||
|           XInstallColormap (window->display->xdisplay, | ||||
|                             window->colormap); | ||||
|           meta_error_trap_pop (window->display); | ||||
|  | ||||
|           /* move into FOCUSED_WINDOW layer */ | ||||
|           meta_window_update_layer (window); | ||||
|  | ||||
|           /* Ungrab click to focus button since the sync grab can interfere | ||||
|            * with some things you might do inside the focused window, by | ||||
|            * causing the client to get funky enter/leave events. | ||||
|            * | ||||
|            * The reason we usually have a passive grab on the window is | ||||
|            * so that we can intercept clicks and raise the window in | ||||
|            * response. For click-to-focus we don't need that since the | ||||
|            * focused window is already raised. When raise_on_click is | ||||
|            * FALSE we also don't need that since we don't do anything | ||||
|            * when the window is clicked. | ||||
|            * | ||||
|            * There is dicussion in bugs 102209, 115072, and 461577 | ||||
|            */ | ||||
|           if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || | ||||
|               !meta_prefs_get_raise_on_click()) | ||||
|             meta_display_ungrab_focus_window_button (window->display, window); | ||||
|  | ||||
|           g_signal_emit (window, window_signals[FOCUS], 0); | ||||
|           g_object_notify (G_OBJECT (window->display), "focus-window"); | ||||
|  | ||||
|           if (!window->attached_focus_window) | ||||
|             meta_window_appears_focused_changed (window); | ||||
|  | ||||
|           meta_window_propagate_focus_appearance (window, TRUE); | ||||
|         } | ||||
|     } | ||||
|   else if (event->evtype == XI_FocusOut) | ||||
|     { | ||||
|       if (event->detail == NotifyInferior) | ||||
|         { | ||||
|           /* This event means the client moved focus to a subwindow */ | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "Ignoring focus out on %s with NotifyInferior\n", | ||||
|                       window->desc); | ||||
|           return TRUE; | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           meta_window_lost_focus (window); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   /* Now set _NET_ACTIVE_WINDOW hint */ | ||||
|   meta_display_update_active_window_hint (window->display); | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| process_property_notify (MetaWindow     *window, | ||||
|                          XPropertyEvent *event) | ||||
| @@ -11216,3 +11125,9 @@ meta_window_compute_tile_match (MetaWindow *window) | ||||
|       window->tile_match = match; | ||||
|     } | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_can_close (MetaWindow *window) | ||||
| { | ||||
|   return window->has_close_func; | ||||
| } | ||||
|   | ||||
| @@ -70,6 +70,7 @@ item(_GNOME_WM_KEYBINDINGS) | ||||
| item(_GNOME_PANEL_ACTION) | ||||
| item(_GNOME_PANEL_ACTION_MAIN_MENU) | ||||
| item(_GNOME_PANEL_ACTION_RUN_DIALOG) | ||||
| item(_MUTTER_FOCUS_SET) | ||||
| item(_MUTTER_SENTINEL) | ||||
| item(_MUTTER_VERSION) | ||||
| item(WM_CLIENT_MACHINE) | ||||
|   | ||||
| @@ -35,7 +35,6 @@ | ||||
|  | ||||
| /* Public compositor API */ | ||||
| ClutterActor *meta_get_stage_for_screen         (MetaScreen *screen); | ||||
| ClutterActor *meta_get_overlay_group_for_screen (MetaScreen *screen); | ||||
| Window        meta_get_overlay_window           (MetaScreen *screen); | ||||
| GList        *meta_get_window_actors            (MetaScreen *screen); | ||||
| ClutterActor *meta_get_window_group_for_screen  (MetaScreen *screen); | ||||
| @@ -47,5 +46,8 @@ void        meta_enable_unredirect_for_screen   (MetaScreen *screen); | ||||
| void meta_set_stage_input_region     (MetaScreen    *screen, | ||||
|                                       XserverRegion  region); | ||||
| void meta_empty_stage_input_region   (MetaScreen    *screen); | ||||
| void meta_focus_stage_window         (MetaScreen    *screen, | ||||
|                                       guint32        timestamp); | ||||
| gboolean meta_stage_is_focused       (MetaScreen    *screen); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -165,6 +165,10 @@ void meta_display_set_input_focus_window   (MetaDisplay *display, | ||||
|                                             gboolean     focus_frame, | ||||
|                                             guint32      timestamp); | ||||
|  | ||||
| void meta_display_request_take_focus       (MetaDisplay *display, | ||||
|                                             MetaWindow  *window, | ||||
|                                             guint32      timestamp); | ||||
|  | ||||
| /* meta_display_focus_the_no_focus_window is called when the | ||||
|  * designated no_focus_window should be focused, but is otherwise the | ||||
|  * same as meta_display_set_input_focus_window | ||||
| @@ -187,4 +191,11 @@ void meta_display_unmanage_screen (MetaDisplay *display, | ||||
|  | ||||
| void meta_display_clear_mouse_mode (MetaDisplay *display); | ||||
|  | ||||
| void meta_display_freeze_keyboard (MetaDisplay *display, | ||||
|                                    Window       window, | ||||
|                                    guint32      timestamp); | ||||
| void meta_display_ungrab_keyboard (MetaDisplay *display, | ||||
|                                    guint32      timestamp); | ||||
| void meta_display_unfreeze_keyboard (MetaDisplay *display, | ||||
|                                      guint32      timestamp); | ||||
| #endif | ||||
|   | ||||
| @@ -353,6 +353,7 @@ typedef enum _MetaKeyBindingAction | ||||
|   META_KEYBINDING_ACTION_MOVE_TO_SIDE_W, | ||||
|   META_KEYBINDING_ACTION_MOVE_TO_CENTER, | ||||
|   META_KEYBINDING_ACTION_OVERLAY_KEY, | ||||
|   META_KEYBINDING_ACTION_ISO_NEXT_GROUP, | ||||
|  | ||||
|   META_KEYBINDING_ACTION_LAST | ||||
| } MetaKeyBindingAction; | ||||
| @@ -442,6 +443,7 @@ void meta_prefs_get_window_binding (const char          *name, | ||||
|                                     MetaVirtualModifier *modifiers); | ||||
|  | ||||
| void meta_prefs_get_overlay_binding (MetaKeyCombo *combo); | ||||
| const char *meta_prefs_get_iso_next_group_option (void); | ||||
|  | ||||
| gboolean           meta_prefs_get_visual_bell      (void); | ||||
| gboolean           meta_prefs_bell_is_audible      (void); | ||||
|   | ||||
| @@ -237,4 +237,6 @@ void meta_window_begin_grab_op (MetaWindow *window, | ||||
|                                 gboolean    frame_action, | ||||
|                                 guint32     timestamp); | ||||
|  | ||||
| gboolean meta_window_can_close (MetaWindow *window); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										112
									
								
								src/ui/frames.c
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/ui/frames.c
									
									
									
									
									
								
							| @@ -739,22 +739,6 @@ meta_ui_frame_get_corner_radiuses (MetaFrames  *frames, | ||||
|     *bottom_right = fgeom.bottom_right_corner_rounded_radius + sqrt(fgeom.bottom_right_corner_rounded_radius); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_frames_get_corner_radiuses (MetaFrames *frames, | ||||
|                                  Window      xwindow, | ||||
|                                  float      *top_left, | ||||
|                                  float      *top_right, | ||||
|                                  float      *bottom_left, | ||||
|                                  float      *bottom_right) | ||||
| { | ||||
|   MetaUIFrame *frame; | ||||
|  | ||||
|   frame = meta_frames_lookup_window (frames, xwindow); | ||||
|  | ||||
|   meta_ui_frame_get_corner_radiuses (frames, frame, top_left, top_right, | ||||
|                                      bottom_left, bottom_right); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_frames_reset_bg (MetaFrames *frames, | ||||
|                       Window  xwindow) | ||||
| @@ -1851,6 +1835,102 @@ clip_region_to_visible_frame_border (cairo_region_t *region, | ||||
|   cairo_region_destroy (frame_border); | ||||
| } | ||||
|  | ||||
| #define TAU (2*M_PI) | ||||
|  | ||||
| /* | ||||
|  * Draw the opaque and semi-opaque pixels of this frame into a mask. | ||||
|  * | ||||
|  * (0,0) in Cairo coordinates is assumed to be the top left corner of the | ||||
|  * invisible border. | ||||
|  * | ||||
|  * The parts of @cr's surface in the clip region are assumed to be | ||||
|  * initialized to fully-transparent, and the clip region is assumed to | ||||
|  * contain the invisible border and the visible parts of the frame, but | ||||
|  * not the client area. | ||||
|  * | ||||
|  * This function uses @cr to draw pixels of arbitrary color (it will | ||||
|  * typically be drawing in a %CAIRO_FORMAT_A8 surface, so the color is | ||||
|  * discarded anyway) with appropriate alpha values to reproduce this | ||||
|  * frame's alpha channel, as a mask to be applied to an opaque pixmap. | ||||
|  * | ||||
|  * @frame: This frame | ||||
|  * @xwindow: The X window for the frame, which has the client window as a child | ||||
|  * @width: The width of the framed window including any invisible borders | ||||
|  * @height: The height of the framed window including any invisible borders | ||||
|  * @cr: Used to draw the resulting mask | ||||
|  */ | ||||
| void | ||||
| meta_frames_get_mask (MetaFrames          *frames, | ||||
|                       Window               xwindow, | ||||
|                       guint                width, | ||||
|                       guint                height, | ||||
|                       cairo_t             *cr) | ||||
| { | ||||
|   MetaUIFrame *frame = meta_frames_lookup_window (frames, xwindow); | ||||
|   float top_left, top_right, bottom_left, bottom_right; | ||||
|   int x, y; | ||||
|   MetaFrameBorders borders; | ||||
|  | ||||
|   if (frame == NULL) | ||||
|     meta_bug ("No such frame 0x%lx\n", xwindow); | ||||
|  | ||||
|   cairo_save (cr); | ||||
|  | ||||
|   meta_ui_frame_get_borders (frames, frame, &borders); | ||||
|   meta_ui_frame_get_corner_radiuses (frames, frame, | ||||
|                                      &top_left, &top_right, | ||||
|                                      &bottom_left, &bottom_right); | ||||
|  | ||||
|   /* top left */ | ||||
|   x = borders.invisible.left; | ||||
|   y = borders.invisible.top; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x + top_left, | ||||
|              y + top_left, | ||||
|              top_left, | ||||
|              2 * TAU / 4, | ||||
|              3 * TAU / 4); | ||||
|  | ||||
|   /* top right */ | ||||
|   x = width - borders.invisible.right - top_right; | ||||
|   y = borders.invisible.top; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x, | ||||
|              y + top_right, | ||||
|              top_right, | ||||
|              3 * TAU / 4, | ||||
|              4 * TAU / 4); | ||||
|  | ||||
|   /* bottom right */ | ||||
|   x = width - borders.invisible.right - bottom_right; | ||||
|   y = height - borders.invisible.bottom - bottom_right; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x, | ||||
|              y, | ||||
|              bottom_right, | ||||
|              0 * TAU / 4, | ||||
|              1 * TAU / 4); | ||||
|  | ||||
|   /* bottom left */ | ||||
|   x = borders.invisible.left; | ||||
|   y = height - borders.invisible.bottom - bottom_left; | ||||
|  | ||||
|   cairo_arc (cr, | ||||
|              x + bottom_left, | ||||
|              y, | ||||
|              bottom_left, | ||||
|              1 * TAU / 4, | ||||
|              2 * TAU / 4); | ||||
|  | ||||
|   cairo_set_source_rgba (cr, 1, 1, 1, 1); | ||||
|   cairo_fill (cr); | ||||
|  | ||||
|   cairo_restore (cr); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| meta_frames_draw (GtkWidget *widget, | ||||
|                   cairo_t   *cr) | ||||
|   | ||||
| @@ -140,12 +140,11 @@ cairo_region_t *meta_frames_get_frame_bounds (MetaFrames *frames, | ||||
|                                               int         window_width, | ||||
|                                               int         window_height); | ||||
|  | ||||
| void meta_frames_get_corner_radiuses (MetaFrames *frames, | ||||
|                                       Window      xwindow, | ||||
|                                       float      *top_left, | ||||
|                                       float      *top_right, | ||||
|                                       float      *bottom_left, | ||||
|                                       float      *bottom_right); | ||||
| void meta_frames_get_mask (MetaFrames *frames, | ||||
|                            Window      xwindow, | ||||
|                            guint       width, | ||||
|                            guint       height, | ||||
|                            cairo_t    *cr); | ||||
|  | ||||
| void meta_frames_move_resize_frame (MetaFrames *frames, | ||||
| 				    Window      xwindow, | ||||
|   | ||||
| @@ -857,10 +857,7 @@ meta_convert_meta_to_wnck (MetaWindow *window, MetaScreen *screen) | ||||
|   WnckWindowDisplayInfo wnck_window; | ||||
|   wnck_window.icon = window->icon; | ||||
|   wnck_window.mini_icon = window->mini_icon; | ||||
|    | ||||
|   wnck_window.is_active = FALSE; | ||||
|   if (window == window->display->expected_focus_window) | ||||
|     wnck_window.is_active = TRUE; | ||||
|   wnck_window.is_active = window->has_focus; | ||||
|  | ||||
|   if (window->frame) | ||||
|     { | ||||
|   | ||||
| @@ -1880,7 +1880,7 @@ debug_print_tokens (PosToken *tokens, | ||||
| /** | ||||
|  * pos_tokenize: | ||||
|  * @expr: The expression | ||||
|  * @tokens_p: (out) The resulting tokens | ||||
|  * @tokens_p: (out): The resulting tokens | ||||
|  * @n_tokens_p: (out): The number of resulting tokens | ||||
|  * @err: (out):  set to the problem if there was a problem | ||||
|   | ||||
|   | ||||
							
								
								
									
										23
									
								
								src/ui/ui.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/ui/ui.c
									
									
									
									
									
								
							| @@ -324,6 +324,16 @@ meta_ui_free (MetaUI *ui) | ||||
|   g_free (ui); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_ui_get_frame_mask (MetaUI  *ui, | ||||
|                         Window   frame_xwindow, | ||||
|                         guint    width, | ||||
|                         guint    height, | ||||
|                         cairo_t *cr) | ||||
| { | ||||
|   meta_frames_get_mask (ui->frames, frame_xwindow, width, height, cr); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_ui_get_frame_borders (MetaUI *ui, | ||||
|                            Window frame_xwindow, | ||||
| @@ -333,19 +343,6 @@ meta_ui_get_frame_borders (MetaUI *ui, | ||||
|                            borders); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_ui_get_corner_radiuses (MetaUI *ui, | ||||
|                              Window  xwindow, | ||||
|                              float  *top_left, | ||||
|                              float  *top_right, | ||||
|                              float  *bottom_left, | ||||
|                              float  *bottom_right) | ||||
| { | ||||
|   meta_frames_get_corner_radiuses (ui->frames, xwindow, | ||||
|                                    top_left, top_right, | ||||
|                                    bottom_left, bottom_right); | ||||
| } | ||||
|  | ||||
| Window | ||||
| meta_ui_create_frame_window (MetaUI *ui, | ||||
|                              Display *xdisplay, | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/ui/ui.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/ui/ui.h
									
									
									
									
									
								
							| @@ -66,6 +66,13 @@ void meta_ui_theme_get_frame_borders (MetaUI *ui, | ||||
| void meta_ui_get_frame_borders (MetaUI *ui, | ||||
|                                 Window frame_xwindow, | ||||
|                                 MetaFrameBorders *borders); | ||||
|  | ||||
| void meta_ui_get_frame_mask (MetaUI *ui, | ||||
|                              Window frame_xwindow, | ||||
|                              guint width, | ||||
|                              guint height, | ||||
|                              cairo_t *cr); | ||||
|  | ||||
| Window meta_ui_create_frame_window (MetaUI *ui, | ||||
|                                     Display *xdisplay, | ||||
|                                     Visual *xvisual, | ||||
| @@ -102,13 +109,6 @@ cairo_region_t *meta_ui_get_frame_bounds (MetaUI  *ui, | ||||
|                                           int      window_width, | ||||
|                                           int      window_height); | ||||
|  | ||||
| void meta_ui_get_corner_radiuses (MetaUI *ui, | ||||
|                                   Window  xwindow, | ||||
|                                   float  *top_left, | ||||
|                                   float  *top_right, | ||||
|                                   float  *bottom_left, | ||||
|                                   float  *bottom_right); | ||||
|  | ||||
| void meta_ui_queue_frame_draw (MetaUI *ui, | ||||
|                                Window xwindow); | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,10 @@ test_size_hints_SOURCES=			\ | ||||
| test_attached_SOURCES=				\ | ||||
| 	test-attached.c | ||||
|  | ||||
| noinst_PROGRAMS=wm-tester test-gravity test-resizing focus-window test-size-hints test-attached | ||||
| test_focus_SOURCES=				\ | ||||
| 	test-focus.c | ||||
|  | ||||
| noinst_PROGRAMS=wm-tester test-gravity test-resizing focus-window test-size-hints test-attached test-focus | ||||
|  | ||||
| wm_tester_LDADD= @MUTTER_LIBS@ | ||||
| test_gravity_LDADD= @MUTTER_LIBS@ | ||||
| @@ -27,3 +30,4 @@ test_resizing_LDADD= @MUTTER_LIBS@ | ||||
| test_size_hints_LDADD= @MUTTER_LIBS@ | ||||
| focus_window_LDADD= @MUTTER_LIBS@ | ||||
| test_attached_LDADD= @MUTTER_LIBS@ | ||||
| test_focus_LDADD= @MUTTER_LIBS@ | ||||
|   | ||||
							
								
								
									
										362
									
								
								src/wm-tester/test-focus.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								src/wm-tester/test-focus.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,362 @@ | ||||
| #include <gtk/gtk.h> | ||||
| #include <gdk/gdkx.h> | ||||
| #include <X11/Xatom.h> | ||||
|  | ||||
| GtkWidget *main_window; | ||||
| GtkWidget *noinput_window, *passive_window, *local_window; | ||||
| GtkWidget *global_window, *lame_window, *grabby_window, *dying_window; | ||||
|  | ||||
| static void | ||||
| clear_on_destroy (GtkWidget *widget, gpointer user_data) | ||||
| { | ||||
|   GtkWidget **widget_pointer = user_data; | ||||
|  | ||||
|   *widget_pointer = NULL; | ||||
| } | ||||
|  | ||||
| static void | ||||
| disable_take_focus (GtkWidget *window) | ||||
| { | ||||
|   GdkDisplay *display; | ||||
|   GdkWindow *gdkwindow; | ||||
|   Atom *protocols, wm_take_focus; | ||||
|   int n_protocols, i; | ||||
|  | ||||
|   gtk_widget_realize (window); | ||||
|   gdkwindow = gtk_widget_get_window (window); | ||||
|   display = gdk_window_get_display (gdkwindow); | ||||
|  | ||||
|   wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"); | ||||
|   XGetWMProtocols (GDK_DISPLAY_XDISPLAY (display), | ||||
|                    GDK_WINDOW_XID (gdkwindow), | ||||
|                    &protocols, &n_protocols); | ||||
|   for (i = 0; i < n_protocols; i++) | ||||
|     { | ||||
|       if (protocols[i] == wm_take_focus) | ||||
|         { | ||||
|           protocols[i] = protocols[n_protocols - 1]; | ||||
|           n_protocols--; | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
|   XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display), | ||||
|                    GDK_WINDOW_XID (gdkwindow), | ||||
|                    protocols, n_protocols); | ||||
|   XFree (protocols); | ||||
| } | ||||
|  | ||||
| static void | ||||
| clear_input_hint (GtkWidget *window) | ||||
| { | ||||
|   /* This needs to be called after gtk_widget_show, otherwise | ||||
|    * GTK+ will overwrite it. */ | ||||
|   GdkWindow *gdkwindow = gtk_widget_get_window (window); | ||||
|   XWMHints *wm_hints; | ||||
|  | ||||
|   wm_hints = XGetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)), | ||||
|                           GDK_WINDOW_XID (gdkwindow)); | ||||
|  | ||||
|   wm_hints->flags |= InputHint; | ||||
|   wm_hints->input = False; | ||||
|  | ||||
|   XSetWMHints (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)), | ||||
|                GDK_WINDOW_XID (gdkwindow), | ||||
|                wm_hints); | ||||
|  | ||||
|   XFree (wm_hints); | ||||
| } | ||||
|  | ||||
| static void | ||||
| active_notify (GObject    *obj, | ||||
|                GParamSpec *pspec, | ||||
|                gpointer    user_data) | ||||
| { | ||||
|   GtkLabel *label = user_data; | ||||
|  | ||||
|   if (gtk_window_is_active (GTK_WINDOW (obj))) | ||||
|     gtk_label_set_text (label, "Focused"); | ||||
|   else | ||||
|     gtk_label_set_text (label, "Not focused"); | ||||
| } | ||||
|  | ||||
| static void | ||||
| make_focused_label (GtkWidget *toplevel, | ||||
|                     GtkWidget *parent) | ||||
| { | ||||
|   GtkWidget *label; | ||||
|  | ||||
|   label = gtk_label_new (""); | ||||
|   gtk_widget_show (label); | ||||
|  | ||||
|   gtk_container_add (GTK_CONTAINER (parent), label); | ||||
|  | ||||
|   g_signal_connect (toplevel, "notify::is-active", | ||||
|                     G_CALLBACK (active_notify), label); | ||||
|   active_notify (G_OBJECT (toplevel), NULL, label); | ||||
| } | ||||
|  | ||||
| static void | ||||
| setup_test_dialog (GtkWidget *toplevel) | ||||
| { | ||||
|   make_focused_label (toplevel, toplevel); | ||||
|   gtk_widget_set_size_request (toplevel, 200, 200); | ||||
| } | ||||
|  | ||||
| static void | ||||
| noinput_clicked (GtkButton *button, gpointer user_data) | ||||
| { | ||||
|   if (noinput_window) | ||||
|     gtk_window_present_with_time (GTK_WINDOW (noinput_window), gtk_get_current_event_time ()); | ||||
|   else | ||||
|     { | ||||
|       noinput_window = g_object_new (GTK_TYPE_WINDOW, | ||||
|                                      "type", GTK_WINDOW_TOPLEVEL, | ||||
|                                      "title", "No Input", | ||||
|                                      "accept-focus", FALSE, | ||||
|                                      NULL); | ||||
|       setup_test_dialog (noinput_window); | ||||
|       g_signal_connect (noinput_window, "destroy", | ||||
|                         G_CALLBACK (clear_on_destroy), &noinput_window); | ||||
|       disable_take_focus (noinput_window); | ||||
|       gtk_widget_show (noinput_window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| passive_clicked (GtkButton *button, gpointer user_data) | ||||
| { | ||||
|   if (passive_window) | ||||
|     gtk_window_present_with_time (GTK_WINDOW (passive_window), gtk_get_current_event_time ()); | ||||
|   else | ||||
|     { | ||||
|       passive_window = g_object_new (GTK_TYPE_WINDOW, | ||||
|                                      "type", GTK_WINDOW_TOPLEVEL, | ||||
|                                      "title", "Passive Input", | ||||
|                                      "accept-focus", TRUE, | ||||
|                                      NULL); | ||||
|       setup_test_dialog (passive_window); | ||||
|       g_signal_connect (passive_window, "destroy", | ||||
|                         G_CALLBACK (clear_on_destroy), &passive_window); | ||||
|       disable_take_focus (passive_window); | ||||
|       gtk_widget_show (passive_window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| local_clicked (GtkButton *button, gpointer user_data) | ||||
| { | ||||
|   if (local_window) | ||||
|     gtk_window_present_with_time (GTK_WINDOW (local_window), gtk_get_current_event_time ()); | ||||
|   else | ||||
|     { | ||||
|       local_window = g_object_new (GTK_TYPE_WINDOW, | ||||
|                                    "type", GTK_WINDOW_TOPLEVEL, | ||||
|                                    "title", "Locally Active Input", | ||||
|                                    "accept-focus", TRUE, | ||||
|                                    NULL); | ||||
|       setup_test_dialog (local_window); | ||||
|       g_signal_connect (local_window, "destroy", | ||||
|                         G_CALLBACK (clear_on_destroy), &local_window); | ||||
|       gtk_widget_show (local_window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| global_clicked (GtkButton *button, gpointer user_data) | ||||
| { | ||||
|   if (global_window) | ||||
|     gtk_window_present_with_time (GTK_WINDOW (global_window), gtk_get_current_event_time ()); | ||||
|   else | ||||
|     { | ||||
|       /* gtk will only process WM_TAKE_FOCUS messages if accept-focus | ||||
|        * is TRUE. So we set that property and then manually clear the | ||||
|        * Input WMHint. | ||||
|        */ | ||||
|       global_window = g_object_new (GTK_TYPE_WINDOW, | ||||
|                                     "type", GTK_WINDOW_TOPLEVEL, | ||||
|                                     "title", "Globally Active Input", | ||||
|                                     "accept-focus", TRUE, | ||||
|                                     NULL); | ||||
|       setup_test_dialog (global_window); | ||||
|       g_signal_connect (global_window, "destroy", | ||||
|                         G_CALLBACK (clear_on_destroy), &global_window); | ||||
|       gtk_widget_show (global_window); | ||||
|       clear_input_hint (global_window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| lame_clicked (GtkButton *button, gpointer user_data) | ||||
| { | ||||
|   if (lame_window) | ||||
|     gtk_window_present_with_time (GTK_WINDOW (lame_window), gtk_get_current_event_time ()); | ||||
|   else | ||||
|     { | ||||
|       lame_window = g_object_new (GTK_TYPE_WINDOW, | ||||
|                                   "type", GTK_WINDOW_TOPLEVEL, | ||||
|                                   "title", "Lame Globally Active Input", | ||||
|                                   "accept-focus", FALSE, | ||||
|                                   NULL); | ||||
|       setup_test_dialog (lame_window); | ||||
|       g_signal_connect (lame_window, "destroy", | ||||
|                         G_CALLBACK (clear_on_destroy), &lame_window); | ||||
|       gtk_widget_show (lame_window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| grabby_active_changed (GObject *object, GParamSpec *param, gpointer user_data) | ||||
| { | ||||
|   if (gtk_window_is_active (GTK_WINDOW (grabby_window))) | ||||
|     { | ||||
|       GdkWindow *gdkwindow = gtk_widget_get_window (grabby_window); | ||||
|       guint32 now = gdk_x11_get_server_time (gdkwindow); | ||||
|  | ||||
|       gtk_window_present_with_time (GTK_WINDOW (main_window), now - 1); | ||||
|       XSetInputFocus (GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdkwindow)), | ||||
|                       GDK_WINDOW_XID (gdkwindow), | ||||
|                       RevertToParent, | ||||
|                       now); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| grabby_clicked (GtkButton *button, gpointer user_data) | ||||
| { | ||||
|   if (grabby_window) | ||||
|     gtk_window_present_with_time (GTK_WINDOW (grabby_window), gtk_get_current_event_time ()); | ||||
|   else | ||||
|     { | ||||
|       grabby_window = g_object_new (GTK_TYPE_WINDOW, | ||||
|                                     "type", GTK_WINDOW_TOPLEVEL, | ||||
|                                     "title", "Focus-grabbing Window", | ||||
|                                     "accept-focus", TRUE, | ||||
|                                     /* Because mutter maps windows | ||||
|                                      * asynchronously, our trick won't | ||||
|                                      * work if we try to do it when the | ||||
|                                      * window is first mapped. | ||||
|                                      */ | ||||
|                                     "focus-on-map", FALSE, | ||||
|                                     NULL); | ||||
|       setup_test_dialog (grabby_window); | ||||
|       g_signal_connect (grabby_window, "destroy", | ||||
|                         G_CALLBACK (clear_on_destroy), &grabby_window); | ||||
|       g_signal_connect (grabby_window, "notify::is-active", | ||||
|                         G_CALLBACK (grabby_active_changed), NULL); | ||||
|       gtk_widget_show (grabby_window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| dying_clicked (GtkButton *button, gpointer user_data) | ||||
| { | ||||
|   if (dying_window) | ||||
|     { | ||||
|       gtk_window_present_with_time (GTK_WINDOW (dying_window), gtk_get_current_event_time ()); | ||||
|       gtk_widget_destroy (dying_window); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       GtkWidget *label; | ||||
|  | ||||
|       dying_window = g_object_new (GTK_TYPE_WINDOW, | ||||
|                                    "type", GTK_WINDOW_TOPLEVEL, | ||||
|                                    "title", "Dying Window", | ||||
|                                    "accept-focus", TRUE, | ||||
|                                    /* As with grabby window */ | ||||
|                                    "focus-on-map", FALSE, | ||||
|                                    NULL); | ||||
|       setup_test_dialog (dying_window); | ||||
|       g_signal_connect (dying_window, "destroy", | ||||
|                         G_CALLBACK (clear_on_destroy), &dying_window); | ||||
|  | ||||
|       label = gtk_label_new ("Click button again to test"); | ||||
|       gtk_container_add (GTK_CONTAINER (dying_window), label); | ||||
|       gtk_widget_set_size_request (dying_window, 200, 200); | ||||
|  | ||||
|       gtk_widget_show_all (dying_window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| main_window_destroyed (GtkWidget *widget, gpointer user_data) | ||||
| { | ||||
|   gtk_main_quit (); | ||||
| } | ||||
|  | ||||
| int | ||||
| main (int argc, char **argv) | ||||
| { | ||||
|   GtkWidget *vbox, *button; | ||||
|  | ||||
|   gtk_init (&argc, &argv); | ||||
|  | ||||
|   main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); | ||||
|   gtk_window_set_title (GTK_WINDOW (main_window), "Focus Tester"); | ||||
|  | ||||
|   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8); | ||||
|   gtk_box_set_homogeneous (GTK_BOX (vbox), 8); | ||||
|   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); | ||||
|   gtk_container_add (GTK_CONTAINER (main_window), vbox); | ||||
|  | ||||
|   make_focused_label (main_window, vbox); | ||||
|  | ||||
|   /* ICCCM "No Input" mode; Input hint False, WM_TAKE_FOCUS absent */ | ||||
|   button = gtk_button_new_with_label ("No Input Window"); | ||||
|   g_signal_connect (button, "clicked", G_CALLBACK (noinput_clicked), NULL); | ||||
|   gtk_container_add (GTK_CONTAINER (vbox), button); | ||||
|  | ||||
|   /* ICCCM "Passive" mode; Input hint True, WM_TAKE_FOCUS absent */ | ||||
|   button = gtk_button_new_with_label ("Passive Input Window"); | ||||
|   g_signal_connect (button, "clicked", G_CALLBACK (passive_clicked), NULL); | ||||
|   gtk_container_add (GTK_CONTAINER (vbox), button); | ||||
|  | ||||
|   /* ICCCM "Locally Active" mode; Input hint True, WM_TAKE_FOCUS present. | ||||
|    * This is the behavior of GtkWindows with accept_focus==TRUE. | ||||
|    */ | ||||
|   button = gtk_button_new_with_label ("Locally Active Window"); | ||||
|   g_signal_connect (button, "clicked", G_CALLBACK (local_clicked), NULL); | ||||
|   gtk_container_add (GTK_CONTAINER (vbox), button); | ||||
|  | ||||
|   /* ICCCM "Globally Active" mode; Input hint False, WM_TAKE_FOCUS present, | ||||
|    * and the window responds to WM_TAKE_FOCUS by calling XSetInputFocus. | ||||
|    */ | ||||
|   button = gtk_button_new_with_label ("Globally Active Window"); | ||||
|   g_signal_connect (button, "clicked", G_CALLBACK (global_clicked), NULL); | ||||
|   gtk_container_add (GTK_CONTAINER (vbox), button); | ||||
|  | ||||
|   /* "Lame" Globally Active mode; like "Globally Active", except that | ||||
|    * the window does not respond to WM_TAKE_FOCUS. This is the | ||||
|    * behavior of GtkWindows with accept_focus==FALSE. | ||||
|    */ | ||||
|   button = gtk_button_new_with_label ("Globally Lame Window"); | ||||
|   g_signal_connect (button, "clicked", G_CALLBACK (lame_clicked), NULL); | ||||
|   gtk_container_add (GTK_CONTAINER (vbox), button); | ||||
|  | ||||
|   /* "Grabby" window; when you activate the window, it asks the wm to | ||||
|    * focus the main window, but then forcibly grabs focus back with | ||||
|    * a newer timestamp, causing the wm to be temporarily confused | ||||
|    * about what window is focused and triggering the "Earlier attempt | ||||
|    * to focus ... failed" codepath. | ||||
|    */ | ||||
|   button = gtk_button_new_with_label ("Grabby Window"); | ||||
|   g_signal_connect (button, "clicked", G_CALLBACK (grabby_clicked), NULL); | ||||
|   gtk_container_add (GTK_CONTAINER (vbox), button); | ||||
|  | ||||
|   /* "Dying" window; we create the window on the first click, then | ||||
|    * activate it and destroy it on the second click, causing mutter to | ||||
|    * do an XSetInputFocus but not receive the corresponding FocusIn. | ||||
|    */ | ||||
|   button = gtk_button_new_with_label ("Dying Window"); | ||||
|   g_signal_connect (button, "clicked", G_CALLBACK (dying_clicked), NULL); | ||||
|   gtk_container_add (GTK_CONTAINER (vbox), button); | ||||
|  | ||||
|   gtk_widget_show_all (main_window); | ||||
|  | ||||
|   g_signal_connect (main_window, "destroy", | ||||
|                     G_CALLBACK (main_window_destroyed), NULL); | ||||
|  | ||||
|   gtk_main (); | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user