Compare commits
	
		
			59 Commits
		
	
	
		
			3.37.1
			...
			gnome-3-32
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | c2e12b3434 | ||
|   | 002299fbef | ||
|   | f0b7cf4d91 | ||
|   | ad4dbefbc0 | ||
|   | 5a486f5b6b | ||
|   | bb5c7b97ba | ||
|   | b7f1588119 | ||
|   | 88e492cac0 | ||
|   | ed03ce53b2 | ||
|   | ccab0f470d | ||
|   | 61691bacc8 | ||
|   | 63f3f5d59f | ||
|   | 80869e0737 | ||
|   | 763092a4b6 | ||
|   | e50cdda4ff | ||
|   | c50df14311 | ||
|   | 194d93efd8 | ||
|   | 947da2c5d5 | ||
|   | 0a8fbbe5cc | ||
|   | 001076339a | ||
|   | 0d570f2dd3 | ||
|   | c5fb1d1975 | ||
|   | 83ce89ef59 | ||
|   | da3195288b | ||
|   | 8b986cd065 | ||
|   | 5efb11acad | ||
|   | 2db94e2e40 | ||
|   | 866d6780c9 | ||
|   | 9009f8d48f | ||
|   | 3d9771b25a | ||
|   | 1be5a57ade | ||
|   | afcea966c0 | ||
|   | 9c079a5261 | ||
|   | d56bd28f64 | ||
|   | 225d18761a | ||
|   | e0922bffea | ||
|   | 8307c0f7ab | ||
|   | cded69da61 | ||
|   | a3a97621be | ||
|   | 8b79c83ad5 | ||
|   | 2ce4a20c8e | ||
|   | fb03e198e5 | ||
|   | 07c9cd498d | ||
|   | e716f9d143 | ||
|   | 13a1624c10 | ||
|   | 30d6e3abe2 | ||
|   | 18e44bb64c | ||
|   | 15803b9558 | ||
|   | 3fdc651179 | ||
|   | 189f71f5d1 | ||
|   | 0a3cddeecf | ||
|   | ee92e4fe13 | ||
|   | 6933ce0976 | ||
|   | f9d6627fe0 | ||
|   | 668c44e66b | ||
|   | 3495a43810 | ||
|   | 9a795d3d0f | ||
|   | abc3fdcc65 | ||
|   | 4705a31049 | 
							
								
								
									
										11
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								NEWS
									
									
									
									
									
								
							| @@ -1,3 +1,14 @@ | ||||
| 3.32.2 | ||||
| ====== | ||||
| * Disable mouse keys with Numlock on [Olivier; #530] | ||||
| * Fix crash when restarting on X11 [Marco; #576] | ||||
| * Fix mapping of touchscreens that don't report dimensions [Carlos; #581] | ||||
| * Fix spurious idle signals that prevent session unblank [Jonas; !543] | ||||
| * Misc. bug fixes and cleanups [Olivier, Marco, Carlos; !552, !557, #586] | ||||
|  | ||||
| Contributors: | ||||
|   Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Marco Trevisan (Treviño) | ||||
|  | ||||
| 3.32.1 | ||||
| ====== | ||||
| * Fix fallback app menu on wayland [Florian; #493] | ||||
|   | ||||
| @@ -1975,6 +1975,7 @@ selection_paint (ClutterText     *self, | ||||
|   else | ||||
|     { | ||||
|       /* Paint selection background first */ | ||||
|       CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline); | ||||
|       PangoLayout *layout = clutter_text_get_layout (self); | ||||
|       CoglPath *selection_path = cogl_path_new (); | ||||
|       CoglColor cogl_color = { 0, }; | ||||
| @@ -1987,11 +1988,19 @@ selection_paint (ClutterText     *self, | ||||
|       else | ||||
|         color = &priv->text_color; | ||||
|  | ||||
|       cogl_color_init_from_4ub (&cogl_color, | ||||
|                                 color->red, | ||||
|                                 color->green, | ||||
|                                 color->blue, | ||||
|                                 paint_opacity * color->alpha / 255); | ||||
|       cogl_color_premultiply (&cogl_color); | ||||
|       cogl_pipeline_set_color (color_pipeline, &cogl_color); | ||||
|  | ||||
|       clutter_text_foreach_selection_rectangle_prescaled (self, | ||||
|                                                           add_selection_rectangle_to_path, | ||||
|                                                           selection_path); | ||||
|  | ||||
|       cogl_path_fill (selection_path); | ||||
|       cogl_framebuffer_fill_path (fb, color_pipeline, selection_path); | ||||
|  | ||||
|       /* Paint selected text */ | ||||
|       cogl_framebuffer_push_path_clip (fb, selection_path); | ||||
|   | ||||
| @@ -855,6 +855,14 @@ emulate_pointer_motion (ClutterInputDeviceEvdev *device, | ||||
|   clutter_virtual_input_device_notify_relative_motion (device->mousekeys_virtual_device, | ||||
|                                                        time_us, dx_motion, dy_motion); | ||||
| } | ||||
| static gboolean | ||||
| is_numlock_active (ClutterInputDeviceEvdev *device) | ||||
| { | ||||
|   ClutterSeatEvdev *seat = device->seat; | ||||
|   return xkb_state_mod_name_is_active (seat->xkb, | ||||
|                                        "Mod2", | ||||
|                                        XKB_STATE_MODS_LOCKED); | ||||
| } | ||||
|  | ||||
| static void | ||||
| enable_mousekeys (ClutterInputDeviceEvdev *device) | ||||
| @@ -1013,6 +1021,10 @@ handle_mousekeys_press (ClutterEvent            *event, | ||||
|   if (!(event->key.flags & CLUTTER_EVENT_FLAG_SYNTHETIC)) | ||||
|     stop_mousekeys_move (device); | ||||
|  | ||||
|   /* Do not handle mousekeys if NumLock is ON */ | ||||
|   if (is_numlock_active (device)) | ||||
|     return FALSE; | ||||
|  | ||||
|   /* Button selection */ | ||||
|   switch (event->key.keyval) | ||||
|     { | ||||
| @@ -1084,6 +1096,10 @@ static gboolean | ||||
| handle_mousekeys_release (ClutterEvent            *event, | ||||
|                           ClutterInputDeviceEvdev *device) | ||||
| { | ||||
|   /* Do not handle mousekeys if NumLock is ON */ | ||||
|   if (is_numlock_active (device)) | ||||
|     return FALSE; | ||||
|  | ||||
|   switch (event->key.keyval) | ||||
|     { | ||||
|     case XKB_KEY_KP_0: | ||||
|   | ||||
| @@ -54,6 +54,7 @@ | ||||
| #include "clutter-main.h" | ||||
| #include "clutter-private.h" | ||||
| #include "clutter-settings-private.h" | ||||
| #include "clutter-xkb-a11y-x11.h" | ||||
|  | ||||
| G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND) | ||||
|  | ||||
| @@ -276,6 +277,20 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11) | ||||
|   _clutter_backend_add_event_translator (backend, translator); | ||||
| } | ||||
|  | ||||
| static void | ||||
| on_keymap_state_change (ClutterKeymapX11 *keymap_x11, | ||||
|                         gpointer          data) | ||||
| { | ||||
|   ClutterDeviceManager *device_manager = CLUTTER_DEVICE_MANAGER (data); | ||||
|   ClutterKbdA11ySettings kbd_a11y_settings; | ||||
|  | ||||
|   /* On keymaps state change, just reapply the current settings, it'll | ||||
|    * take care of enabling/disabling mousekeys based on NumLock state. | ||||
|    */ | ||||
|   clutter_device_manager_get_kbd_a11y_settings (device_manager, &kbd_a11y_settings); | ||||
|   clutter_device_manager_x11_apply_kbd_a11y_settings (device_manager, &kbd_a11y_settings); | ||||
| } | ||||
|  | ||||
| static void | ||||
| clutter_backend_x11_create_keymap (ClutterBackendX11 *backend_x11) | ||||
| { | ||||
| @@ -292,6 +307,11 @@ clutter_backend_x11_create_keymap (ClutterBackendX11 *backend_x11) | ||||
|       backend = CLUTTER_BACKEND (backend_x11); | ||||
|       translator = CLUTTER_EVENT_TRANSLATOR (backend_x11->keymap); | ||||
|       _clutter_backend_add_event_translator (backend, translator); | ||||
|  | ||||
|       g_signal_connect (backend_x11->keymap, | ||||
|                         "state-changed", | ||||
|                         G_CALLBACK (on_keymap_state_change), | ||||
|                         backend->device_manager); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -241,7 +241,12 @@ clutter_device_manager_x11_apply_kbd_a11y_settings (ClutterDeviceManager   *devi | ||||
|     } | ||||
|  | ||||
|   /* mouse keys */ | ||||
|   if (set_xkb_ctrl (desc, kbd_a11y_settings->controls, | ||||
|   if (clutter_keymap_get_num_lock_state (CLUTTER_KEYMAP (backend_x11->keymap))) | ||||
|     { | ||||
|       /* Disable mousekeys when NumLock is ON */ | ||||
|       desc->ctrls->enabled_ctrls &= ~(XkbMouseKeysMask | XkbMouseKeysAccelMask); | ||||
|     } | ||||
|   else if (set_xkb_ctrl (desc, kbd_a11y_settings->controls, | ||||
|                          CLUTTER_A11Y_MOUSE_KEYS_ENABLED, XkbMouseKeysMask | XkbMouseKeysAccelMask)) | ||||
|     { | ||||
|       gint mk_max_speed; | ||||
|   | ||||
| @@ -460,9 +460,7 @@ cogl_path_fill (CoglPath *path); | ||||
|  * use while filling a path.</note> | ||||
|  * | ||||
|  * Stability: unstable | ||||
|  * Deprecated: 1.16: Use cogl_path_fill() instead | ||||
|  */ | ||||
| COGL_DEPRECATED_FOR (cogl_path_fill) | ||||
| void | ||||
| cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, | ||||
|                             CoglPipeline *pipeline, | ||||
| @@ -492,9 +490,7 @@ cogl_path_stroke (CoglPath *path); | ||||
|  * regardless of the current transformation matrix. | ||||
|  * | ||||
|  * Stability: unstable | ||||
|  * Deprecated: 1.16: Use cogl_path_stroke() instead | ||||
|  */ | ||||
| COGL_DEPRECATED_FOR (cogl_path_stroke) | ||||
| void | ||||
| cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, | ||||
|                               CoglPipeline *pipeline, | ||||
| @@ -529,9 +525,7 @@ cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, | ||||
|  * | ||||
|  * Since: 1.8 | ||||
|  * Stability: Unstable | ||||
|  * Deprecated: 1.16: Use cogl_framebuffer_push_path_clip() instead | ||||
|  */ | ||||
| COGL_DEPRECATED_FOR (cogl_framebuffer_push_path_clip) | ||||
| void | ||||
| cogl_clip_push_from_path (CoglPath *path); | ||||
|  | ||||
|   | ||||
| @@ -1504,7 +1504,6 @@ cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, | ||||
|       COGL_FRAMEBUFFER_STATE_CLIP; | ||||
| } | ||||
|  | ||||
| /* XXX: deprecated */ | ||||
| void | ||||
| cogl_clip_push_from_path (CoglPath *path) | ||||
| { | ||||
| @@ -1575,7 +1574,6 @@ _cogl_path_build_stroke_attribute_buffer (CoglPath *path) | ||||
|   data->stroke_n_attributes = n_attributes; | ||||
| } | ||||
|  | ||||
| /* XXX: deprecated */ | ||||
| void | ||||
| cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, | ||||
|                             CoglPipeline *pipeline, | ||||
| @@ -1588,7 +1586,6 @@ cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, | ||||
|   _cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */); | ||||
| } | ||||
|  | ||||
| /* XXX: deprecated */ | ||||
| void | ||||
| cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, | ||||
|                               CoglPipeline *pipeline, | ||||
|   | ||||
| @@ -455,9 +455,6 @@ _cogl_pipeline_free (CoglPipeline *pipeline) | ||||
|       _cogl_bitmask_destroy (&uniforms_state->changed_mask); | ||||
|     } | ||||
|  | ||||
|   if (pipeline->differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE) | ||||
|     g_slice_free (CoglPipelineBigState, pipeline->big_state); | ||||
|  | ||||
|   if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) | ||||
|     { | ||||
|       g_list_foreach (pipeline->layer_differences, | ||||
| @@ -471,6 +468,9 @@ _cogl_pipeline_free (CoglPipeline *pipeline) | ||||
|   if (pipeline->differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS) | ||||
|     _cogl_pipeline_snippet_list_free (&pipeline->big_state->fragment_snippets); | ||||
|  | ||||
|   if (pipeline->differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE) | ||||
|     g_slice_free (CoglPipelineBigState, pipeline->big_state); | ||||
|  | ||||
|   g_list_free (pipeline->deprecated_get_layers_list); | ||||
|  | ||||
|   recursively_free_layer_caches (pipeline); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| project('mutter', 'c', | ||||
|   version: '3.32.1', | ||||
|   meson_version: '>= 0.48.0', | ||||
|   version: '3.32.2', | ||||
|   meson_version: '>= 0.50.0', | ||||
|   license: 'GPLv2+' | ||||
| ) | ||||
|  | ||||
|   | ||||
							
								
								
									
										12
									
								
								po/de.po
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								po/de.po
									
									
									
									
									
								
							| @@ -13,9 +13,9 @@ msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: mutter master\n" | ||||
| "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/mutter/issues\n" | ||||
| "POT-Creation-Date: 2019-02-04 17:52+0000\n" | ||||
| "PO-Revision-Date: 2019-02-26 20:43+0100\n" | ||||
| "Last-Translator: Tim Sabsch <tim@sabsch.com>\n" | ||||
| "POT-Creation-Date: 2019-08-06 00:49+0000\n" | ||||
| "PO-Revision-Date: 2019-09-05 23:42+0200\n" | ||||
| "Last-Translator: Christian Kirbach <christian.kirbach@gmail.com>\n" | ||||
| "Language-Team: Deutsch <gnome-de@gnome.org>\n" | ||||
| "Language: de\n" | ||||
| "MIME-Version: 1.0\n" | ||||
| @@ -152,13 +152,15 @@ msgstr "Zur Arbeitsfläche 4 wechseln" | ||||
| msgid "Switch to last workspace" | ||||
| msgstr "Zur letzten Arbeitsfläche wechseln" | ||||
|  | ||||
| # Wechsel der Arbeitsfläche. Es wird nichts verschoben. | ||||
| #: data/50-mutter-navigation.xml:123 | ||||
| msgid "Move to workspace above" | ||||
| msgstr "Auf Arbeitsfläche darüber verschieben" | ||||
| msgstr "Zur Arbeitsfläche darüber wechseln" | ||||
|  | ||||
| # Wechsel der Arbeitsfläche. Es wird nichts verschoben. | ||||
| #: data/50-mutter-navigation.xml:126 | ||||
| msgid "Move to workspace below" | ||||
| msgstr "Auf Arbeitsfläche darunter verschieben" | ||||
| msgstr "Zur Arbeitsfläche darunter wechseln" | ||||
|  | ||||
| #: data/50-mutter-system.xml:6 data/50-mutter-wayland.xml:6 | ||||
| msgid "System" | ||||
|   | ||||
| @@ -49,6 +49,14 @@ | ||||
| #define DEFAULT_XKB_RULES_FILE "evdev" | ||||
| #define DEFAULT_XKB_MODEL "pc105+inet" | ||||
|  | ||||
| typedef enum | ||||
| { | ||||
|   META_SEQUENCE_NONE, | ||||
|   META_SEQUENCE_ACCEPTED, | ||||
|   META_SEQUENCE_REJECTED, | ||||
|   META_SEQUENCE_PENDING_END | ||||
| } MetaSequenceState; | ||||
|  | ||||
| struct _MetaBackendClass | ||||
| { | ||||
|   GObjectClass parent_class; | ||||
| @@ -71,6 +79,10 @@ struct _MetaBackendClass | ||||
|                               int          device_id, | ||||
|                               uint32_t     timestamp); | ||||
|  | ||||
|   void (* finish_touch_sequence) (MetaBackend          *backend, | ||||
|                                   ClutterEventSequence *sequence, | ||||
|                                   MetaSequenceState     state); | ||||
|  | ||||
|   void (* warp_pointer) (MetaBackend *backend, | ||||
|                          int          x, | ||||
|                          int          y); | ||||
| @@ -135,6 +147,10 @@ gboolean meta_backend_ungrab_device (MetaBackend *backend, | ||||
|                                      int          device_id, | ||||
|                                      uint32_t     timestamp); | ||||
|  | ||||
| void meta_backend_finish_touch_sequence (MetaBackend          *backend, | ||||
|                                          ClutterEventSequence *sequence, | ||||
|                                          MetaSequenceState     state); | ||||
|  | ||||
| void meta_backend_warp_pointer (MetaBackend *backend, | ||||
|                                 int          x, | ||||
|                                 int          y); | ||||
|   | ||||
| @@ -1007,6 +1007,20 @@ meta_backend_ungrab_device (MetaBackend *backend, | ||||
|   return META_BACKEND_GET_CLASS (backend)->ungrab_device (backend, device_id, timestamp); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_backend_finish_touch_sequence: (skip) | ||||
|  */ | ||||
| void | ||||
| meta_backend_finish_touch_sequence (MetaBackend          *backend, | ||||
|                                     ClutterEventSequence *sequence, | ||||
|                                     MetaSequenceState     state) | ||||
| { | ||||
|   if (META_BACKEND_GET_CLASS (backend)->finish_touch_sequence) | ||||
|     META_BACKEND_GET_CLASS (backend)->finish_touch_sequence (backend, | ||||
|                                                              sequence, | ||||
|                                                              state); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_backend_warp_pointer: (skip) | ||||
|  */ | ||||
|   | ||||
| @@ -207,6 +207,8 @@ update_inhibited (MetaIdleMonitor *monitor, | ||||
|   if (inhibited == monitor->inhibited) | ||||
|     return; | ||||
|  | ||||
|   monitor->inhibited = inhibited; | ||||
|  | ||||
|   g_hash_table_foreach (monitor->watches, | ||||
|                         update_inhibited_watch, | ||||
|                         monitor); | ||||
| @@ -316,11 +318,18 @@ idle_monitor_dispatch_timeout (GSource     *source, | ||||
|                                gpointer     user_data) | ||||
| { | ||||
|   MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) user_data; | ||||
|   int64_t now; | ||||
|   int64_t ready_time; | ||||
|  | ||||
|   now = g_source_get_time (source); | ||||
|   ready_time = g_source_get_ready_time (source); | ||||
|   if (ready_time > now) | ||||
|     return G_SOURCE_CONTINUE; | ||||
|  | ||||
|   _meta_idle_monitor_watch_fire (watch); | ||||
|   g_source_set_ready_time (watch->timeout_source, -1); | ||||
|  | ||||
|   return TRUE; | ||||
|   return G_SOURCE_CONTINUE; | ||||
| } | ||||
|  | ||||
| static GSourceFuncs idle_monitor_source_funcs = { | ||||
| @@ -506,6 +515,12 @@ meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor) | ||||
|         { | ||||
|           _meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           if (monitor->inhibited) | ||||
|             { | ||||
|               g_source_set_ready_time (watch->timeout_source, -1); | ||||
|             } | ||||
|           else | ||||
|             { | ||||
|               g_source_set_ready_time (watch->timeout_source, | ||||
| @@ -513,6 +528,7 @@ meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor) | ||||
|                                        watch->timeout_msec * 1000); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   g_list_free (watch_ids); | ||||
| } | ||||
|   | ||||
| @@ -381,7 +381,7 @@ guess_candidates (MetaInputMapper     *mapper, | ||||
|       info->candidates[META_MATCH_SIZE] = matched_monitor; | ||||
|     } | ||||
|  | ||||
|   if (input->builtin) | ||||
|   if (input->builtin || best == N_OUTPUT_MATCHES) | ||||
|     { | ||||
|       best = MIN (best, META_MATCH_IS_BUILTIN); | ||||
|       find_builtin_output (mapper, &info->candidates[META_MATCH_IS_BUILTIN]); | ||||
|   | ||||
| @@ -1216,7 +1216,7 @@ load_keyboard_a11y_settings (MetaInputSettings  *input_settings, | ||||
|                              ClutterInputDevice *device) | ||||
| { | ||||
|   MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); | ||||
|   ClutterKbdA11ySettings kbd_a11y_settings; | ||||
|   ClutterKbdA11ySettings kbd_a11y_settings = { 0 }; | ||||
|   ClutterInputDevice *core_keyboard; | ||||
|   guint i; | ||||
|  | ||||
|   | ||||
| @@ -90,6 +90,12 @@ meta_renderer_create_view (MetaRenderer       *renderer, | ||||
|  */ | ||||
| void | ||||
| meta_renderer_rebuild_views (MetaRenderer *renderer) | ||||
| { | ||||
|   return META_RENDERER_GET_CLASS (renderer)->rebuild_views (renderer); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_renderer_real_rebuild_views (MetaRenderer *renderer) | ||||
| { | ||||
|   MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); | ||||
|   MetaBackend *backend = meta_get_backend (); | ||||
| @@ -181,4 +187,6 @@ meta_renderer_class_init (MetaRendererClass *klass) | ||||
|   GObjectClass *object_class = G_OBJECT_CLASS (klass); | ||||
|  | ||||
|   object_class->finalize = meta_renderer_finalize; | ||||
|  | ||||
|   klass->rebuild_views = meta_renderer_real_rebuild_views; | ||||
| } | ||||
|   | ||||
| @@ -43,6 +43,7 @@ struct _MetaRendererClass | ||||
|   CoglRenderer * (* create_cogl_renderer) (MetaRenderer *renderer); | ||||
|   MetaRendererView * (* create_view) (MetaRenderer       *renderer, | ||||
|                                       MetaLogicalMonitor *logical_monitor); | ||||
|   void (* rebuild_views) (MetaRenderer *renderer); | ||||
| }; | ||||
|  | ||||
| CoglRenderer * meta_renderer_create_cogl_renderer (MetaRenderer *renderer); | ||||
|   | ||||
| @@ -368,12 +368,8 @@ parse_transforms (MetaCrtc          *crtc, | ||||
|  | ||||
|       if (strcmp (prop->enums[i].name, "rotate-0") == 0) | ||||
|         transform = META_MONITOR_TRANSFORM_NORMAL; | ||||
|       else if (strcmp (prop->enums[i].name, "rotate-90") == 0) | ||||
|         transform = META_MONITOR_TRANSFORM_90; | ||||
|       else if (strcmp (prop->enums[i].name, "rotate-180") == 0) | ||||
|         transform = META_MONITOR_TRANSFORM_180; | ||||
|       else if (strcmp (prop->enums[i].name, "rotate-270") == 0) | ||||
|         transform = META_MONITOR_TRANSFORM_270; | ||||
|  | ||||
|       if (transform != -1) | ||||
|         { | ||||
|   | ||||
| @@ -823,6 +823,7 @@ static void | ||||
| cursor_priv_free (MetaCursorNativePrivate *cursor_priv) | ||||
| { | ||||
|   g_hash_table_destroy (cursor_priv->gpu_states); | ||||
|   g_free (cursor_priv); | ||||
| } | ||||
|  | ||||
| static MetaCursorNativePrivate * | ||||
|   | ||||
| @@ -258,6 +258,9 @@ cogl_pixel_format_from_drm_format (uint32_t               drm_format, | ||||
|                                    CoglPixelFormat       *out_format, | ||||
|                                    CoglTextureComponents *out_components); | ||||
|  | ||||
| static void | ||||
| meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); | ||||
|  | ||||
| static MetaBackend * | ||||
| backend_from_renderer_native (MetaRendererNative *renderer_native) | ||||
| { | ||||
| @@ -1277,7 +1280,7 @@ meta_renderer_native_egl_context_created (CoglDisplay *cogl_display, | ||||
|                                       cogl_display_egl->dummy_surface, | ||||
|                                       cogl_display_egl->egl_context)) | ||||
|     { | ||||
|       _cogl_set_error (error, COGL_WINSYS_ERROR, | ||||
|       g_set_error (error, COGL_WINSYS_ERROR, | ||||
|                    COGL_WINSYS_ERROR_CREATE_CONTEXT, | ||||
|                    "Failed to make context current"); | ||||
|       return FALSE; | ||||
| @@ -3035,11 +3038,53 @@ meta_onscreen_native_allocate (CoglOnscreen *onscreen, | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| destroy_egl_surface (CoglOnscreen *onscreen) | ||||
| { | ||||
|   CoglOnscreenEGL *onscreen_egl = onscreen->winsys; | ||||
|  | ||||
|   if (onscreen_egl->egl_surface != EGL_NO_SURFACE) | ||||
|     { | ||||
|       MetaOnscreenNative *onscreen_native = onscreen_egl->platform; | ||||
|       MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); | ||||
|       CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); | ||||
|       CoglContext *cogl_context = framebuffer->context; | ||||
|       CoglRenderer *cogl_renderer = cogl_context->display->renderer; | ||||
|       CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; | ||||
|  | ||||
|       meta_egl_destroy_surface (egl, | ||||
|                                 cogl_renderer_egl->edpy, | ||||
|                                 onscreen_egl->egl_surface, | ||||
|                                 NULL); | ||||
|       onscreen_egl->egl_surface = EGL_NO_SURFACE; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| discard_onscreen_page_flip_retries (MetaOnscreenNative *onscreen_native) | ||||
| { | ||||
|   g_list_free_full (onscreen_native->pending_page_flip_retries, | ||||
|                     (GDestroyNotify) retry_page_flip_data_free); | ||||
|   onscreen_native->pending_page_flip_retries = NULL; | ||||
|  | ||||
|   if (onscreen_native->retry_page_flips_source) | ||||
|     { | ||||
|       MetaBackend *backend = | ||||
|         backend_from_renderer_native (onscreen_native->renderer_native); | ||||
|  | ||||
|       meta_backend_thaw_updates (backend); | ||||
|       g_clear_pointer (&onscreen_native->retry_page_flips_source, | ||||
|                        g_source_destroy); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) | ||||
| { | ||||
|   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); | ||||
|   CoglContext *cogl_context = framebuffer->context; | ||||
|   CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); | ||||
|   CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; | ||||
|   CoglRenderer *cogl_renderer = cogl_context->display->renderer; | ||||
|   CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; | ||||
|   CoglOnscreenEGL *onscreen_egl = onscreen->winsys; | ||||
| @@ -3052,28 +3097,18 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) | ||||
|  | ||||
|   onscreen_native = onscreen_egl->platform; | ||||
|  | ||||
|   g_list_free_full (onscreen_native->pending_page_flip_retries, | ||||
|                     (GDestroyNotify) retry_page_flip_data_free); | ||||
|   if (onscreen_native->retry_page_flips_source) | ||||
|   if (onscreen_egl->egl_surface != EGL_NO_SURFACE && | ||||
|       (cogl_display_egl->current_draw_surface == onscreen_egl->egl_surface || | ||||
|        cogl_display_egl->current_read_surface == onscreen_egl->egl_surface)) | ||||
|     { | ||||
|       MetaBackend *backend = | ||||
|         backend_from_renderer_native (onscreen_native->renderer_native); | ||||
|  | ||||
|       meta_backend_thaw_updates (backend); | ||||
|       g_clear_pointer (&onscreen_native->retry_page_flips_source, | ||||
|                        g_source_destroy); | ||||
|       if (!_cogl_winsys_egl_make_current (cogl_display, | ||||
|                                           cogl_display_egl->dummy_surface, | ||||
|                                           cogl_display_egl->dummy_surface, | ||||
|                                           cogl_display_egl->egl_context)) | ||||
|         g_warning ("Failed to clear current context"); | ||||
|     } | ||||
|  | ||||
|   if (onscreen_egl->egl_surface != EGL_NO_SURFACE) | ||||
|     { | ||||
|       MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); | ||||
|  | ||||
|       meta_egl_destroy_surface (egl, | ||||
|                                 cogl_renderer_egl->edpy, | ||||
|                                 onscreen_egl->egl_surface, | ||||
|                                 NULL); | ||||
|       onscreen_egl->egl_surface = EGL_NO_SURFACE; | ||||
|     } | ||||
|   discard_onscreen_page_flip_retries (onscreen_native); | ||||
|  | ||||
|   renderer_gpu_data = | ||||
|     meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, | ||||
| @@ -3087,6 +3122,8 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) | ||||
|  | ||||
|       free_current_bo (onscreen); | ||||
|  | ||||
|       destroy_egl_surface (onscreen); | ||||
|  | ||||
|       if (onscreen_native->gbm.surface) | ||||
|         { | ||||
|           gbm_surface_destroy (onscreen_native->gbm.surface); | ||||
| @@ -3097,6 +3134,9 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) | ||||
|     case META_RENDERER_NATIVE_MODE_EGL_DEVICE: | ||||
|       release_dumb_fb (&onscreen_native->egl.dumb_fb, | ||||
|                        onscreen_native->render_gpu); | ||||
|  | ||||
|       destroy_egl_surface (onscreen); | ||||
|  | ||||
|       if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR) | ||||
|         { | ||||
|           MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); | ||||
| @@ -3157,7 +3197,7 @@ meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native) | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| void | ||||
| static void | ||||
| meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native) | ||||
| { | ||||
|   MetaRenderer *renderer = META_RENDERER (renderer_native); | ||||
| @@ -3523,6 +3563,37 @@ meta_renderer_native_create_view (MetaRenderer       *renderer, | ||||
|   return view; | ||||
| } | ||||
|  | ||||
| static void | ||||
| discard_page_flip_retries (MetaRenderer *renderer) | ||||
| { | ||||
|   GList *l; | ||||
|  | ||||
|   for (l = meta_renderer_get_views (renderer); l; l = l->next) | ||||
|     { | ||||
|       ClutterStageView *stage_view = l->data; | ||||
|       CoglFramebuffer *framebuffer = | ||||
|         clutter_stage_view_get_onscreen (stage_view); | ||||
|       CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); | ||||
|       CoglOnscreenEGL *onscreen_egl = onscreen->winsys; | ||||
|       MetaOnscreenNative *onscreen_native = onscreen_egl->platform; | ||||
|  | ||||
|       discard_onscreen_page_flip_retries (onscreen_native); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_renderer_native_rebuild_views (MetaRenderer *renderer) | ||||
| { | ||||
|   MetaRendererClass *parent_renderer_class = | ||||
|     META_RENDERER_CLASS (meta_renderer_native_parent_class); | ||||
|  | ||||
|   discard_page_flip_retries (renderer); | ||||
|  | ||||
|   parent_renderer_class->rebuild_views (renderer); | ||||
|  | ||||
|   meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer)); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_renderer_native_finish_frame (MetaRendererNative *renderer_native) | ||||
| { | ||||
| @@ -4038,6 +4109,7 @@ create_renderer_gpu_data_egl_device (MetaRendererNative  *renderer_native, | ||||
|                    G_IO_ERROR_FAILED, | ||||
|                    "Missing EGL extensions required for EGLDevice renderer: %s", | ||||
|                    missing_extensions_str); | ||||
|       meta_egl_terminate (egl, egl_display, NULL); | ||||
|       g_free (missing_extensions_str); | ||||
|       g_free (missing_extensions); | ||||
|       return NULL; | ||||
| @@ -4320,6 +4392,7 @@ meta_renderer_native_class_init (MetaRendererNativeClass *klass) | ||||
|  | ||||
|   renderer_class->create_cogl_renderer = meta_renderer_native_create_cogl_renderer; | ||||
|   renderer_class->create_view = meta_renderer_native_create_view; | ||||
|   renderer_class->rebuild_views = meta_renderer_native_rebuild_views; | ||||
|  | ||||
|   obj_props[PROP_MONITOR_MANAGER] = | ||||
|     g_param_spec_object ("monitor-manager", | ||||
|   | ||||
| @@ -53,20 +53,6 @@ struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms); | ||||
|  | ||||
| gboolean meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native); | ||||
|  | ||||
| void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); | ||||
|  | ||||
| gboolean meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native, | ||||
|                                                     MetaRendererView   *view, | ||||
|                                                     int                 width, | ||||
|                                                     int                 height, | ||||
|                                                     GError            **error); | ||||
|  | ||||
| void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native, | ||||
|                                            uint32_t            id, | ||||
|                                            gboolean            ignore); | ||||
|  | ||||
| MetaRendererView * meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native); | ||||
|  | ||||
| void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native); | ||||
|  | ||||
| int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native); | ||||
|   | ||||
| @@ -140,7 +140,6 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native) | ||||
|   ClutterActor *stage = meta_backend_get_stage (backend); | ||||
|  | ||||
|   meta_renderer_rebuild_views (renderer); | ||||
|   meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer)); | ||||
|   clutter_stage_update_resource_scales (CLUTTER_STAGE (stage)); | ||||
|   ensure_frame_callbacks (stage_native); | ||||
| } | ||||
|   | ||||
| @@ -66,6 +66,10 @@ struct _MetaBackendX11Private | ||||
|   XSyncAlarm user_active_alarm; | ||||
|   XSyncCounter counter; | ||||
|  | ||||
|   int current_touch_replay_sync_serial; | ||||
|   int pending_touch_replay_sync_serial; | ||||
|   Atom touch_replay_sync_atom; | ||||
|  | ||||
|   int xinput_opcode; | ||||
|   int xinput_event_base; | ||||
|   int xinput_error_base; | ||||
| @@ -174,6 +178,26 @@ meta_backend_x11_translate_device_event (MetaBackendX11 *x11, | ||||
|   backend_x11_class->translate_device_event (x11, device_event); | ||||
| } | ||||
|  | ||||
| static void | ||||
| maybe_translate_touch_replay_pointer_event (MetaBackendX11 *x11, | ||||
|                                             XIDeviceEvent  *device_event) | ||||
| { | ||||
|   MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); | ||||
|  | ||||
|   if (!device_event->send_event && | ||||
|       device_event->time != META_CURRENT_TIME && | ||||
|       priv->current_touch_replay_sync_serial != | ||||
|       priv->pending_touch_replay_sync_serial && | ||||
|       XSERVER_TIME_IS_BEFORE (device_event->time, priv->latest_evtime)) | ||||
|     { | ||||
|       /* Emulated pointer events received after XIRejectTouch is received | ||||
|        * on a passive touch grab will contain older timestamps, update those | ||||
|        * so we dont get InvalidTime at grabs. | ||||
|        */ | ||||
|       device_event->time = priv->latest_evtime; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| translate_device_event (MetaBackendX11 *x11, | ||||
|                         XIDeviceEvent  *device_event) | ||||
| @@ -183,20 +207,8 @@ translate_device_event (MetaBackendX11 *x11, | ||||
|   meta_backend_x11_translate_device_event (x11, device_event); | ||||
|  | ||||
|   if (!device_event->send_event && device_event->time != META_CURRENT_TIME) | ||||
|     { | ||||
|       if (XSERVER_TIME_IS_BEFORE (device_event->time, priv->latest_evtime)) | ||||
|         { | ||||
|           /* Emulated pointer events received after XIRejectTouch is received | ||||
|            * on a passive touch grab will contain older timestamps, update those | ||||
|            * so we dont get InvalidTime at grabs. | ||||
|            */ | ||||
|           device_event->time = priv->latest_evtime; | ||||
|         } | ||||
|  | ||||
|       /* Update the internal latest evtime, for any possible later use */ | ||||
|     priv->latest_evtime = device_event->time; | ||||
| } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_backend_x11_translate_crossing_event (MetaBackendX11 *x11, | ||||
| @@ -260,6 +272,9 @@ maybe_spoof_event_as_stage_event (MetaBackendX11 *x11, | ||||
|     case XI_Motion: | ||||
|     case XI_ButtonPress: | ||||
|     case XI_ButtonRelease: | ||||
|       maybe_translate_touch_replay_pointer_event (x11, | ||||
|                                                   (XIDeviceEvent *) input_event); | ||||
|       /* Intentional fall-through */ | ||||
|     case XI_KeyPress: | ||||
|     case XI_KeyRelease: | ||||
|     case XI_TouchBegin: | ||||
| @@ -327,6 +342,17 @@ handle_host_xevent (MetaBackend *backend, | ||||
|   MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); | ||||
|   gboolean bypass_clutter = FALSE; | ||||
|  | ||||
|   switch (event->type) | ||||
|     { | ||||
|     case ClientMessage: | ||||
|       if (event->xclient.window == meta_backend_x11_get_xwindow (x11) && | ||||
|           event->xclient.message_type == priv->touch_replay_sync_atom) | ||||
|         priv->current_touch_replay_sync_serial = event->xclient.data.l[0]; | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|   XGetEventData (priv->xdisplay, &event->xcookie); | ||||
|  | ||||
|   { | ||||
| @@ -534,6 +560,10 @@ meta_backend_x11_post_init (MetaBackend *backend) | ||||
|   monitor_manager = meta_backend_get_monitor_manager (backend); | ||||
|   g_signal_connect (monitor_manager, "monitors-changed-internal", | ||||
|                     G_CALLBACK (on_monitors_changed), backend); | ||||
|  | ||||
|   priv->touch_replay_sync_atom = XInternAtom (priv->xdisplay, | ||||
|                                               "_MUTTER_TOUCH_SEQUENCE_SYNC", | ||||
|                                               False); | ||||
| } | ||||
|  | ||||
| static ClutterBackend * | ||||
| @@ -591,6 +621,43 @@ meta_backend_x11_ungrab_device (MetaBackend *backend, | ||||
|   return (ret == Success); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_backend_x11_finish_touch_sequence (MetaBackend          *backend, | ||||
|                                         ClutterEventSequence *sequence, | ||||
|                                         MetaSequenceState     state) | ||||
| { | ||||
|   MetaBackendX11 *x11 = META_BACKEND_X11 (backend); | ||||
|   MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); | ||||
|   int event_mode; | ||||
|  | ||||
|   if (state == META_SEQUENCE_ACCEPTED) | ||||
|     event_mode = XIAcceptTouch; | ||||
|   else if (state == META_SEQUENCE_REJECTED) | ||||
|     event_mode = XIRejectTouch; | ||||
|   else | ||||
|     g_return_if_reached (); | ||||
|  | ||||
|   XIAllowTouchEvents (priv->xdisplay, | ||||
|                       META_VIRTUAL_CORE_POINTER_ID, | ||||
|                       clutter_x11_event_sequence_get_touch_detail (sequence), | ||||
|                       DefaultRootWindow (priv->xdisplay), event_mode); | ||||
|  | ||||
|   if (state == META_SEQUENCE_REJECTED) | ||||
|     { | ||||
|       XClientMessageEvent ev; | ||||
|  | ||||
|       ev = (XClientMessageEvent) { | ||||
|         .type = ClientMessage, | ||||
|         .window = meta_backend_x11_get_xwindow (x11), | ||||
|         .message_type = priv->touch_replay_sync_atom, | ||||
|         .format = 32, | ||||
|         .data.l[0] = ++priv->pending_touch_replay_sync_serial, | ||||
|       }; | ||||
|       XSendEvent (priv->xdisplay, meta_backend_x11_get_xwindow (x11), | ||||
|                   False, 0, (XEvent *) &ev); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_backend_x11_warp_pointer (MetaBackend *backend, | ||||
|                                int          x, | ||||
| @@ -776,6 +843,7 @@ meta_backend_x11_class_init (MetaBackendX11Class *klass) | ||||
|   backend_class->post_init = meta_backend_x11_post_init; | ||||
|   backend_class->grab_device = meta_backend_x11_grab_device; | ||||
|   backend_class->ungrab_device = meta_backend_x11_ungrab_device; | ||||
|   backend_class->finish_touch_sequence = meta_backend_x11_finish_touch_sequence; | ||||
|   backend_class->warp_pointer = meta_backend_x11_warp_pointer; | ||||
|   backend_class->get_current_logical_monitor = meta_backend_x11_get_current_logical_monitor; | ||||
|   backend_class->get_keymap = meta_backend_x11_get_keymap; | ||||
|   | ||||
| @@ -18,6 +18,9 @@ struct _MetaCompositor | ||||
|   guint           pre_paint_func_id; | ||||
|   guint           post_paint_func_id; | ||||
|  | ||||
|   guint           stage_presented_id; | ||||
|   guint           stage_after_paint_id; | ||||
|  | ||||
|   gint64          server_time_query_time; | ||||
|   gint64          server_time_offset; | ||||
|  | ||||
|   | ||||
| @@ -92,6 +92,10 @@ on_presented (ClutterStage     *stage, | ||||
|               ClutterFrameInfo *frame_info, | ||||
|               MetaCompositor   *compositor); | ||||
|  | ||||
| static void | ||||
| on_top_window_actor_destroyed (MetaWindowActor *window_actor, | ||||
|                                MetaCompositor  *compositor); | ||||
|  | ||||
| static gboolean | ||||
| is_modal (MetaDisplay *display) | ||||
| { | ||||
| @@ -131,9 +135,31 @@ meta_switch_workspace_completed (MetaCompositor *compositor) | ||||
| void | ||||
| meta_compositor_destroy (MetaCompositor *compositor) | ||||
| { | ||||
|   g_signal_handler_disconnect (compositor->stage, | ||||
|                                compositor->stage_after_paint_id); | ||||
|   g_signal_handler_disconnect (compositor->stage, | ||||
|                                compositor->stage_presented_id); | ||||
|  | ||||
|   compositor->stage_after_paint_id = 0; | ||||
|   compositor->stage_presented_id = 0; | ||||
|   compositor->stage = NULL; | ||||
|  | ||||
|   clutter_threads_remove_repaint_func (compositor->pre_paint_func_id); | ||||
|   clutter_threads_remove_repaint_func (compositor->post_paint_func_id); | ||||
|  | ||||
|   if (compositor->top_window_actor) | ||||
|     { | ||||
|       g_signal_handlers_disconnect_by_func (compositor->top_window_actor, | ||||
|                                             on_top_window_actor_destroyed, | ||||
|                                             compositor); | ||||
|       compositor->top_window_actor = NULL; | ||||
|     } | ||||
|  | ||||
|   g_clear_pointer (&compositor->window_group, clutter_actor_destroy); | ||||
|   g_clear_pointer (&compositor->top_window_group, clutter_actor_destroy); | ||||
|   g_clear_pointer (&compositor->feedback_group, clutter_actor_destroy); | ||||
|   g_clear_pointer (&compositor->windows, g_list_free); | ||||
|  | ||||
|   if (compositor->have_x11_sync_object) | ||||
|     meta_sync_ring_destroy (); | ||||
| } | ||||
| @@ -503,6 +529,7 @@ meta_compositor_manage (MetaCompositor *compositor) | ||||
|  | ||||
|   compositor->stage = meta_backend_get_stage (backend); | ||||
|  | ||||
|   compositor->stage_presented_id = | ||||
|     g_signal_connect (compositor->stage, "presented", | ||||
|                       G_CALLBACK (on_presented), | ||||
|                                                      compositor); | ||||
| @@ -515,7 +542,8 @@ meta_compositor_manage (MetaCompositor *compositor) | ||||
|    * connections to ::after-paint, connect() vs. connect_after() doesn't | ||||
|    * matter. | ||||
|    */ | ||||
|   g_signal_connect_after (CLUTTER_STAGE (compositor->stage), "after-paint", | ||||
|   compositor->stage_after_paint_id = | ||||
|     g_signal_connect_after (compositor->stage, "after-paint", | ||||
|                             G_CALLBACK (after_stage_paint), compositor); | ||||
|  | ||||
|   clutter_stage_set_sync_delay (CLUTTER_STAGE (compositor->stage), META_SYNC_DELAY); | ||||
|   | ||||
| @@ -252,12 +252,11 @@ static void | ||||
| set_file (MetaBackground       *self, | ||||
|           GFile               **filep, | ||||
|           MetaBackgroundImage **imagep, | ||||
|           GFile                *file) | ||||
|           GFile                *file, | ||||
|           gboolean              force_reload) | ||||
| { | ||||
|   if (!file_equal0 (*filep, file)) | ||||
|   if (force_reload || !file_equal0 (*filep, file)) | ||||
|     { | ||||
|       g_clear_object (filep); | ||||
|  | ||||
|       if (*imagep) | ||||
|         { | ||||
|           g_signal_handlers_disconnect_by_func (*imagep, | ||||
| @@ -267,11 +266,12 @@ set_file (MetaBackground       *self, | ||||
|           *imagep = NULL; | ||||
|         } | ||||
|  | ||||
|       g_set_object (filep, file); | ||||
|  | ||||
|       if (file) | ||||
|         { | ||||
|           MetaBackgroundImageCache *cache = meta_background_image_cache_get_default (); | ||||
|  | ||||
|           *filep = g_object_ref (file); | ||||
|           *imagep = meta_background_image_cache_load (cache, file); | ||||
|           g_signal_connect (*imagep, "loaded", | ||||
|                             G_CALLBACK (on_background_loaded), self); | ||||
| @@ -279,6 +279,32 @@ set_file (MetaBackground       *self, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| on_gl_video_memory_purged (MetaBackground *self) | ||||
| { | ||||
|   MetaBackgroundImageCache *cache = meta_background_image_cache_get_default (); | ||||
|  | ||||
|   /* The GPU memory that just got invalidated is the texture inside | ||||
|    * self->background_image1,2 and/or its mipmaps. However, to save memory the | ||||
|    * original pixbuf isn't kept in RAM so we can't do a simple re-upload. The | ||||
|    * only copy of the image was the one in texture memory that got invalidated. | ||||
|    * So we need to do a full reload from disk. | ||||
|    */ | ||||
|   if (self->file1) | ||||
|     { | ||||
|       meta_background_image_cache_purge (cache, self->file1); | ||||
|       set_file (self, &self->file1, &self->background_image1, self->file1, TRUE); | ||||
|     } | ||||
|  | ||||
|   if (self->file2) | ||||
|     { | ||||
|       meta_background_image_cache_purge (cache, self->file2); | ||||
|       set_file (self, &self->file2, &self->background_image2, self->file2, TRUE); | ||||
|     } | ||||
|  | ||||
|   mark_changed (self); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_background_dispose (GObject *object) | ||||
| { | ||||
| @@ -287,8 +313,8 @@ meta_background_dispose (GObject *object) | ||||
|   free_color_texture (self); | ||||
|   free_wallpaper_texture (self); | ||||
|  | ||||
|   set_file (self, &self->file1, &self->background_image1, NULL); | ||||
|   set_file (self, &self->file2, &self->background_image2, NULL); | ||||
|   set_file (self, &self->file1, &self->background_image1, NULL, FALSE); | ||||
|   set_file (self, &self->file2, &self->background_image2, NULL, FALSE); | ||||
|  | ||||
|   set_display (self, NULL); | ||||
|  | ||||
| @@ -312,7 +338,7 @@ meta_background_constructed (GObject *object) | ||||
|   G_OBJECT_CLASS (meta_background_parent_class)->constructed (object); | ||||
|  | ||||
|   g_signal_connect_object (self->display, "gl-video-memory-purged", | ||||
|                            G_CALLBACK (mark_changed), object, G_CONNECT_SWAPPED); | ||||
|                            G_CALLBACK (on_gl_video_memory_purged), object, G_CONNECT_SWAPPED); | ||||
|  | ||||
|   g_signal_connect_object (monitor_manager, "monitors-changed", | ||||
|                            G_CALLBACK (on_monitors_changed), self, | ||||
| @@ -937,8 +963,8 @@ meta_background_set_blend (MetaBackground          *self, | ||||
|   g_return_if_fail (META_IS_BACKGROUND (self)); | ||||
|   g_return_if_fail (blend_factor >= 0.0 && blend_factor <= 1.0); | ||||
|  | ||||
|   set_file (self, &self->file1, &self->background_image1, file1); | ||||
|   set_file (self, &self->file2, &self->background_image2, file2); | ||||
|   set_file (self, &self->file1, &self->background_image1, file1, FALSE); | ||||
|   set_file (self, &self->file2, &self->background_image2, file2, FALSE); | ||||
|  | ||||
|   self->blend_factor = blend_factor; | ||||
|   self->style = style; | ||||
|   | ||||
| @@ -32,6 +32,7 @@ | ||||
| #include "cogl/winsys/cogl-texture-pixmap-x11.h" | ||||
| #include "compositor/meta-cullable.h" | ||||
| #include "compositor/meta-shaped-texture-private.h" | ||||
| #include "compositor/meta-window-actor-private.h" | ||||
| #include "core/window-private.h" | ||||
| #include "meta/meta-x11-errors.h" | ||||
| #include "x11/meta-x11-display-private.h" | ||||
| @@ -71,11 +72,13 @@ static void | ||||
| free_damage (MetaSurfaceActorX11 *self) | ||||
| { | ||||
|   MetaDisplay *display = self->display; | ||||
|   Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); | ||||
|   Display *xdisplay; | ||||
|  | ||||
|   if (self->damage == None) | ||||
|     return; | ||||
|  | ||||
|   xdisplay = meta_x11_display_get_xdisplay (display->x11_display); | ||||
|  | ||||
|   meta_x11_error_trap_push (display->x11_display); | ||||
|   XDamageDestroy (xdisplay, self->damage); | ||||
|   self->damage = None; | ||||
| @@ -86,12 +89,14 @@ static void | ||||
| detach_pixmap (MetaSurfaceActorX11 *self) | ||||
| { | ||||
|   MetaDisplay *display = self->display; | ||||
|   Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); | ||||
|   MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); | ||||
|   Display *xdisplay; | ||||
|  | ||||
|   if (self->pixmap == None) | ||||
|     return; | ||||
|  | ||||
|   xdisplay = meta_x11_display_get_xdisplay (display->x11_display); | ||||
|  | ||||
|   /* Get rid of all references to the pixmap before freeing it; it's unclear whether | ||||
|    * you are supposed to be able to free a GLXPixmap after freeing the underlying | ||||
|    * pixmap, but it certainly doesn't work with current DRI/Mesa | ||||
| @@ -343,13 +348,19 @@ meta_surface_actor_x11_is_unredirected (MetaSurfaceActor *actor) | ||||
|   return self->unredirected; | ||||
| } | ||||
|  | ||||
| static void | ||||
| release_x11_resources (MetaSurfaceActorX11 *self) | ||||
| { | ||||
|   detach_pixmap (self); | ||||
|   free_damage (self); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_surface_actor_x11_dispose (GObject *object) | ||||
| { | ||||
|   MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (object); | ||||
|  | ||||
|   detach_pixmap (self); | ||||
|   free_damage (self); | ||||
|   release_x11_resources (self); | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_surface_actor_x11_parent_class)->dispose (object); | ||||
| } | ||||
| @@ -403,8 +414,7 @@ window_decorated_notify (MetaWindow *window, | ||||
| { | ||||
|   MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (user_data); | ||||
|  | ||||
|   detach_pixmap (self); | ||||
|   free_damage (self); | ||||
|   release_x11_resources (self); | ||||
|   create_damage (self); | ||||
| } | ||||
|  | ||||
| @@ -441,6 +451,10 @@ meta_surface_actor_x11_new (MetaWindow *window) | ||||
|   g_signal_connect_object (self->window, "notify::decorated", | ||||
|                            G_CALLBACK (window_decorated_notify), self, 0); | ||||
|  | ||||
|   g_signal_connect_object (meta_window_actor_from_window (window), "destroy", | ||||
|                            G_CALLBACK (release_x11_resources), self, | ||||
|                            G_CONNECT_SWAPPED); | ||||
|  | ||||
|   self->unredirected = FALSE; | ||||
|   sync_unredirected (self); | ||||
|  | ||||
|   | ||||
| @@ -52,8 +52,6 @@ typedef enum | ||||
|  | ||||
| typedef struct _MetaWindowActorPrivate | ||||
| { | ||||
|   ClutterActor           parent; | ||||
|  | ||||
|   MetaWindow *window; | ||||
|   MetaCompositor *compositor; | ||||
|  | ||||
| @@ -417,7 +415,7 @@ meta_window_actor_update_surface (MetaWindowActor *self) | ||||
|   else | ||||
|     surface_actor = NULL; | ||||
|  | ||||
|   set_surface (self, surface_actor); | ||||
|   META_WINDOW_ACTOR_GET_CLASS (self)->set_surface_actor (self, surface_actor); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -430,6 +428,9 @@ meta_window_actor_constructed (GObject *object) | ||||
|  | ||||
|   priv->compositor = window->display->compositor; | ||||
|  | ||||
|   /* Hang our compositor window state off the MetaWindow for fast retrieval */ | ||||
|   meta_window_set_compositor_private (window, object); | ||||
|  | ||||
|   meta_window_actor_update_surface (self); | ||||
|  | ||||
|   meta_window_actor_update_opacity (self); | ||||
| @@ -446,9 +447,6 @@ meta_window_actor_constructed (GObject *object) | ||||
|     priv->first_frame_state = DRAWING_FIRST_FRAME; | ||||
|  | ||||
|   meta_window_actor_sync_actor_geometry (self, priv->window->placed); | ||||
|  | ||||
|   /* Hang our compositor window state off the MetaWindow for fast retrieval */ | ||||
|   meta_window_set_compositor_private (window, object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -476,7 +474,7 @@ meta_window_actor_dispose (GObject *object) | ||||
|  | ||||
|   g_clear_object (&priv->window); | ||||
|  | ||||
|   set_surface (self, NULL); | ||||
|   META_WINDOW_ACTOR_GET_CLASS (self)->set_surface_actor (self, NULL); | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object); | ||||
| } | ||||
|   | ||||
| @@ -42,6 +42,7 @@ | ||||
| #include <X11/extensions/Xdamage.h> | ||||
| #include <X11/extensions/Xfixes.h> | ||||
|  | ||||
| #include "backends/meta-backend-private.h" | ||||
| #include "backends/meta-cursor-sprite-xcursor.h" | ||||
| #include "backends/meta-cursor-tracker-private.h" | ||||
| #include "backends/meta-idle-monitor-dbus.h" | ||||
| @@ -598,27 +599,23 @@ gesture_tracker_state_changed (MetaGestureTracker   *tracker, | ||||
|                                MetaSequenceState     state, | ||||
|                                MetaDisplay          *display) | ||||
| { | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|   switch (state) | ||||
|     { | ||||
|       if (state == META_SEQUENCE_ACCEPTED) | ||||
|         meta_display_cancel_touch (display); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); | ||||
|       int event_mode; | ||||
|  | ||||
|       if (state == META_SEQUENCE_ACCEPTED) | ||||
|         event_mode = XIAcceptTouch; | ||||
|       else if (state == META_SEQUENCE_REJECTED) | ||||
|         event_mode = XIRejectTouch; | ||||
|       else | ||||
|     case META_SEQUENCE_NONE: | ||||
|     case META_SEQUENCE_PENDING_END: | ||||
|       return; | ||||
|     case META_SEQUENCE_ACCEPTED: | ||||
|       meta_display_cancel_touch (display); | ||||
|  | ||||
|       XIAllowTouchEvents (meta_backend_x11_get_xdisplay (backend), | ||||
|                           META_VIRTUAL_CORE_POINTER_ID, | ||||
|                           clutter_x11_event_sequence_get_touch_detail (sequence), | ||||
|                           DefaultRootWindow (display->x11_display->xdisplay), event_mode); | ||||
|       /* Intentional fall-through */ | ||||
|     case META_SEQUENCE_REJECTED: | ||||
|       { | ||||
|         MetaBackend *backend; | ||||
|  | ||||
|         backend = meta_get_backend (); | ||||
|         meta_backend_finish_touch_sequence (backend, sequence, state); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -920,10 +917,6 @@ meta_display_close (MetaDisplay *display, | ||||
|  | ||||
|   g_clear_object (&display->gesture_tracker); | ||||
|  | ||||
|   g_clear_pointer (&display->stack, meta_stack_free); | ||||
|   g_clear_pointer (&display->stack_tracker, | ||||
|                    meta_stack_tracker_free); | ||||
|  | ||||
|   if (display->focus_timeout_id) | ||||
|     g_source_remove (display->focus_timeout_id); | ||||
|   display->focus_timeout_id = 0; | ||||
| @@ -940,12 +933,6 @@ meta_display_close (MetaDisplay *display, | ||||
|   /* Stop caring about events */ | ||||
|   meta_display_free_events (display); | ||||
|  | ||||
|   /* Must be after all calls to meta_window_unmanage() since they | ||||
|    * unregister windows | ||||
|    */ | ||||
|   g_hash_table_destroy (display->wayland_windows); | ||||
|   g_hash_table_destroy (display->stamps); | ||||
|  | ||||
|   if (display->compositor) | ||||
|     meta_compositor_destroy (display->compositor); | ||||
|  | ||||
| @@ -956,6 +943,16 @@ meta_display_close (MetaDisplay *display, | ||||
|       g_clear_object (&display->x11_display); | ||||
|     } | ||||
|  | ||||
|   /* Must be after all calls to meta_window_unmanage() since they | ||||
|    * unregister windows | ||||
|    */ | ||||
|   g_hash_table_destroy (display->wayland_windows); | ||||
|   g_hash_table_destroy (display->stamps); | ||||
|  | ||||
|   g_clear_pointer (&display->stack, meta_stack_free); | ||||
|   g_clear_pointer (&display->stack_tracker, | ||||
|                    meta_stack_tracker_free); | ||||
|  | ||||
|   meta_display_shutdown_keys (display); | ||||
|  | ||||
|   g_clear_object (&display->bell); | ||||
|   | ||||
| @@ -26,6 +26,7 @@ | ||||
|  | ||||
| #include <glib-object.h> | ||||
|  | ||||
| #include "backends/meta-backend-private.h" | ||||
| #include "clutter/clutter.h" | ||||
| #include "meta/window.h" | ||||
|  | ||||
| @@ -39,14 +40,6 @@ | ||||
| typedef struct _MetaGestureTracker MetaGestureTracker; | ||||
| typedef struct _MetaGestureTrackerClass MetaGestureTrackerClass; | ||||
|  | ||||
| typedef enum | ||||
| { | ||||
|   META_SEQUENCE_NONE, | ||||
|   META_SEQUENCE_ACCEPTED, | ||||
|   META_SEQUENCE_REJECTED, | ||||
|   META_SEQUENCE_PENDING_END | ||||
| } MetaSequenceState; | ||||
|  | ||||
| struct _MetaGestureTracker | ||||
| { | ||||
|   GObject parent_instance; | ||||
|   | ||||
| @@ -110,13 +110,24 @@ static void | ||||
| meta_launch_context_constructed (GObject *object) | ||||
| { | ||||
|   MetaLaunchContext *context = META_LAUNCH_CONTEXT (object); | ||||
|   const char *x11_display, *wayland_display; | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_launch_context_parent_class)->constructed (object); | ||||
|  | ||||
|   x11_display = getenv ("DISPLAY"); | ||||
|   wayland_display = getenv ("WAYLAND_DISPLAY"); | ||||
|  | ||||
|   if (x11_display) | ||||
|     { | ||||
|       g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context), | ||||
|                                "DISPLAY", getenv ("DISPLAY")); | ||||
|                                    "DISPLAY", x11_display); | ||||
|     } | ||||
|  | ||||
|   if (wayland_display) | ||||
|     { | ||||
|       g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context), | ||||
|                                "WAYLAND_DISPLAY", getenv ("WAYLAND_DISPLAY")); | ||||
|                                    "WAYLAND_DISPLAY", wayland_display); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static gchar * | ||||
|   | ||||
| @@ -1194,6 +1194,27 @@ window_contains_point (MetaWindow *window, | ||||
|   return POINT_IN_RECT (root_x, root_y, rect); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| window_can_get_default_focus (MetaWindow *window) | ||||
| { | ||||
|   if (window->unmaps_pending > 0) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (window->unmanaging) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (!meta_window_is_focusable (window)) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (!meta_window_should_be_showing (window)) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (window->type == META_WINDOW_DOCK) | ||||
|     return FALSE; | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static MetaWindow* | ||||
| get_default_focus_window (MetaStack     *stack, | ||||
|                           MetaWorkspace *workspace, | ||||
| @@ -1221,24 +1242,12 @@ get_default_focus_window (MetaStack     *stack, | ||||
|       if (window == not_this_one) | ||||
|         continue; | ||||
|  | ||||
|       if (window->unmaps_pending > 0) | ||||
|         continue; | ||||
|  | ||||
|       if (window->unmanaging) | ||||
|         continue; | ||||
|  | ||||
|       if (!meta_window_is_focusable (window)) | ||||
|         continue; | ||||
|  | ||||
|       if (!meta_window_should_be_showing (window)) | ||||
|       if (!window_can_get_default_focus (window)) | ||||
|         continue; | ||||
|  | ||||
|       if (must_be_at_point && !window_contains_point (window, root_x, root_y)) | ||||
|         continue; | ||||
|  | ||||
|       if (window->type == META_WINDOW_DOCK) | ||||
|         continue; | ||||
|  | ||||
|       return window; | ||||
|     } | ||||
|  | ||||
| @@ -1293,6 +1302,26 @@ meta_stack_list_windows (MetaStack     *stack, | ||||
|   return workspace_windows; | ||||
| } | ||||
|  | ||||
| GList * | ||||
| meta_stack_get_default_focus_candidates (MetaStack     *stack, | ||||
|                                          MetaWorkspace *workspace) | ||||
| { | ||||
|   GList *windows = meta_stack_list_windows (stack, workspace); | ||||
|   GList *l; | ||||
|  | ||||
|   for (l = windows; l;) | ||||
|     { | ||||
|       GList *next = l->next; | ||||
|  | ||||
|       if (!window_can_get_default_focus (l->data)) | ||||
|         windows = g_list_delete_link (windows, l); | ||||
|  | ||||
|       l = next; | ||||
|     } | ||||
|  | ||||
|   return windows; | ||||
| } | ||||
|  | ||||
| int | ||||
| meta_stack_windows_cmp  (MetaStack  *stack, | ||||
|                          MetaWindow *window_a, | ||||
|   | ||||
| @@ -337,6 +337,21 @@ MetaWindow* meta_stack_get_default_focus_window_at_point (MetaStack     *stack, | ||||
|                                                           int            root_x, | ||||
|                                                           int            root_y); | ||||
|  | ||||
| /** | ||||
|  * meta_stack_get_default_focus_candidates: | ||||
|  * @stack: The stack to examine. | ||||
|  * @workspace: If not %NULL, only windows on this workspace will be | ||||
|  *             returned; otherwise all windows in the stack will be | ||||
|  *             returned. | ||||
|  * | ||||
|  * Returns all the focus candidate windows in the stack, in order. | ||||
|  * | ||||
|  * Returns: (transfer container) (element-type Meta.Window): | ||||
|  *     A #GList of #MetaWindow, in stacking order, honouring layers. | ||||
|  */ | ||||
| GList *     meta_stack_get_default_focus_candidates (MetaStack     *stack, | ||||
|                                                      MetaWorkspace *workspace); | ||||
|  | ||||
| /** | ||||
|  * meta_stack_list_windows: | ||||
|  * @stack: The stack to examine. | ||||
|   | ||||
| @@ -3683,6 +3683,13 @@ meta_window_activate_full (MetaWindow     *window, | ||||
| { | ||||
|   MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; | ||||
|   gboolean allow_workspace_switch; | ||||
|  | ||||
|   if (window->unmanaging) | ||||
|     { | ||||
|       g_warning ("Trying to activate unmanaged window '%s'", window->desc); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   meta_topic (META_DEBUG_FOCUS, | ||||
|               "_NET_ACTIVE_WINDOW message sent for %s at time %u " | ||||
|               "by client type %u.\n", | ||||
| @@ -8562,6 +8569,8 @@ meta_window_shortcuts_inhibited (MetaWindow         *window, | ||||
| gboolean | ||||
| meta_window_is_focusable (MetaWindow *window) | ||||
| { | ||||
|   g_return_val_if_fail (!window->unmanaging, FALSE); | ||||
|  | ||||
|   return META_WINDOW_GET_CLASS (window)->is_focusable (window); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -85,6 +85,12 @@ typedef struct _MetaWorkspaceLogicalMonitorData | ||||
|   MetaRectangle logical_monitor_work_area; | ||||
| } MetaWorkspaceLogicalMonitorData; | ||||
|  | ||||
| typedef struct _MetaWorkspaceFocusableAncestorData | ||||
| { | ||||
|   MetaWorkspace *workspace; | ||||
|   MetaWindow *out_window; | ||||
| } MetaWorkspaceFocusableAncestorData; | ||||
|  | ||||
| static MetaWorkspaceLogicalMonitorData * | ||||
| meta_workspace_get_logical_monitor_data (MetaWorkspace      *workspace, | ||||
|                                          MetaLogicalMonitor *logical_monitor) | ||||
| @@ -1322,13 +1328,20 @@ meta_workspace_focus_default_window (MetaWorkspace *workspace, | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| record_ancestor (MetaWindow *window, | ||||
|                  void       *data) | ||||
| find_focusable_ancestor (MetaWindow *window, | ||||
|                          gpointer    user_data) | ||||
| { | ||||
|   MetaWindow **result = data; | ||||
|   MetaWorkspaceFocusableAncestorData *data = user_data; | ||||
|  | ||||
|   *result = window; | ||||
|   return FALSE; /* quit with the first ancestor we find */ | ||||
|   if (!window->unmanaging && meta_window_is_focusable (window) && | ||||
|       meta_window_located_on_workspace (window, data->workspace) && | ||||
|       meta_window_showing_on_its_workspace (window)) | ||||
|     { | ||||
|       data->out_window = window; | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| /* Focus ancestor of not_this_one if there is one */ | ||||
| @@ -1350,11 +1363,15 @@ focus_ancestor_or_top_window (MetaWorkspace *workspace, | ||||
|   if (not_this_one) | ||||
|     { | ||||
|       MetaWindow *ancestor; | ||||
|       ancestor = NULL; | ||||
|       meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor); | ||||
|       if (ancestor != NULL && | ||||
|           meta_window_located_on_workspace (ancestor, workspace) && | ||||
|           meta_window_showing_on_its_workspace (ancestor)) | ||||
|       MetaWorkspaceFocusableAncestorData data; | ||||
|  | ||||
|       data = (MetaWorkspaceFocusableAncestorData) { | ||||
|         .workspace = workspace, | ||||
|       }; | ||||
|       meta_window_foreach_ancestor (not_this_one, find_focusable_ancestor, &data); | ||||
|       ancestor = data.out_window; | ||||
|  | ||||
|       if (ancestor) | ||||
|         { | ||||
|           meta_topic (META_DEBUG_FOCUS, | ||||
|                       "Focusing %s, ancestor of %s\n", | ||||
|   | ||||
| @@ -32,6 +32,7 @@ | ||||
| #include "wayland/meta-wayland.h" | ||||
|  | ||||
| #define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) | ||||
| #define FRAME_WARNING "Frame has assigned frame counter but no frame drawn time" | ||||
|  | ||||
| static gboolean | ||||
| run_tests (gpointer data) | ||||
| @@ -40,6 +41,8 @@ run_tests (gpointer data) | ||||
|   MetaSettings *settings = meta_backend_get_settings (backend); | ||||
|   gboolean ret; | ||||
|  | ||||
|   g_test_log_set_fatal_handler (NULL, NULL); | ||||
|  | ||||
|   meta_settings_override_experimental_features (settings); | ||||
|  | ||||
|   meta_settings_enable_experimental_feature ( | ||||
| @@ -53,6 +56,20 @@ run_tests (gpointer data) | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| ignore_frame_counter_warning (const gchar    *log_domain, | ||||
|                               GLogLevelFlags  log_level, | ||||
|                               const gchar    *message, | ||||
|                               gpointer        user_data) | ||||
| { | ||||
|   if ((log_level & G_LOG_LEVEL_WARNING) && | ||||
|       g_strcmp0 (log_domain, "mutter") == 0 && | ||||
|       g_str_has_suffix (message, FRAME_WARNING)) | ||||
|     return FALSE; | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_test_headless_start (void) | ||||
| { | ||||
| @@ -193,6 +210,8 @@ main (int argc, char *argv[]) | ||||
|   meta_init (); | ||||
|   meta_register_with_session (); | ||||
|  | ||||
|   g_test_log_set_fatal_handler (ignore_frame_counter_warning, NULL); | ||||
|  | ||||
|   g_idle_add (run_tests, NULL); | ||||
|  | ||||
|   return meta_run (); | ||||
|   | ||||
| @@ -38,6 +38,7 @@ test_client = executable('mutter-test-client', | ||||
|   dependencies: [ | ||||
|     gtk3_dep, | ||||
|     gio_unix_dep, | ||||
|     x11_dep, | ||||
|     xext_dep, | ||||
|   ], | ||||
|   install: have_installed_tests, | ||||
| @@ -104,6 +105,13 @@ headless_start_test = executable('mutter-headless-start-test', | ||||
| stacking_tests = files([ | ||||
|   'stacking/basic-x11.metatest', | ||||
|   'stacking/basic-wayland.metatest', | ||||
|   'stacking/closed-transient-no-input-no-take-focus-parent.metatest', | ||||
|   'stacking/closed-transient-no-input-no-take-focus-parents.metatest', | ||||
|   'stacking/closed-transient-no-input-parent.metatest', | ||||
|   'stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatest', | ||||
|   'stacking/closed-transient-no-input-parents.metatest', | ||||
|   'stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatest', | ||||
|   'stacking/closed-transient-only-take-focus-parents.metatest', | ||||
|   'stacking/minimized.metatest', | ||||
|   'stacking/mixed-windows.metatest', | ||||
|   'stacking/set-parent.metatest', | ||||
|   | ||||
| @@ -0,0 +1,23 @@ | ||||
| new_client 1 x11 | ||||
| create 1/1 | ||||
| show 1/1 | ||||
|  | ||||
| create 1/2 csd | ||||
| set_parent 1/2 1 | ||||
| can_take_focus 1/2 false | ||||
| accept_focus 1/2 false | ||||
| show 1/2 | ||||
|  | ||||
| create 1/3 csd | ||||
| set_parent 1/3 2 | ||||
| show 1/3 | ||||
|  | ||||
| wait | ||||
| assert_focused 1/3 | ||||
| assert_stacking 1/1 1/2 1/3 | ||||
|  | ||||
| destroy 1/3 | ||||
|  | ||||
| wait | ||||
| assert_focused 1/1 | ||||
| assert_stacking 1/1 1/2 | ||||
| @@ -0,0 +1,30 @@ | ||||
| new_client 2 x11 | ||||
| create 2/1 | ||||
| show 2/1 | ||||
| wait | ||||
|  | ||||
| new_client 1 x11 | ||||
| create 1/1 | ||||
| accept_focus 1/1 false | ||||
| can_take_focus 1/1 false | ||||
| show 1/1 | ||||
|  | ||||
| create 1/2 csd | ||||
| set_parent 1/2 1 | ||||
| can_take_focus 1/2 false | ||||
| accept_focus 1/2 false | ||||
| show 1/2 | ||||
|  | ||||
| create 1/3 csd | ||||
| set_parent 1/3 2 | ||||
| show 1/3 | ||||
|  | ||||
| wait | ||||
| assert_focused 1/3 | ||||
| assert_stacking 2/1 1/1 1/2 1/3 | ||||
|  | ||||
| destroy 1/3 | ||||
|  | ||||
| wait | ||||
| assert_stacking 1/1 1/2 2/1 | ||||
| assert_focused 2/1 | ||||
| @@ -0,0 +1,36 @@ | ||||
| new_client 2 x11 | ||||
| create 2/1 | ||||
| show 2/1 | ||||
|  | ||||
| new_client 1 x11 | ||||
| create 1/1 | ||||
| show 1/1 | ||||
|  | ||||
| create 1/2 csd | ||||
| set_parent 1/2 1 | ||||
| accept_focus 1/2 false | ||||
| show 1/2 | ||||
|  | ||||
| create 1/3 csd | ||||
| set_parent 1/3 2 | ||||
| show 1/3 | ||||
|  | ||||
| wait | ||||
| assert_focused 1/3 | ||||
| assert_stacking 2/1 1/1 1/2 1/3 | ||||
|  | ||||
| destroy 1/3 | ||||
| dispatch | ||||
|  | ||||
| assert_focused none | ||||
| assert_stacking 2/1 1/1 1/2 | ||||
|  | ||||
| activate 2/1 | ||||
| wait | ||||
|  | ||||
| assert_focused 2/1 | ||||
| assert_stacking 1/1 1/2 2/1 | ||||
|  | ||||
| sleep 250 | ||||
| assert_focused 2/1 | ||||
| assert_stacking 1/1 1/2 2/1 | ||||
							
								
								
									
										30
									
								
								src/tests/stacking/closed-transient-no-input-parent.metatest
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/tests/stacking/closed-transient-no-input-parent.metatest
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| new_client 2 x11 | ||||
| create 2/1 | ||||
| show 2/1 | ||||
|  | ||||
| new_client 1 x11 | ||||
| create 1/1 | ||||
| show 1/1 | ||||
|  | ||||
| create 1/2 csd | ||||
| set_parent 1/2 1 | ||||
| accept_focus 1/2 false | ||||
| show 1/2 | ||||
|  | ||||
| create 1/3 csd | ||||
| set_parent 1/3 2 | ||||
| show 1/3 | ||||
|  | ||||
| wait | ||||
| assert_focused 1/3 | ||||
| assert_stacking 2/1 1/1 1/2 1/3 | ||||
|  | ||||
| destroy 1/3 | ||||
| dispatch | ||||
|  | ||||
| assert_focused none | ||||
| assert_stacking 2/1 1/1 1/2 | ||||
|  | ||||
| sleep 150 | ||||
| assert_focused 1/1 | ||||
| assert_stacking 2/1 1/1 1/2 | ||||
| @@ -0,0 +1,43 @@ | ||||
| new_client 0 x11 | ||||
| create 0/1 | ||||
| show 0/1 | ||||
|  | ||||
| new_client 1 x11 | ||||
| create 1/1 | ||||
| show 1/1 | ||||
|  | ||||
| create 1/2 csd | ||||
| set_parent 1/2 1 | ||||
| accept_focus 1/2 false | ||||
| show 1/2 | ||||
|  | ||||
| create 1/3 csd | ||||
| set_parent 1/3 2 | ||||
| accept_focus 1/3 false | ||||
| show 1/3 | ||||
|  | ||||
| create 1/4 csd | ||||
| set_parent 1/4 3 | ||||
| accept_focus 1/4 false | ||||
| show 1/4 | ||||
|  | ||||
| create 1/5 csd | ||||
| set_parent 1/5 3 | ||||
| show 1/5 | ||||
|  | ||||
| wait | ||||
| assert_focused 1/5 | ||||
| assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5 | ||||
|  | ||||
| destroy 1/5 | ||||
| dispatch | ||||
|  | ||||
| assert_focused none | ||||
| assert_stacking 0/1 1/1 1/2 1/3 1/4 | ||||
|  | ||||
| destroy 1/2 | ||||
| dispatch | ||||
|  | ||||
| sleep 450 | ||||
| assert_focused 1/1 | ||||
| assert_stacking 0/1 1/1 1/3 1/4 | ||||
| @@ -0,0 +1,46 @@ | ||||
| new_client 0 x11 | ||||
| create 0/1 | ||||
| show 0/1 | ||||
|  | ||||
| new_client 1 x11 | ||||
| create 1/1 | ||||
| show 1/1 | ||||
|  | ||||
| create 1/2 csd | ||||
| set_parent 1/2 1 | ||||
| accept_focus 1/2 false | ||||
| show 1/2 | ||||
|  | ||||
| create 1/3 csd | ||||
| set_parent 1/3 2 | ||||
| accept_focus 1/3 false | ||||
| show 1/3 | ||||
|  | ||||
| create 1/4 csd | ||||
| set_parent 1/4 3 | ||||
| accept_focus 1/4 false | ||||
| show 1/4 | ||||
|  | ||||
| create 1/5 csd | ||||
| set_parent 1/5 3 | ||||
| show 1/5 | ||||
|  | ||||
| wait | ||||
| assert_focused 1/5 | ||||
| assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5 | ||||
|  | ||||
| destroy 1/5 | ||||
| dispatch | ||||
|  | ||||
| assert_focused none | ||||
| assert_stacking 0/1 1/1 1/2 1/3 1/4 | ||||
|  | ||||
| sleep 600 | ||||
| assert_focused 1/1 | ||||
| assert_stacking 0/1 1/1 1/2 1/3 1/4 | ||||
|  | ||||
| destroy 1/3 | ||||
| wait | ||||
|  | ||||
| assert_focused 1/1 | ||||
| assert_stacking 0/1 1/1 1/2 1/4 | ||||
| @@ -0,0 +1,34 @@ | ||||
| new_client 0 x11 | ||||
| create 0/1 | ||||
| show 0/1 | ||||
|  | ||||
| new_client 1 x11 | ||||
| create 1/1 | ||||
| accept_focus 1/1 false | ||||
| can_take_focus 1/1 true | ||||
| accept_take_focus 1/1 true | ||||
| show 1/1 | ||||
|  | ||||
| create 1/2 csd | ||||
| set_parent 1/2 1 | ||||
| accept_focus 1/2 false | ||||
| can_take_focus 1/2 true | ||||
| accept_take_focus 1/2 true | ||||
| show 1/2 | ||||
|  | ||||
| create 1/3 | ||||
| set_parent 1/3 2 | ||||
| show 1/3 | ||||
|  | ||||
| assert_focused 1/3 | ||||
| assert_stacking 0/1 1/1 1/2 1/3 | ||||
|  | ||||
| destroy 1/3 | ||||
| wait | ||||
|  | ||||
| assert_focused 1/2 | ||||
| assert_stacking 0/1 1/1 1/2 | ||||
|  | ||||
| sleep 150 | ||||
| assert_focused 1/2 | ||||
| assert_stacking 0/1 1/1 1/2 | ||||
| @@ -31,6 +31,11 @@ | ||||
| const char *client_id = "0"; | ||||
| static gboolean wayland; | ||||
| GHashTable *windows; | ||||
| GQuark event_source_quark; | ||||
| GQuark event_handlers_quark; | ||||
| GQuark can_take_focus_quark; | ||||
|  | ||||
| typedef void (*XEventHandler) (GtkWidget *window, XEvent *event); | ||||
|  | ||||
| static void read_next_line (GDataInputStream *in); | ||||
|  | ||||
| @@ -57,6 +62,186 @@ lookup_window (const char *window_id) | ||||
|   return window; | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|   GSource base; | ||||
|   GSource **self_ref; | ||||
|   GPollFD event_poll_fd; | ||||
|   Display *xdisplay; | ||||
| } XClientEventSource; | ||||
|  | ||||
| static gboolean | ||||
| x_event_source_prepare (GSource *source, | ||||
|                         int     *timeout) | ||||
| { | ||||
|   XClientEventSource *x_source = (XClientEventSource *) source; | ||||
|  | ||||
|   *timeout = -1; | ||||
|  | ||||
|   return XPending (x_source->xdisplay); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| x_event_source_check (GSource *source) | ||||
| { | ||||
|   XClientEventSource *x_source = (XClientEventSource *) source; | ||||
|  | ||||
|   return XPending (x_source->xdisplay); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| x_event_source_dispatch (GSource     *source, | ||||
|                          GSourceFunc  callback, | ||||
|                          gpointer     user_data) | ||||
| { | ||||
|   XClientEventSource *x_source = (XClientEventSource *) source; | ||||
|  | ||||
|   while (XPending (x_source->xdisplay)) | ||||
|     { | ||||
|       GHashTableIter iter; | ||||
|       XEvent event; | ||||
|       gpointer value; | ||||
|  | ||||
|       XNextEvent (x_source->xdisplay, &event); | ||||
|  | ||||
|       g_hash_table_iter_init (&iter, windows); | ||||
|       while (g_hash_table_iter_next (&iter, NULL, &value)) | ||||
|         { | ||||
|           GList *l; | ||||
|           GtkWidget *window = value; | ||||
|           GList *handlers = | ||||
|             g_object_get_qdata (G_OBJECT (window), event_handlers_quark); | ||||
|  | ||||
|           for (l = handlers; l; l = l->next) | ||||
|             { | ||||
|               XEventHandler handler = l->data; | ||||
|               handler (window, &event); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| x_event_source_finalize (GSource *source) | ||||
| { | ||||
|   XClientEventSource *x_source = (XClientEventSource *) source; | ||||
|  | ||||
|   *x_source->self_ref = NULL; | ||||
| } | ||||
|  | ||||
| static GSourceFuncs x_event_funcs = { | ||||
|   x_event_source_prepare, | ||||
|   x_event_source_check, | ||||
|   x_event_source_dispatch, | ||||
|   x_event_source_finalize, | ||||
| }; | ||||
|  | ||||
| static GSource* | ||||
| ensure_xsource_handler (GdkDisplay *gdkdisplay) | ||||
| { | ||||
|   static GSource *source = NULL; | ||||
|   Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay); | ||||
|   XClientEventSource *x_source; | ||||
|  | ||||
|   if (source) | ||||
|     return g_source_ref (source); | ||||
|  | ||||
|   source = g_source_new (&x_event_funcs, sizeof (XClientEventSource)); | ||||
|   x_source = (XClientEventSource *) source; | ||||
|   x_source->self_ref = &source; | ||||
|   x_source->xdisplay = xdisplay; | ||||
|   x_source->event_poll_fd.fd = ConnectionNumber (xdisplay); | ||||
|   x_source->event_poll_fd.events = G_IO_IN; | ||||
|   g_source_add_poll (source, &x_source->event_poll_fd); | ||||
|  | ||||
|   g_source_set_priority (source, GDK_PRIORITY_EVENTS - 1); | ||||
|   g_source_set_can_recurse (source, TRUE); | ||||
|   g_source_attach (source, NULL); | ||||
|  | ||||
|   return source; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| window_has_x11_event_handler (GtkWidget     *window, | ||||
|                               XEventHandler  handler) | ||||
| { | ||||
|   GList *handlers = | ||||
|     g_object_get_qdata (G_OBJECT (window), event_handlers_quark); | ||||
|  | ||||
|   g_return_val_if_fail (handler, FALSE); | ||||
|   g_return_val_if_fail (!wayland, FALSE); | ||||
|  | ||||
|   return g_list_find (handlers, handler) != NULL; | ||||
| } | ||||
|  | ||||
| static void | ||||
| unref_and_maybe_destroy_gsource (GSource *source) | ||||
| { | ||||
|   g_source_unref (source); | ||||
|  | ||||
|   if (source->ref_count == 1) | ||||
|     g_source_destroy (source); | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_add_x11_event_handler (GtkWidget     *window, | ||||
|                               XEventHandler  handler) | ||||
| { | ||||
|   GSource *source; | ||||
|   GList *handlers = | ||||
|     g_object_get_qdata (G_OBJECT (window), event_handlers_quark); | ||||
|  | ||||
|   g_return_if_fail (!window_has_x11_event_handler (window, handler)); | ||||
|  | ||||
|   source = ensure_xsource_handler (gtk_widget_get_display (window)); | ||||
|   g_object_set_qdata_full (G_OBJECT (window), event_source_quark, source, | ||||
|                            (GDestroyNotify) unref_and_maybe_destroy_gsource); | ||||
|  | ||||
|   handlers = g_list_append (handlers, handler); | ||||
|   g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers); | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_remove_x11_event_handler (GtkWidget     *window, | ||||
|                                  XEventHandler  handler) | ||||
| { | ||||
|   GList *handlers = | ||||
|     g_object_get_qdata (G_OBJECT (window), event_handlers_quark); | ||||
|  | ||||
|   g_return_if_fail (window_has_x11_event_handler (window, handler)); | ||||
|  | ||||
|   g_object_set_qdata (G_OBJECT (window), event_source_quark, NULL); | ||||
|  | ||||
|   handlers = g_list_remove (handlers, handler); | ||||
|   g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_take_focus (GtkWidget *window, | ||||
|                    XEvent    *xevent) | ||||
| { | ||||
|   GdkWindow *gdkwindow = gtk_widget_get_window (window); | ||||
|   GdkDisplay *display = gtk_widget_get_display (window); | ||||
|   Atom wm_protocols = | ||||
|     gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS"); | ||||
|   Atom wm_take_focus = | ||||
|     gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"); | ||||
|  | ||||
|   if (xevent->xany.type != ClientMessage || | ||||
|       xevent->xany.window != GDK_WINDOW_XID (gdkwindow)) | ||||
|     return; | ||||
|  | ||||
|   if (xevent->xclient.message_type == wm_protocols && | ||||
|       xevent->xclient.data.l[0] == wm_take_focus) | ||||
|     { | ||||
|       XSetInputFocus (xevent->xany.display, | ||||
|                       GDK_WINDOW_XID (gdkwindow), | ||||
|                       RevertToParent, | ||||
|                       xevent->xclient.data.l[1]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| process_line (const char *line) | ||||
| { | ||||
| @@ -125,6 +310,9 @@ process_line (const char *line) | ||||
|       gtk_window_set_title (GTK_WINDOW (window), title); | ||||
|       g_free (title); | ||||
|  | ||||
|       g_object_set_qdata (G_OBJECT (window), can_take_focus_quark, | ||||
|                           GUINT_TO_POINTER (TRUE)); | ||||
|  | ||||
|       gtk_widget_realize (window); | ||||
|  | ||||
|       if (!wayland) | ||||
| @@ -196,6 +384,130 @@ process_line (const char *line) | ||||
|                                              NULL)) | ||||
|         g_print ("Fail to export handle for window id %s", argv[2]); | ||||
|     } | ||||
|   else if (strcmp (argv[0], "accept_focus") == 0) | ||||
|     { | ||||
|       if (argc != 3) | ||||
|         { | ||||
|           g_print ("usage: %s <window-id> [true|false]", argv[0]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       GtkWidget *window = lookup_window (argv[1]); | ||||
|       if (!window) | ||||
|         { | ||||
|           g_print ("unknown window %s", argv[1]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       if (!wayland && | ||||
|           window_has_x11_event_handler (window, handle_take_focus)) | ||||
|         { | ||||
|           g_print ("Impossible to use %s for windows accepting take focus", | ||||
|                    argv[1]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0; | ||||
|       gtk_window_set_accept_focus (GTK_WINDOW (window), enabled); | ||||
|     } | ||||
|   else if (strcmp (argv[0], "can_take_focus") == 0) | ||||
|     { | ||||
|       if (argc != 3) | ||||
|         { | ||||
|           g_print ("usage: %s <window-id> [true|false]", argv[0]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       GtkWidget *window = lookup_window (argv[1]); | ||||
|       if (!window) | ||||
|         { | ||||
|           g_print ("unknown window %s", argv[1]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       if (wayland) | ||||
|         { | ||||
|           g_print ("%s not supported under wayland", argv[0]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       if (window_has_x11_event_handler (window, handle_take_focus)) | ||||
|         { | ||||
|           g_print ("Impossible to change %s for windows accepting take focus", | ||||
|                    argv[1]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       GdkDisplay *display = gdk_display_get_default (); | ||||
|       GdkWindow *gdkwindow = gtk_widget_get_window (window); | ||||
|       Display *xdisplay = gdk_x11_display_get_xdisplay (display); | ||||
|       Window xwindow = GDK_WINDOW_XID (gdkwindow); | ||||
|       Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"); | ||||
|       gboolean add = g_ascii_strcasecmp(argv[2], "true") == 0; | ||||
|       Atom *protocols = NULL; | ||||
|       Atom *new_protocols; | ||||
|       int n_protocols = 0; | ||||
|       int i, n = 0; | ||||
|  | ||||
|       gdk_display_sync (display); | ||||
|       XGetWMProtocols (xdisplay, xwindow, &protocols, &n_protocols); | ||||
|       new_protocols = g_new0 (Atom, n_protocols + (add ? 1 : 0)); | ||||
|  | ||||
|       for (i = 0; i < n_protocols; ++i) | ||||
|         { | ||||
|           if (protocols[i] != wm_take_focus) | ||||
|             new_protocols[n++] = protocols[i]; | ||||
|         } | ||||
|  | ||||
|       if (add) | ||||
|         new_protocols[n++] = wm_take_focus; | ||||
|  | ||||
|       XSetWMProtocols (xdisplay, xwindow, new_protocols, n); | ||||
|       g_object_set_qdata (G_OBJECT (window), can_take_focus_quark, | ||||
|                           GUINT_TO_POINTER (add)); | ||||
|  | ||||
|       XFree (new_protocols); | ||||
|       XFree (protocols); | ||||
|     } | ||||
|   else if (strcmp (argv[0], "accept_take_focus") == 0) | ||||
|     { | ||||
|       if (argc != 3) | ||||
|         { | ||||
|           g_print ("usage: %s <window-id> [true|false]", argv[0]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       GtkWidget *window = lookup_window (argv[1]); | ||||
|       if (!window) | ||||
|         { | ||||
|           g_print ("unknown window %s", argv[1]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       if (wayland) | ||||
|         { | ||||
|           g_print ("%s not supported under wayland", argv[0]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       if (gtk_window_get_accept_focus (GTK_WINDOW (window))) | ||||
|         { | ||||
|           g_print ("%s not supported for input windows", argv[0]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       if (!g_object_get_qdata (G_OBJECT (window), can_take_focus_quark)) | ||||
|         { | ||||
|           g_print ("%s not supported for windows with no WM_TAKE_FOCUS set", | ||||
|                    argv[0]); | ||||
|           goto out; | ||||
|         } | ||||
|  | ||||
|       if (g_ascii_strcasecmp (argv[2], "true") == 0) | ||||
|         window_add_x11_event_handler (window, handle_take_focus); | ||||
|       else | ||||
|         window_remove_x11_event_handler (window, handle_take_focus); | ||||
|     } | ||||
|   else if (strcmp (argv[0], "show") == 0) | ||||
|     { | ||||
|       if (argc != 2) | ||||
| @@ -460,6 +772,9 @@ main(int argc, char **argv) | ||||
|  | ||||
|   windows = g_hash_table_new_full (g_str_hash, g_str_equal, | ||||
|                                    g_free, NULL); | ||||
|   event_source_quark = g_quark_from_static_string ("event-source"); | ||||
|   event_handlers_quark = g_quark_from_static_string ("event-handlers"); | ||||
|   can_take_focus_quark = g_quark_from_static_string ("can-take-focus"); | ||||
|  | ||||
|   GInputStream *raw_in = g_unix_input_stream_new (0, FALSE); | ||||
|   GDataInputStream *in = g_data_input_stream_new (raw_in); | ||||
|   | ||||
| @@ -77,7 +77,7 @@ test_case_new (void) | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| test_case_before_redraw (gpointer data) | ||||
| test_case_loop_quit (gpointer data) | ||||
| { | ||||
|   TestCase *test = data; | ||||
|  | ||||
| @@ -86,6 +86,24 @@ test_case_before_redraw (gpointer data) | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| test_case_dispatch (TestCase *test, | ||||
|                     GError  **error) | ||||
| { | ||||
|   /* Wait until we've done any outstanding queued up work. | ||||
|    * Though we add this as BEFORE_REDRAW, the iteration that runs the | ||||
|    * BEFORE_REDRAW idles will proceed on and do the redraw, so we're | ||||
|    * waiting until after *all* frame processing. | ||||
|    */ | ||||
|   meta_later_add (META_LATER_BEFORE_REDRAW, | ||||
|                   test_case_loop_quit, | ||||
|                   test, | ||||
|                   NULL); | ||||
|   g_main_loop_run (test->loop); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| test_case_wait (TestCase *test, | ||||
|                 GError  **error) | ||||
| @@ -102,16 +120,8 @@ test_case_wait (TestCase *test, | ||||
|     if (!test_client_wait (value, error)) | ||||
|       return FALSE; | ||||
|  | ||||
|   /* Then wait until we've done any outstanding queued up work. | ||||
|    * Though we add this as BEFORE_REDRAW, the iteration that runs the | ||||
|    * BEFORE_REDRAW idles will proceed on and do the redraw, so we're | ||||
|    * waiting until after *all* frame processing. | ||||
|    */ | ||||
|   meta_later_add (META_LATER_BEFORE_REDRAW, | ||||
|                   test_case_before_redraw, | ||||
|                   test, | ||||
|                   NULL); | ||||
|   g_main_loop_run (test->loop); | ||||
|   /* Then wait until we've done any outstanding queued up work. */ | ||||
|   test_case_dispatch (test, error); | ||||
|  | ||||
|   /* Then set an XSync counter ourselves and and wait until | ||||
|    * we receive the resulting event - this makes sure that we've | ||||
| @@ -121,6 +131,17 @@ test_case_wait (TestCase *test, | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| test_case_sleep (TestCase  *test, | ||||
|                  guint32    interval, | ||||
|                  GError   **error) | ||||
| { | ||||
|   g_timeout_add_full (G_PRIORITY_LOW, interval, test_case_loop_quit, test, NULL); | ||||
|   g_main_loop_run (test->loop); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| #define BAD_COMMAND(...)                                                \ | ||||
|   G_STMT_START {                                                        \ | ||||
|       g_set_error (error,                                               \ | ||||
| @@ -237,6 +258,37 @@ test_case_assert_stacking (TestCase *test, | ||||
|   return *error == NULL; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| test_case_assert_focused (TestCase    *test, | ||||
|                           const char  *expected_window, | ||||
|                           GError     **error) | ||||
| { | ||||
|   MetaDisplay *display = meta_get_display (); | ||||
|  | ||||
|   if (!display->focus_window) | ||||
|     { | ||||
|       if (g_strcmp0 (expected_window, "none") != 0) | ||||
|         { | ||||
|           g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED, | ||||
|                        "focus: expected='%s', actual='none'", expected_window); | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       const char *focused = display->focus_window->title; | ||||
|  | ||||
|       if (g_str_has_prefix (focused, "test/")) | ||||
|         focused += 5; | ||||
|  | ||||
|       if (g_strcmp0 (focused, expected_window) != 0) | ||||
|         g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED, | ||||
|                      "focus: expected='%s', actual='%s'", | ||||
|                      expected_window, focused); | ||||
|     } | ||||
|  | ||||
|   return *error == NULL; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| test_case_check_xserver_stacking (TestCase *test, | ||||
|                                   GError  **error) | ||||
| @@ -385,6 +437,9 @@ test_case_do (TestCase *test, | ||||
|                            argc == 3 ? argv[2] : NULL, | ||||
|                            NULL)) | ||||
|         return FALSE; | ||||
|  | ||||
|       if (!test_client_wait (client, error)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "set_parent") == 0 || | ||||
|            strcmp (argv[0], "set_parent_exported") == 0) | ||||
| @@ -398,6 +453,63 @@ test_case_do (TestCase *test, | ||||
|       if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) | ||||
|         return FALSE; | ||||
|  | ||||
|       if (!test_client_do (client, error, | ||||
|                            argv[0], window_id, | ||||
|                            argv[2], | ||||
|                            NULL)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "accept_focus") == 0) | ||||
|     { | ||||
|       if (argc != 3 || | ||||
|           (g_ascii_strcasecmp (argv[2], "true") != 0 && | ||||
|            g_ascii_strcasecmp (argv[2], "false") != 0)) | ||||
|         BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]", | ||||
|                     argv[0]); | ||||
|  | ||||
|       TestClient *client; | ||||
|       const char *window_id; | ||||
|       if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) | ||||
|         return FALSE; | ||||
|  | ||||
|       if (!test_client_do (client, error, | ||||
|                            argv[0], window_id, | ||||
|                            argv[2], | ||||
|                            NULL)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "can_take_focus") == 0) | ||||
|     { | ||||
|       if (argc != 3 || | ||||
|           (g_ascii_strcasecmp (argv[2], "true") != 0 && | ||||
|            g_ascii_strcasecmp (argv[2], "false") != 0)) | ||||
|         BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]", | ||||
|                     argv[0]); | ||||
|  | ||||
|       TestClient *client; | ||||
|       const char *window_id; | ||||
|       if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) | ||||
|         return FALSE; | ||||
|  | ||||
|       if (!test_client_do (client, error, | ||||
|                            argv[0], window_id, | ||||
|                            argv[2], | ||||
|                            NULL)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "accept_take_focus") == 0) | ||||
|     { | ||||
|       if (argc != 3 || | ||||
|           (g_ascii_strcasecmp (argv[2], "true") != 0 && | ||||
|            g_ascii_strcasecmp (argv[2], "false") != 0)) | ||||
|         BAD_COMMAND("usage: %s <client-id>/<window-id> [true|false]", | ||||
|                     argv[0]); | ||||
|  | ||||
|       TestClient *client; | ||||
|       const char *window_id; | ||||
|       if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) | ||||
|         return FALSE; | ||||
|  | ||||
|       if (!test_client_do (client, error, | ||||
|                            argv[0], window_id, | ||||
|                            argv[2], | ||||
| @@ -477,6 +589,28 @@ test_case_do (TestCase *test, | ||||
|       if (!test_case_wait (test, error)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "dispatch") == 0) | ||||
|     { | ||||
|       if (argc != 1) | ||||
|         BAD_COMMAND("usage: %s", argv[0]); | ||||
|  | ||||
|       if (!test_case_dispatch (test, error)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "sleep") == 0) | ||||
|     { | ||||
|       guint64 interval; | ||||
|  | ||||
|       if (argc != 2) | ||||
|         BAD_COMMAND("usage: %s <milliseconds>", argv[0]); | ||||
|  | ||||
|       if (!g_ascii_string_to_unsigned (argv[1], 10, 0, G_MAXUINT32, | ||||
|                                        &interval, error)) | ||||
|         return FALSE; | ||||
|  | ||||
|       if (!test_case_sleep (test, (guint32) interval, error)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "assert_stacking") == 0) | ||||
|     { | ||||
|       if (!test_case_assert_stacking (test, argv + 1, argc - 1, error)) | ||||
| @@ -485,6 +619,11 @@ test_case_do (TestCase *test, | ||||
|       if (!test_case_check_xserver_stacking (test, error)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else if (strcmp (argv[0], "assert_focused") == 0) | ||||
|     { | ||||
|       if (!test_case_assert_focused (test, argv[1], error)) | ||||
|         return FALSE; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       BAD_COMMAND("Unknown command %s", argv[0]); | ||||
|   | ||||
| @@ -170,8 +170,9 @@ meta_wayland_cursor_surface_commit (MetaWaylandSurfaceRole  *surface_role, | ||||
|   wl_list_init (&pending->frame_callback_list); | ||||
|  | ||||
|   if (pending->newly_attached && | ||||
|       (!cairo_region_is_empty (pending->surface_damage) || | ||||
|        !cairo_region_is_empty (pending->buffer_damage))) | ||||
|       ((!cairo_region_is_empty (pending->surface_damage) || | ||||
|         !cairo_region_is_empty (pending->buffer_damage)) || | ||||
|        !priv->buffer)) | ||||
|     update_cursor_sprite_texture (META_WAYLAND_CURSOR_SURFACE (surface_role)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -266,7 +266,7 @@ meta_wayland_seat_free (MetaWaylandSeat *seat) | ||||
|   meta_wayland_gtk_text_input_destroy (seat->gtk_text_input); | ||||
|   meta_wayland_text_input_destroy (seat->text_input); | ||||
|  | ||||
|   g_slice_free (MetaWaylandSeat, seat); | ||||
|   g_free (seat); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
|   | ||||
| @@ -737,6 +737,10 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface      *surface, | ||||
|               g_clear_pointer (&snippet, cogl_object_unref); | ||||
|             } | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           cogl_clear_object (&surface->texture); | ||||
|         } | ||||
|  | ||||
|       /* If the newly attached buffer is going to be accessed directly without | ||||
|        * making a copy, such as an EGL buffer, mark it as in-use don't release | ||||
|   | ||||
| @@ -50,6 +50,8 @@ | ||||
| #include "x11/window-props.h" | ||||
| #include "x11/xprops.h" | ||||
|  | ||||
| #define TAKE_FOCUS_FALLBACK_DELAY_MS 150 | ||||
|  | ||||
| enum _MetaGtkEdgeConstraints | ||||
| { | ||||
|   META_GTK_EDGE_CONSTRAINT_TOP_TILED = 1 << 0, | ||||
| @@ -64,6 +66,11 @@ enum _MetaGtkEdgeConstraints | ||||
|  | ||||
| G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW) | ||||
|  | ||||
| static void | ||||
| meta_window_x11_maybe_focus_delayed (MetaWindow *window, | ||||
|                                      GQueue     *other_focus_candidates, | ||||
|                                      guint32     timestamp); | ||||
|  | ||||
| static void | ||||
| meta_window_x11_init (MetaWindowX11 *window_x11) | ||||
| { | ||||
| @@ -776,6 +783,158 @@ request_take_focus (MetaWindow *window, | ||||
|   send_icccm_message (window, display->x11_display->atom_WM_TAKE_FOCUS, timestamp); | ||||
| } | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   MetaWindow *window; | ||||
|   GQueue *pending_focus_candidates; | ||||
|   guint32 timestamp; | ||||
|   guint timeout_id; | ||||
|   gulong unmanaged_id; | ||||
|   gulong focused_changed_id; | ||||
| } MetaWindowX11DelayedFocusData; | ||||
|  | ||||
| static void | ||||
| disconnect_pending_focus_window_signals (MetaWindow *window, | ||||
|                                          GQueue     *focus_candidates) | ||||
| { | ||||
|   g_signal_handlers_disconnect_by_func (window, g_queue_remove, | ||||
|                                         focus_candidates); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_window_x11_delayed_focus_data_free (MetaWindowX11DelayedFocusData *data) | ||||
| { | ||||
|   g_signal_handler_disconnect (data->window, data->unmanaged_id); | ||||
|   g_signal_handler_disconnect (data->window->display, data->focused_changed_id); | ||||
|  | ||||
|   if (data->pending_focus_candidates) | ||||
|     { | ||||
|       g_queue_foreach (data->pending_focus_candidates, | ||||
|                        (GFunc) disconnect_pending_focus_window_signals, | ||||
|                        data->pending_focus_candidates); | ||||
|       g_queue_free (data->pending_focus_candidates); | ||||
|     } | ||||
|  | ||||
|   g_clear_handle_id (&data->timeout_id, g_source_remove); | ||||
|   g_free (data); | ||||
| } | ||||
|  | ||||
| static void | ||||
| focus_candidates_maybe_take_and_focus_next (GQueue  **focus_candidates_ptr, | ||||
|                                             guint32   timestamp) | ||||
| { | ||||
|   MetaWindow *focus_window; | ||||
|   GQueue *focus_candidates; | ||||
|  | ||||
|   g_assert (*focus_candidates_ptr); | ||||
|  | ||||
|   if (g_queue_is_empty (*focus_candidates_ptr)) | ||||
|     return; | ||||
|  | ||||
|   focus_candidates = g_steal_pointer (focus_candidates_ptr); | ||||
|   focus_window = g_queue_pop_head (focus_candidates); | ||||
|  | ||||
|   disconnect_pending_focus_window_signals (focus_window, focus_candidates); | ||||
|   meta_window_x11_maybe_focus_delayed (focus_window, focus_candidates, timestamp); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| focus_window_delayed_timeout (gpointer user_data) | ||||
| { | ||||
|   MetaWindowX11DelayedFocusData *data = user_data; | ||||
|   MetaWindow *window = data->window; | ||||
|   guint32 timestamp = data->timestamp; | ||||
|  | ||||
|   focus_candidates_maybe_take_and_focus_next (&data->pending_focus_candidates, | ||||
|                                               timestamp); | ||||
|  | ||||
|   data->timeout_id = 0; | ||||
|   meta_window_x11_delayed_focus_data_free (data); | ||||
|  | ||||
|   meta_window_focus (window, timestamp); | ||||
|  | ||||
|   return G_SOURCE_REMOVE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_window_x11_maybe_focus_delayed (MetaWindow *window, | ||||
|                                      GQueue     *other_focus_candidates, | ||||
|                                      guint32     timestamp) | ||||
| { | ||||
|   MetaWindowX11DelayedFocusData *data; | ||||
|  | ||||
|   data = g_new0 (MetaWindowX11DelayedFocusData, 1); | ||||
|   data->window = window; | ||||
|   data->timestamp = timestamp; | ||||
|   data->pending_focus_candidates = other_focus_candidates; | ||||
|  | ||||
|   meta_topic (META_DEBUG_FOCUS, | ||||
|               "Requesting delayed focus to %s\n", window->desc); | ||||
|  | ||||
|   data->unmanaged_id = | ||||
|     g_signal_connect_swapped (window, "unmanaged", | ||||
|                               G_CALLBACK (meta_window_x11_delayed_focus_data_free), | ||||
|                               data); | ||||
|  | ||||
|   data->focused_changed_id = | ||||
|     g_signal_connect_swapped (window->display, "notify::focus-window", | ||||
|                               G_CALLBACK (meta_window_x11_delayed_focus_data_free), | ||||
|                               data); | ||||
|  | ||||
|   data->timeout_id = g_timeout_add (TAKE_FOCUS_FALLBACK_DELAY_MS, | ||||
|                                     focus_window_delayed_timeout, data); | ||||
| } | ||||
|  | ||||
| static void | ||||
| maybe_focus_default_window (MetaDisplay *display, | ||||
|                             MetaWindow  *not_this_one, | ||||
|                             guint32      timestamp) | ||||
| { | ||||
|   MetaWorkspace *workspace; | ||||
|   MetaStack *stack = display->stack; | ||||
|   g_autoptr (GList) focusable_windows = NULL; | ||||
|   g_autoptr (GQueue) focus_candidates = NULL; | ||||
|   GList *l; | ||||
|  | ||||
|   if (not_this_one && not_this_one->workspace) | ||||
|     workspace = not_this_one->workspace; | ||||
|   else | ||||
|     workspace = display->workspace_manager->active_workspace; | ||||
|  | ||||
|    /* Go through all the focusable windows and try to focus them | ||||
|     * in order, waiting for a delay. The first one that replies to | ||||
|     * the request (in case of take focus windows) changing the display | ||||
|     * focused window, will stop the chained requests. | ||||
|     */ | ||||
|   focusable_windows = | ||||
|     meta_stack_get_default_focus_candidates (stack, workspace); | ||||
|   focus_candidates = g_queue_new (); | ||||
|  | ||||
|   for (l = g_list_last (focusable_windows); l; l = l->prev) | ||||
|     { | ||||
|       MetaWindow *focus_window = l->data; | ||||
|  | ||||
|       if (focus_window == not_this_one) | ||||
|         continue; | ||||
|  | ||||
|       g_queue_push_tail (focus_candidates, focus_window); | ||||
|       g_signal_connect_swapped (focus_window, "unmanaged", | ||||
|                                 G_CALLBACK (g_queue_remove), | ||||
|                                 focus_candidates); | ||||
|  | ||||
|       if (!META_IS_WINDOW_X11 (focus_window)) | ||||
|         break; | ||||
|  | ||||
|       if (focus_window->input) | ||||
|         break; | ||||
|  | ||||
|       if (focus_window->shaded && focus_window->frame) | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|   focus_candidates_maybe_take_and_focus_next (&focus_candidates, timestamp); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_window_x11_focus (MetaWindow *window, | ||||
|                        guint32     timestamp) | ||||
| @@ -827,13 +986,20 @@ meta_window_x11_focus (MetaWindow *window, | ||||
|                * Normally, we want to just leave the focus undisturbed until | ||||
|                * the window responds to WM_TAKE_FOCUS, but if we're unmanaging | ||||
|                * the current focus window we *need* to move the focus away, so | ||||
|                * we focus the no_focus_window now (and set | ||||
|                * display->focus_window to that) before sending WM_TAKE_FOCUS. | ||||
|                * we focus the no focus window before sending WM_TAKE_FOCUS, | ||||
|                * and eventually the default focus windwo excluding this one, | ||||
|                * if meanwhile we don't get any focus request. | ||||
|                */ | ||||
|               if (window->display->focus_window != NULL && | ||||
|                   window->display->focus_window->unmanaging) | ||||
|                 meta_x11_display_focus_the_no_focus_window (window->display->x11_display, | ||||
|                 { | ||||
|                   MetaX11Display *x11_display = window->display->x11_display; | ||||
|  | ||||
|                   meta_x11_display_focus_the_no_focus_window (x11_display, | ||||
|                                                               timestamp); | ||||
|                   maybe_focus_default_window (window->display, window, | ||||
|                                               timestamp); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|           request_take_focus (window, timestamp); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user