Compare commits
	
		
			7 Commits
		
	
	
		
			wip/waylan
			...
			wip/waylan
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 10fbaf7695 | ||
|   | ef73402654 | ||
|   | 22cdf2d650 | ||
|   | 284b497b4c | ||
|   | 7d9141c56f | ||
|   | 519a06b93d | ||
|   | 0cceddab75 | 
| @@ -139,12 +139,6 @@ AM_GLIB_GNU_GETTEXT | ||||
| ## here we get the flags we'll actually use | ||||
| # GRegex requires Glib-2.14.0 | ||||
| PKG_CHECK_MODULES(ALL, glib-2.0 >= 2.14.0) | ||||
| PKG_CHECK_MODULES(MUTTER_LAUNCH, libdrm libsystemd-login) | ||||
|  | ||||
| saved_LIBS="$LIBS" | ||||
| LIBS="$LIBS $MUTTER_LAUNCH" | ||||
| AC_CHECK_FUNCS([sd_session_get_vt]) | ||||
| LIBS="$saved_LIBS" | ||||
|  | ||||
| # Unconditionally use this dir to avoid a circular dep with gnomecc | ||||
| GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings" | ||||
| @@ -232,7 +226,7 @@ AS_IF([test "x$WAYLAND_SCANNER" = "xno"], | ||||
| AC_SUBST([WAYLAND_SCANNER]) | ||||
| AC_SUBST(XWAYLAND_PATH) | ||||
|  | ||||
| MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server libdrm" | ||||
| MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server" | ||||
| PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES) | ||||
|  | ||||
| PKG_CHECK_EXISTS([xi >= 1.6.99.1], | ||||
|   | ||||
| @@ -195,9 +195,7 @@ libmutter_wayland_la_SOURCES +=			\ | ||||
| 	wayland/meta-wayland-seat.c		\ | ||||
| 	wayland/meta-wayland-seat.h		\ | ||||
| 	wayland/meta-wayland-stage.h		\ | ||||
| 	wayland/meta-wayland-stage.c		\ | ||||
| 	wayland/meta-weston-launch.c		\ | ||||
| 	wayland/meta-weston-launch.h | ||||
| 	wayland/meta-wayland-stage.c | ||||
|  | ||||
| libmutter_wayland_la_LDFLAGS = -no-undefined | ||||
| libmutter_wayland_la_LIBADD  = $(MUTTER_LIBS) | ||||
| @@ -249,17 +247,6 @@ bin_PROGRAMS=mutter-wayland | ||||
| mutter_wayland_SOURCES = core/mutter.c | ||||
| mutter_wayland_LDADD = $(MUTTER_LIBS) libmutter-wayland.la | ||||
|  | ||||
| bin_PROGRAMS+=mutter-launch | ||||
|  | ||||
| mutter_launch_SOURCES = wayland/weston-launch.c wayland/weston-launch.h | ||||
|  | ||||
| mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS) -DLIBDIR=\"$(libdir)\" | ||||
| mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam | ||||
|  | ||||
| install-exec-hook: | ||||
| 	-chown root $(DESTDIR)$(bindir)/mutter-launch | ||||
| 	-chmod u+s $(DESTDIR)$(bindir)/mutter-launch | ||||
|  | ||||
| if HAVE_INTROSPECTION | ||||
| include $(INTROSPECTION_MAKEFILE) | ||||
|  | ||||
| @@ -319,7 +306,7 @@ xml_DATA     = $(xml_in_files:.xml.in=.xml) | ||||
|  | ||||
| dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h | ||||
|  | ||||
| gsettings_SCHEMAS = org.gnome.mutter.gschema.xml org.gnome.mutter.wayland.gschema.xml | ||||
| gsettings_SCHEMAS = org.gnome.mutter.gschema.xml | ||||
| @INTLTOOL_XML_NOMERGE_RULE@ | ||||
| @GSETTINGS_RULES@ | ||||
|  | ||||
|   | ||||
| @@ -930,8 +930,8 @@ is_grabbed_event (MetaDisplay *display, | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_compositor_window_x11_shape_changed (MetaCompositor *compositor, | ||||
|                                           MetaWindow     *window) | ||||
| meta_compositor_window_shape_changed (MetaCompositor *compositor, | ||||
|                                       MetaWindow     *window) | ||||
| { | ||||
|   MetaWindowActor *window_actor; | ||||
|   window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); | ||||
|   | ||||
| @@ -91,11 +91,10 @@ struct _MetaShapedTexturePrivate | ||||
|   CoglTexture *texture; | ||||
|  | ||||
|   CoglTexture *mask_texture; | ||||
|   CoglPipeline *pipeline; | ||||
|   CoglPipeline *pipeline_unshaped; | ||||
|  | ||||
|   cairo_region_t *clip_region; | ||||
|   cairo_region_t *input_shape_region; | ||||
|   cairo_region_t *opaque_region; | ||||
|  | ||||
|   guint tex_width, tex_height; | ||||
|  | ||||
| @@ -145,8 +144,6 @@ meta_shaped_texture_dispose (GObject *object) | ||||
|     meta_texture_tower_free (priv->paint_tower); | ||||
|   priv->paint_tower = NULL; | ||||
|  | ||||
|   g_clear_pointer (&priv->pipeline, cogl_object_unref); | ||||
|   g_clear_pointer (&priv->pipeline_unshaped, cogl_object_unref); | ||||
|   g_clear_pointer (&priv->texture, cogl_object_unref); | ||||
|  | ||||
|   meta_shaped_texture_set_mask_texture (self, NULL); | ||||
| @@ -155,6 +152,76 @@ meta_shaped_texture_dispose (GObject *object) | ||||
|   G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object); | ||||
| } | ||||
|  | ||||
| static CoglPipeline * | ||||
| get_unmasked_pipeline (CoglContext *ctx) | ||||
| { | ||||
|   return cogl_pipeline_new (ctx); | ||||
| } | ||||
|  | ||||
| static CoglPipeline * | ||||
| get_masked_pipeline (CoglContext *ctx) | ||||
| { | ||||
|   static CoglPipeline *template = NULL; | ||||
|   if (G_UNLIKELY (template == NULL)) | ||||
|     { | ||||
|       template = cogl_pipeline_new (ctx); | ||||
|       cogl_pipeline_set_layer_combine (template, 1, | ||||
|                                        "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", | ||||
|                                        NULL); | ||||
|     } | ||||
|  | ||||
|   return cogl_pipeline_copy (template); | ||||
| } | ||||
|  | ||||
| static CoglPipeline * | ||||
| get_unblended_pipeline (CoglContext *ctx) | ||||
| { | ||||
|   static CoglPipeline *template = NULL; | ||||
|   if (G_UNLIKELY (template == NULL)) | ||||
|     { | ||||
|       CoglColor color; | ||||
|       template = cogl_pipeline_new (ctx); | ||||
|       cogl_color_init_from_4ub (&color, 255, 255, 255, 255); | ||||
|       cogl_pipeline_set_blend (template, | ||||
|                                "RGBA = ADD (SRC_COLOR, 0)", | ||||
|                                NULL); | ||||
|       cogl_pipeline_set_color (template, &color); | ||||
|     } | ||||
|  | ||||
|   return cogl_pipeline_copy (template); | ||||
| } | ||||
|  | ||||
| static void | ||||
| paint_clipped_rectangle (CoglFramebuffer       *fb, | ||||
|                          CoglPipeline          *pipeline, | ||||
|                          cairo_rectangle_int_t *rect, | ||||
|                          ClutterActorBox       *alloc) | ||||
| { | ||||
|   float coords[8]; | ||||
|   float x1, y1, x2, y2; | ||||
|  | ||||
|   x1 = rect->x; | ||||
|   y1 = rect->y; | ||||
|   x2 = rect->x + rect->width; | ||||
|   y2 = rect->y + rect->height; | ||||
|  | ||||
|   coords[0] = rect->x / (alloc->x2 - alloc->x1); | ||||
|   coords[1] = rect->y / (alloc->y2 - alloc->y1); | ||||
|   coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1); | ||||
|   coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1); | ||||
|  | ||||
|   coords[4] = coords[0]; | ||||
|   coords[5] = coords[1]; | ||||
|   coords[6] = coords[2]; | ||||
|   coords[7] = coords[3]; | ||||
|  | ||||
|   cogl_framebuffer_draw_multitextured_rectangle (fb, pipeline, | ||||
|                                                  x1, y1, x2, y2, | ||||
|                                                  &coords[0], 8); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| set_cogl_texture (MetaShapedTexture *stex, | ||||
|                   CoglTexture       *cogl_tex) | ||||
| @@ -171,12 +238,6 @@ set_cogl_texture (MetaShapedTexture *stex, | ||||
|  | ||||
|   priv->texture = cogl_tex; | ||||
|  | ||||
|   if (priv->pipeline != NULL) | ||||
|     cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex)); | ||||
|  | ||||
|   if (priv->pipeline_unshaped != NULL) | ||||
|     cogl_pipeline_set_layer_texture (priv->pipeline_unshaped, 0, COGL_TEXTURE (cogl_tex)); | ||||
|  | ||||
|   if (cogl_tex != NULL) | ||||
|     { | ||||
|       width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex)); | ||||
| @@ -210,14 +271,14 @@ meta_shaped_texture_paint (ClutterActor *actor) | ||||
| { | ||||
|   MetaShapedTexture *stex = (MetaShapedTexture *) actor; | ||||
|   MetaShapedTexturePrivate *priv = stex->priv; | ||||
|   CoglTexture *paint_tex; | ||||
|   guint tex_width, tex_height; | ||||
|   guchar opacity; | ||||
|   CoglContext *ctx; | ||||
|   CoglFramebuffer *fb; | ||||
|   CoglPipeline *pipeline = NULL; | ||||
|   CoglTexture *paint_tex; | ||||
|   ClutterActorBox alloc; | ||||
|  | ||||
|   static CoglPipeline *pipeline_template = NULL; | ||||
|   static CoglPipeline *pipeline_unshaped_template = NULL; | ||||
|  | ||||
|   CoglPipeline *pipeline; | ||||
|   cairo_region_t *blended_region = NULL; | ||||
|  | ||||
|   if (priv->clip_region && cairo_region_is_empty (priv->clip_region)) | ||||
|     return; | ||||
| @@ -254,38 +315,67 @@ meta_shaped_texture_paint (ClutterActor *actor) | ||||
|   if (tex_width == 0 || tex_height == 0) /* no contents yet */ | ||||
|     return; | ||||
|  | ||||
|   ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); | ||||
|   fb = cogl_get_draw_framebuffer (); | ||||
|  | ||||
|   opacity = clutter_actor_get_paint_opacity (actor); | ||||
|   clutter_actor_get_allocation_box (actor, &alloc); | ||||
|  | ||||
|   if (priv->opaque_region != NULL && opacity == 255) | ||||
|     { | ||||
|       CoglPipeline *opaque_pipeline; | ||||
|       cairo_region_t *region; | ||||
|       int n_rects; | ||||
|       int i; | ||||
|  | ||||
|       region = cairo_region_copy (priv->clip_region); | ||||
|       cairo_region_intersect (region, priv->opaque_region); | ||||
|  | ||||
|       if (cairo_region_is_empty (region)) | ||||
|         goto paint_blended; | ||||
|  | ||||
|       opaque_pipeline = get_unblended_pipeline (ctx); | ||||
|       cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex); | ||||
|  | ||||
|       n_rects = cairo_region_num_rectangles (region); | ||||
|       for (i = 0; i < n_rects; i++) | ||||
|         { | ||||
|           cairo_rectangle_int_t rect; | ||||
|           cairo_region_get_rectangle (region, i, &rect); | ||||
|           paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc); | ||||
|         } | ||||
|  | ||||
|       cogl_object_unref (opaque_pipeline); | ||||
|  | ||||
|       if (priv->clip_region != NULL) | ||||
|         { | ||||
|           blended_region = cairo_region_copy (priv->clip_region); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height }; | ||||
|           blended_region = cairo_region_create_rectangle (&rect); | ||||
|         } | ||||
|  | ||||
|       cairo_region_subtract (blended_region, priv->opaque_region); | ||||
|  | ||||
|     paint_blended: | ||||
|       cairo_region_destroy (region); | ||||
|     } | ||||
|  | ||||
|   if (blended_region == NULL && priv->clip_region != NULL) | ||||
|     blended_region = cairo_region_reference (priv->clip_region); | ||||
|  | ||||
|   if (blended_region != NULL && cairo_region_is_empty (blended_region)) | ||||
|     goto out; | ||||
|  | ||||
|   if (priv->mask_texture == NULL) | ||||
|     { | ||||
|       /* Use a single-layer texture if we don't have a mask. */ | ||||
|  | ||||
|       if (priv->pipeline_unshaped == NULL) | ||||
|         { | ||||
|           if (G_UNLIKELY (pipeline_unshaped_template == NULL)) | ||||
|             { | ||||
|               CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); | ||||
|               pipeline_unshaped_template = cogl_pipeline_new (ctx); | ||||
|             } | ||||
|  | ||||
|           priv->pipeline_unshaped = cogl_pipeline_copy (pipeline_unshaped_template); | ||||
|         } | ||||
|         pipeline = priv->pipeline_unshaped; | ||||
|       pipeline = get_unmasked_pipeline (ctx); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       if (priv->pipeline == NULL) | ||||
| 	{ | ||||
| 	   if (G_UNLIKELY (pipeline_template == NULL)) | ||||
| 	    { | ||||
|               CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); | ||||
| 	      pipeline_template =  cogl_pipeline_new (ctx); | ||||
| 	      cogl_pipeline_set_layer_combine (pipeline_template, 1, | ||||
| 					   "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", | ||||
| 					   NULL); | ||||
| 	    } | ||||
| 	  priv->pipeline = cogl_pipeline_copy (pipeline_template); | ||||
| 	} | ||||
|       pipeline = priv->pipeline; | ||||
|  | ||||
|       pipeline = get_masked_pipeline (ctx); | ||||
|       cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture); | ||||
|     } | ||||
|  | ||||
| @@ -293,20 +383,13 @@ meta_shaped_texture_paint (ClutterActor *actor) | ||||
|  | ||||
|   { | ||||
|     CoglColor color; | ||||
|     guchar opacity = clutter_actor_get_paint_opacity (actor); | ||||
|     cogl_color_set_from_4ub (&color, opacity, opacity, opacity, opacity); | ||||
|     cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity); | ||||
|     cogl_pipeline_set_color (pipeline, &color); | ||||
|   } | ||||
|  | ||||
|   cogl_set_source (pipeline); | ||||
|  | ||||
|   clutter_actor_get_allocation_box (actor, &alloc); | ||||
|  | ||||
|   if (priv->clip_region) | ||||
|   if (blended_region != NULL) | ||||
|     { | ||||
|       int n_rects; | ||||
|       int i; | ||||
|       cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height }; | ||||
|  | ||||
|       /* Limit to how many separate rectangles we'll draw; beyond this just | ||||
|        * fall back and draw the whole thing */ | ||||
| @@ -315,8 +398,8 @@ meta_shaped_texture_paint (ClutterActor *actor) | ||||
|       n_rects = cairo_region_num_rectangles (priv->clip_region); | ||||
|       if (n_rects <= MAX_RECTS) | ||||
| 	{ | ||||
| 	  float coords[8]; | ||||
|           float x1, y1, x2, y2; | ||||
|           int i; | ||||
|           cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height }; | ||||
|  | ||||
| 	  for (i = 0; i < n_rects; i++) | ||||
| 	    { | ||||
| @@ -327,32 +410,23 @@ meta_shaped_texture_paint (ClutterActor *actor) | ||||
| 	      if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect)) | ||||
| 		continue; | ||||
|  | ||||
| 	      x1 = rect.x; | ||||
| 	      y1 = rect.y; | ||||
| 	      x2 = rect.x + rect.width; | ||||
| 	      y2 = rect.y + rect.height; | ||||
|  | ||||
| 	      coords[0] = rect.x / (alloc.x2 - alloc.x1); | ||||
| 	      coords[1] = rect.y / (alloc.y2 - alloc.y1); | ||||
| 	      coords[2] = (rect.x + rect.width) / (alloc.x2 - alloc.x1); | ||||
| 	      coords[3] = (rect.y + rect.height) / (alloc.y2 - alloc.y1); | ||||
|  | ||||
|               coords[4] = coords[0]; | ||||
|               coords[5] = coords[1]; | ||||
|               coords[6] = coords[2]; | ||||
|               coords[7] = coords[3]; | ||||
|  | ||||
|               cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2, | ||||
|                                                        &coords[0], 8); | ||||
|               paint_clipped_rectangle (fb, pipeline, &rect, &alloc); | ||||
|             } | ||||
|  | ||||
| 	  return; | ||||
|           goto out; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|   cogl_rectangle (0, 0, | ||||
| 		  alloc.x2 - alloc.x1, | ||||
| 		  alloc.y2 - alloc.y1); | ||||
|   cogl_framebuffer_draw_rectangle (fb, pipeline, | ||||
|                                    0, 0, | ||||
|                                    alloc.x2 - alloc.x1, | ||||
|                                    alloc.y2 - alloc.y1); | ||||
|  | ||||
|  out: | ||||
|   if (pipeline != NULL) | ||||
|     cogl_object_unref (pipeline); | ||||
|   if (blended_region != NULL) | ||||
|     cairo_region_destroy (blended_region); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -368,13 +442,17 @@ meta_shaped_texture_pick (ClutterActor       *actor, | ||||
|  | ||||
|   /* If there is no region then use the regular pick */ | ||||
|   if (priv->input_shape_region == NULL) | ||||
|     CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class) | ||||
|       ->pick (actor, color); | ||||
|     CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)->pick (actor, color); | ||||
|   else | ||||
|     { | ||||
|       int n_rects; | ||||
|       float *rectangles; | ||||
|       int i; | ||||
|       ClutterActorBox alloc; | ||||
|       CoglPipeline *pipeline; | ||||
|       CoglContext *ctx; | ||||
|       CoglFramebuffer *fb; | ||||
|       CoglColor cogl_color; | ||||
|  | ||||
|       /* Note: We don't bother trying to intersect the pick and clip regions | ||||
|        * since needing to copy the region, do the intersection, and probably | ||||
| @@ -403,12 +481,17 @@ meta_shaped_texture_pick (ClutterActor       *actor, | ||||
|           rectangles[pos + 3] = rect.y + rect.height; | ||||
|         } | ||||
|  | ||||
|       cogl_set_source_color4ub (color->red, | ||||
|                                 color->green, | ||||
|                                 color->blue, | ||||
|                                 color->alpha); | ||||
|       ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); | ||||
|       fb = cogl_get_draw_framebuffer (); | ||||
|  | ||||
|       cogl_rectangles (rectangles, n_rects); | ||||
|       cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); | ||||
|  | ||||
|       pipeline = cogl_pipeline_new (ctx); | ||||
|       cogl_pipeline_set_color (pipeline, &cogl_color); | ||||
|  | ||||
|       cogl_framebuffer_draw_rectangles (fb, pipeline, | ||||
|                                         rectangles, n_rects); | ||||
|       cogl_object_unref (pipeline); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -845,6 +928,36 @@ meta_shaped_texture_set_clip_region (MetaShapedTexture *stex, | ||||
|     priv->clip_region = NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_shaped_texture_set_opaque_region: | ||||
|  * @stex: a #MetaShapedTexture | ||||
|  * @opaque_region: (transfer full): the region of the texture that | ||||
|  *   can have blending turned off. | ||||
|  * | ||||
|  * As most windows have a large portion that does not require blending, | ||||
|  * we can easily turn off blending if we know the areas that do not | ||||
|  * require blending. This sets the region where we will not blend for | ||||
|  * optimization purposes. | ||||
|  */ | ||||
| void | ||||
| meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex, | ||||
|                                        cairo_region_t    *opaque_region) | ||||
| { | ||||
|   MetaShapedTexturePrivate *priv; | ||||
|  | ||||
|   g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); | ||||
|  | ||||
|   priv = stex->priv; | ||||
|  | ||||
|   if (priv->opaque_region) | ||||
|     cairo_region_destroy (priv->opaque_region); | ||||
|  | ||||
|   if (opaque_region) | ||||
|     priv->opaque_region = cairo_region_reference (opaque_region); | ||||
|   else | ||||
|     priv->opaque_region = NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_shaped_texture_get_image: | ||||
|  * @stex: A #MetaShapedTexture | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
|  | ||||
| #include <math.h> | ||||
|  | ||||
| #include <X11/extensions/shape.h> | ||||
| #include <X11/extensions/Xcomposite.h> | ||||
| #include <X11/extensions/Xdamage.h> | ||||
| #include <X11/extensions/Xrender.h> | ||||
| @@ -71,7 +70,7 @@ struct _MetaWindowActorPrivate | ||||
|   /* A region that matches the shape of the window, including frame bounds */ | ||||
|   cairo_region_t   *shape_region; | ||||
|   /* If the window has an input shape, a region that matches the shape */ | ||||
|   cairo_region_t   *input_shape_region; | ||||
|   cairo_region_t   *input_region; | ||||
|   /* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with | ||||
|    * the shape region. */ | ||||
|   cairo_region_t   *opaque_region; | ||||
| @@ -407,8 +406,8 @@ meta_window_actor_constructed (GObject *object) | ||||
|  | ||||
|   /* Start off with empty regions to maintain the invariant that | ||||
|      these regions are always set */ | ||||
|   priv->shape_region = cairo_region_create(); | ||||
|   priv->input_shape_region = cairo_region_create(); | ||||
|   priv->shape_region = cairo_region_create (); | ||||
|   priv->input_region = cairo_region_create (); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -438,7 +437,7 @@ meta_window_actor_dispose (GObject *object) | ||||
|     } | ||||
|  | ||||
|   g_clear_pointer (&priv->shape_region, cairo_region_destroy); | ||||
|   g_clear_pointer (&priv->input_shape_region, cairo_region_destroy); | ||||
|   g_clear_pointer (&priv->input_region, cairo_region_destroy); | ||||
|   g_clear_pointer (&priv->opaque_region, cairo_region_destroy); | ||||
|   g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); | ||||
|  | ||||
| @@ -1260,7 +1259,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self) | ||||
|   if (priv->opacity != 0xff) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (metaWindow->has_shape) | ||||
|   if (metaWindow->shape_region != NULL) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (priv->argb32 && !meta_window_requested_bypass_compositor (metaWindow)) | ||||
| @@ -2171,85 +2170,21 @@ build_and_scan_frame_mask (MetaWindowActor       *self, | ||||
|   g_free (mask_data); | ||||
| } | ||||
|  | ||||
| static cairo_region_t * | ||||
| region_create_from_x_rectangles (const XRectangle *rects, | ||||
|                                  int n_rects, | ||||
|                                  int dx, | ||||
|                                  int dy) | ||||
| { | ||||
|   int i; | ||||
|   cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects); | ||||
|  | ||||
|   for (i = 0; i < n_rects; i ++) | ||||
|     { | ||||
|       cairo_rects[i].x = rects[i].x + dx; | ||||
|       cairo_rects[i].y = rects[i].y + dy; | ||||
|       cairo_rects[i].width = rects[i].width; | ||||
|       cairo_rects[i].height = rects[i].height; | ||||
|     } | ||||
|  | ||||
|   return cairo_region_create_rectangles (cairo_rects, n_rects); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_window_actor_update_x11_shape_region (MetaWindowActor *self, | ||||
|                                            cairo_rectangle_int_t *client_area) | ||||
| meta_window_actor_update_shape_region (MetaWindowActor       *self, | ||||
|                                        cairo_rectangle_int_t *client_area) | ||||
| { | ||||
|   MetaWindowActorPrivate *priv = self->priv; | ||||
|   cairo_region_t *region = NULL; | ||||
|   gboolean needs_mask; | ||||
|  | ||||
|   if (priv->shadow_shape != NULL) | ||||
|   if (priv->window->frame != NULL && priv->window->shape_region != NULL) | ||||
|     { | ||||
|       meta_window_shape_unref (priv->shadow_shape); | ||||
|       priv->shadow_shape = NULL; | ||||
|       region = cairo_region_copy (priv->window->shape_region); | ||||
|       cairo_region_translate (region, client_area->x, client_area->y); | ||||
|     } | ||||
|  | ||||
|   meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL); | ||||
|   g_clear_pointer (&priv->shape_region, cairo_region_destroy); | ||||
|   g_clear_pointer (&priv->opaque_region, cairo_region_destroy); | ||||
|  | ||||
| #ifdef HAVE_SHAPE | ||||
|   if (priv->window->has_shape) | ||||
|   else if (priv->window->shape_region != NULL) | ||||
|     { | ||||
|       /* Translate the set of XShape rectangles that we | ||||
|        * get from the X server to a cairo_region. */ | ||||
|       MetaScreen *screen = priv->screen; | ||||
|       MetaDisplay *display = meta_screen_get_display (screen); | ||||
|       Display *xdisplay = meta_display_get_xdisplay (display); | ||||
|       XRectangle *rects; | ||||
|       int n_rects, ordering; | ||||
|  | ||||
|       meta_error_trap_push (display); | ||||
|       rects = XShapeGetRectangles (xdisplay, | ||||
|                                    priv->window->xwindow, | ||||
|                                    ShapeBounding, | ||||
|                                    &n_rects, | ||||
|                                    &ordering); | ||||
|       meta_error_trap_pop (display); | ||||
|  | ||||
|       if (rects) | ||||
|         { | ||||
|           region = region_create_from_x_rectangles (rects, n_rects, | ||||
|                                                     client_area->x, | ||||
|                                                     client_area->y); | ||||
|           XFree (rects); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|   needs_mask = (region != NULL) || (priv->window->frame != NULL); | ||||
|  | ||||
|   if (region != NULL) | ||||
|     { | ||||
|       /* The shape we get back from the client may have coordinates | ||||
|        * outside of the frame. The X SHAPE Extension requires that | ||||
|        * the overall shape the client provides never exceeds the | ||||
|        * "bounding rectangle" of the window -- the shape that the | ||||
|        * window would have gotten if it was unshaped. In our case, | ||||
|        * this is simply the client area. | ||||
|        */ | ||||
|       cairo_region_intersect_rectangle (region, client_area); | ||||
|       region = cairo_region_reference (priv->window->shape_region); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| @@ -2259,11 +2194,70 @@ meta_window_actor_update_x11_shape_region (MetaWindowActor *self, | ||||
|       region = cairo_region_create_rectangle (client_area); | ||||
|     } | ||||
|  | ||||
|   /* The region at this point should be constrained to the | ||||
|    * bounds of the client rectangle. */ | ||||
|   meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL); | ||||
|   if ((priv->window->shape_region != NULL) || (priv->window->frame != NULL)) | ||||
|     build_and_scan_frame_mask (self, client_area, region); | ||||
|  | ||||
|   g_clear_pointer (&priv->shape_region, cairo_region_destroy); | ||||
|   priv->shape_region = region; | ||||
|  | ||||
|   if (priv->shadow_shape != NULL) | ||||
|     { | ||||
|       meta_window_shape_unref (priv->shadow_shape); | ||||
|       priv->shadow_shape = NULL; | ||||
|     } | ||||
|  | ||||
|   meta_window_actor_invalidate_shadow (self); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_window_actor_update_input_region (MetaWindowActor       *self, | ||||
|                                        cairo_rectangle_int_t *client_area) | ||||
| { | ||||
|   MetaWindowActorPrivate *priv = self->priv; | ||||
|   MetaShapedTexture *stex = META_SHAPED_TEXTURE (priv->actor); | ||||
|   cairo_region_t *region = NULL; | ||||
|  | ||||
|   if (priv->window->frame != NULL && priv->window->shape_region != NULL) | ||||
|     { | ||||
|       cairo_region_t *client_region = cairo_region_copy (priv->window->input_region); | ||||
|  | ||||
|       region = meta_frame_get_frame_bounds (priv->window->frame); | ||||
|  | ||||
|       cairo_region_subtract_rectangle (region, client_area); | ||||
|       cairo_region_translate (client_region, client_area->x, client_area->y); | ||||
|       cairo_region_union (region, client_region); | ||||
|       cairo_region_destroy (client_region); | ||||
|     } | ||||
|   else if (priv->window->shape_region != NULL) | ||||
|     { | ||||
|       region = cairo_region_reference (priv->window->input_region); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       /* If we don't have a shape on the server, that means that | ||||
|        * we have an implicit shape of one rectangle covering the | ||||
|        * entire window. */ | ||||
|       region = cairo_region_create_rectangle (client_area); | ||||
|     } | ||||
|  | ||||
|   meta_shaped_texture_set_input_shape_region (stex, region); | ||||
|   cairo_region_destroy (region); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_window_actor_update_opaque_region (MetaWindowActor *self) | ||||
| { | ||||
|   MetaWindowActorPrivate *priv = self->priv; | ||||
|  | ||||
|   g_clear_pointer (&priv->opaque_region, cairo_region_destroy); | ||||
|  | ||||
|   if (priv->argb32 && priv->window->opaque_region != NULL) | ||||
|     { | ||||
|       MetaFrameBorders borders; | ||||
|  | ||||
|       meta_frame_calc_borders (priv->window->frame, &borders); | ||||
|  | ||||
|       /* The opaque region is defined to be a part of the | ||||
|        * window which ARGB32 will always paint with opaque | ||||
|        * pixels. For these regions, we want to avoid painting | ||||
| @@ -2275,91 +2269,16 @@ meta_window_actor_update_x11_shape_region (MetaWindowActor *self, | ||||
|        * case, graphical glitches will occur. | ||||
|        */ | ||||
|       priv->opaque_region = cairo_region_copy (priv->window->opaque_region); | ||||
|       cairo_region_translate (priv->opaque_region, client_area->x, client_area->y); | ||||
|       cairo_region_intersect (priv->opaque_region, region); | ||||
|       cairo_region_translate (priv->opaque_region, borders.total.left, borders.total.top); | ||||
|       cairo_region_intersect (priv->opaque_region, priv->shape_region); | ||||
|     } | ||||
|   else if (priv->argb32) | ||||
|     priv->opaque_region = NULL; | ||||
|   else | ||||
|     priv->opaque_region = cairo_region_reference (region); | ||||
|     priv->opaque_region = cairo_region_reference (priv->shape_region); | ||||
|  | ||||
|   if (needs_mask) | ||||
|     { | ||||
|       /* This takes the region, generates a mask using GTK+ | ||||
|        * and scans the mask looking for all opaque pixels, | ||||
|        * adding it to region. | ||||
|        */ | ||||
|       build_and_scan_frame_mask (self, client_area, region); | ||||
|     } | ||||
|  | ||||
|   priv->shape_region = region; | ||||
|  | ||||
|   meta_window_actor_invalidate_shadow (self); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_window_actor_update_x11_input_shape_region (MetaWindowActor *self, | ||||
|                                                  cairo_rectangle_int_t *client_area) | ||||
| { | ||||
|   MetaWindowActorPrivate *priv = self->priv; | ||||
|   cairo_region_t *region = NULL; | ||||
|  | ||||
|   g_clear_pointer (&priv->input_shape_region, cairo_region_destroy); | ||||
|  | ||||
| #ifdef HAVE_SHAPE | ||||
|   /* Note: we currently assume that mutter never sets an input region | ||||
|    * when there is a frame. */ | ||||
|   if (priv->window->frame == NULL && priv->window->has_input_shape) | ||||
|     { | ||||
|       MetaScreen *screen = priv->screen; | ||||
|       MetaDisplay *display = meta_screen_get_display (screen); | ||||
|       Display *xdisplay = meta_display_get_xdisplay (display); | ||||
|       XRectangle *rects; | ||||
|       int n_rects, ordering; | ||||
|  | ||||
|       /* Note we only actually query the ShapeInput shape of a window | ||||
|        * when we don't have a frame because we assume currently that | ||||
|        * mutter never sets an ShapeInput shape on a frame. */ | ||||
|       meta_error_trap_push (display); | ||||
|       rects = XShapeGetRectangles (xdisplay, | ||||
|                                    priv->window->xwindow, | ||||
|                                    ShapeInput, | ||||
|                                    &n_rects, | ||||
|                                    &ordering); | ||||
|       meta_error_trap_pop (display); | ||||
|       if (rects) | ||||
|         { | ||||
|           region = region_create_from_x_rectangles (rects, n_rects, | ||||
|                                                     client_area->x, | ||||
|                                                     client_area->y); | ||||
|           XFree (rects); | ||||
|         } | ||||
|     } | ||||
| #endif /* HAVE_SHAPE */ | ||||
|  | ||||
|   if (region != NULL) | ||||
|     { | ||||
|       /* The X shape extension requires us to intersect the input | ||||
|        * region with the effective bounding shape to determine the | ||||
|        * effective input region. | ||||
|        */ | ||||
|       if (priv->shape_region) | ||||
|         cairo_region_intersect (region, priv->shape_region); | ||||
|       else | ||||
|         cairo_region_intersect_rectangle (region, client_area); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       /* If we don't have a shape on the server, that means that we | ||||
|        * have an implicit shape of one rectangle covering the entire | ||||
|        * window. */ | ||||
|       region = cairo_region_create_rectangle (client_area); | ||||
|     } | ||||
|  | ||||
|   priv->input_shape_region = region; | ||||
|  | ||||
|   meta_shaped_texture_set_input_shape_region (META_SHAPED_TEXTURE (priv->actor), | ||||
|                                               priv->input_shape_region); | ||||
|   meta_shaped_texture_set_opaque_region (META_SHAPED_TEXTURE (priv->actor), | ||||
|                                          priv->opaque_region); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -2385,24 +2304,9 @@ check_needs_reshape (MetaWindowActor *self) | ||||
|   else | ||||
|     client_area.height = priv->window->rect.height; | ||||
|  | ||||
|   if (priv->window->client_type == META_WINDOW_CLIENT_TYPE_X11) | ||||
|     { | ||||
|       meta_window_actor_update_x11_shape_region (self, &client_area); | ||||
|       meta_window_actor_update_x11_input_shape_region (self, &client_area); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       /* TODO: properly support setting an input region as specified | ||||
|        * via the wayland protocol */ | ||||
|  | ||||
|       g_clear_pointer (&priv->shape_region, cairo_region_destroy); | ||||
|       g_clear_pointer (&priv->opaque_region, cairo_region_destroy); | ||||
|       g_clear_pointer (&priv->input_shape_region, cairo_region_destroy); | ||||
|  | ||||
|       priv->shape_region = cairo_region_create_rectangle (&client_area); | ||||
|       priv->opaque_region = cairo_region_reference (priv->shape_region); | ||||
|       priv->input_shape_region = cairo_region_reference (priv->shape_region); | ||||
|     } | ||||
|   meta_window_actor_update_shape_region (self, &client_area); | ||||
|   meta_window_actor_update_input_region (self, &client_area); | ||||
|   meta_window_actor_update_opaque_region (self); | ||||
|  | ||||
|   priv->needs_reshape = FALSE; | ||||
| } | ||||
| @@ -2463,6 +2367,8 @@ meta_window_actor_set_wayland_surface (MetaWindowActor *self, | ||||
|                                            surface); | ||||
|   if (surface && surface->buffer_ref.buffer) | ||||
|     maybe_emit_size_changed (self, surface->buffer_ref.buffer); | ||||
|  | ||||
|   meta_window_actor_invalidate_shadow (self); | ||||
| } | ||||
|  | ||||
| void | ||||
|   | ||||
| @@ -186,7 +186,6 @@ meta_window_group_paint (ClutterActor *actor) | ||||
|  | ||||
|       if (META_IS_WINDOW_ACTOR (child)) | ||||
|         { | ||||
|           MetaWindow *meta_window; | ||||
|           MetaWindowActor *window_actor = META_WINDOW_ACTOR (child); | ||||
|           int x, y; | ||||
|  | ||||
| @@ -201,14 +200,7 @@ meta_window_group_paint (ClutterActor *actor) | ||||
|  | ||||
|           meta_window_actor_set_visible_region (window_actor, visible_region); | ||||
|  | ||||
|           /* TODO: Track the opaque regions of wayland clients. | ||||
|            * Although wayland clients can report opaque window | ||||
|            * regions, for now we assume that all wayland clients are | ||||
|            * transparent... */ | ||||
|           meta_window = meta_window_actor_get_meta_window (window_actor); | ||||
|  | ||||
|           if (meta_window->client_type != META_WINDOW_CLIENT_TYPE_WAYLAND && | ||||
|               clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff) | ||||
|           if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff) | ||||
|             { | ||||
|               cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor); | ||||
|               if (obscured_region) | ||||
|   | ||||
| @@ -75,6 +75,8 @@ | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "meta-xwayland-private.h" | ||||
|  | ||||
| #define GRAB_OP_IS_WINDOW_SWITCH(g)                     \ | ||||
|         (g == META_GRAB_OP_KEYBOARD_TABBING_NORMAL  ||  \ | ||||
|          g == META_GRAB_OP_KEYBOARD_TABBING_DOCK    ||  \ | ||||
| @@ -516,6 +518,9 @@ meta_display_open (void) | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|     meta_xwayland_complete_init (); | ||||
|  | ||||
|   if (meta_is_syncing ()) | ||||
|     XSynchronize (xdisplay, True); | ||||
|    | ||||
| @@ -2331,59 +2336,9 @@ meta_display_handle_event (MetaDisplay *display, | ||||
|           XShapeEvent *sev = (XShapeEvent*) event; | ||||
|  | ||||
|           if (sev->kind == ShapeBounding) | ||||
|             { | ||||
|               if (sev->shaped && !window->has_shape) | ||||
|                 { | ||||
|                   window->has_shape = TRUE;                   | ||||
|                   meta_topic (META_DEBUG_SHAPES, | ||||
|                               "Window %s now has a shape\n", | ||||
|                               window->desc); | ||||
|                 } | ||||
|               else if (!sev->shaped && window->has_shape) | ||||
|                 { | ||||
|                   window->has_shape = FALSE; | ||||
|                   meta_topic (META_DEBUG_SHAPES, | ||||
|                               "Window %s no longer has a shape\n", | ||||
|                               window->desc); | ||||
|                 } | ||||
|               else | ||||
|                 { | ||||
|                   meta_topic (META_DEBUG_SHAPES, | ||||
|                               "Window %s shape changed\n", | ||||
|                               window->desc); | ||||
|                 } | ||||
|  | ||||
|               if (display->compositor) | ||||
|                 meta_compositor_window_x11_shape_changed (display->compositor, | ||||
|                                                           window); | ||||
|             } | ||||
|             meta_window_update_shape_region_x11 (window); | ||||
|           else if (sev->kind == ShapeInput) | ||||
|             { | ||||
|               if (sev->shaped && !window->has_input_shape) | ||||
|                 { | ||||
|                   window->has_input_shape = TRUE;                   | ||||
|                   meta_topic (META_DEBUG_SHAPES, | ||||
|                               "Window %s now has an input shape\n", | ||||
|                               window->desc); | ||||
|                 } | ||||
|               else if (!sev->shaped && window->has_input_shape) | ||||
|                 { | ||||
|                   window->has_input_shape = FALSE; | ||||
|                   meta_topic (META_DEBUG_SHAPES, | ||||
|                               "Window %s no longer has an input shape\n", | ||||
|                               window->desc); | ||||
|                 } | ||||
|               else | ||||
|                 { | ||||
|                   meta_topic (META_DEBUG_SHAPES, | ||||
|                               "Window %s input shape changed\n", | ||||
|                               window->desc); | ||||
|                 } | ||||
|  | ||||
|               if (display->compositor) | ||||
|                 meta_compositor_window_x11_shape_changed (display->compositor, | ||||
|                                                           window); | ||||
|             } | ||||
|             meta_window_update_input_region_x11 (window); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|   | ||||
| @@ -53,13 +53,8 @@ | ||||
| #include <X11/XKBlib.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_WAYLAND | ||||
| #include "meta-wayland-private.h" | ||||
| #endif | ||||
|  | ||||
| #define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings" | ||||
| #define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings" | ||||
| #define SCHEMA_MUTTER_WAYLAND_KEYBINDINGS "org.gnome.mutter.wayland.keybindings" | ||||
|  | ||||
| static gboolean add_builtin_keybinding (MetaDisplay          *display, | ||||
|                                         const char           *name, | ||||
| @@ -4103,40 +4098,6 @@ handle_set_spew_mark (MetaDisplay    *display, | ||||
|   meta_verbose ("-- MARK MARK MARK MARK --\n"); | ||||
| } | ||||
|  | ||||
| #ifdef HAVE_WAYLAND | ||||
| static void | ||||
| handle_switch_vt (MetaDisplay    *display, | ||||
|                   MetaScreen     *screen, | ||||
|                   MetaWindow     *window, | ||||
|                   XIDeviceEvent  *event, | ||||
|                   MetaKeyBinding *binding, | ||||
|                   gpointer        dummy) | ||||
| { | ||||
|     gint vt = binding->handler->data; | ||||
|     MetaWaylandCompositor *compositor; | ||||
|     MetaLauncher *launcher; | ||||
|  | ||||
|     compositor = meta_wayland_compositor_get_default (); | ||||
|     launcher = meta_wayland_compositor_get_launcher (compositor); | ||||
|  | ||||
|     if (launcher) | ||||
|       { | ||||
|         GError *error; | ||||
|  | ||||
|         error = NULL; | ||||
|         if (!meta_launcher_activate_vt (launcher, vt, &error)) | ||||
|           { | ||||
|             g_warning ("Failed to switch VT: %s", error->message); | ||||
|             g_error_free (error); | ||||
|           } | ||||
|       } | ||||
|     else | ||||
|       { | ||||
|         g_debug ("Ignoring VT switch keybinding, not running as VT manager"); | ||||
|       } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * meta_keybindings_set_custom_handler: | ||||
|  * @name: The name of the keybinding to set | ||||
| @@ -4200,7 +4161,6 @@ init_builtin_key_bindings (MetaDisplay *display) | ||||
|                                META_KEY_BINDING_IS_REVERSED) | ||||
|   GSettings *common_keybindings = g_settings_new (SCHEMA_COMMON_KEYBINDINGS); | ||||
|   GSettings *mutter_keybindings = g_settings_new (SCHEMA_MUTTER_KEYBINDINGS); | ||||
|   GSettings *mutter_wayland_keybindings = g_settings_new (SCHEMA_MUTTER_WAYLAND_KEYBINDINGS); | ||||
|  | ||||
|   add_builtin_keybinding (display, | ||||
|                           "switch-to-workspace-1", | ||||
| @@ -4462,60 +4422,6 @@ init_builtin_key_bindings (MetaDisplay *display) | ||||
|                           META_KEYBINDING_ACTION_SET_SPEW_MARK, | ||||
|                           handle_set_spew_mark, 0); | ||||
|  | ||||
| #ifdef HAVE_WAYLAND | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|     { | ||||
|       add_builtin_keybinding (display, | ||||
|                               "switch-to-session-1", | ||||
|                               mutter_wayland_keybindings, | ||||
|                               META_KEY_BINDING_NONE, | ||||
|                               META_KEYBINDING_ACTION_NONE, | ||||
|                               handle_switch_vt, 1); | ||||
|  | ||||
|       add_builtin_keybinding (display, | ||||
|                               "switch-to-session-2", | ||||
|                               mutter_wayland_keybindings, | ||||
|                               META_KEY_BINDING_NONE, | ||||
|                               META_KEYBINDING_ACTION_NONE, | ||||
|                               handle_switch_vt, 2); | ||||
|  | ||||
|       add_builtin_keybinding (display, | ||||
|                               "switch-to-session-3", | ||||
|                               mutter_wayland_keybindings, | ||||
|                               META_KEY_BINDING_NONE, | ||||
|                               META_KEYBINDING_ACTION_NONE, | ||||
|                               handle_switch_vt, 3); | ||||
|  | ||||
|       add_builtin_keybinding (display, | ||||
|                               "switch-to-session-4", | ||||
|                               mutter_wayland_keybindings, | ||||
|                               META_KEY_BINDING_NONE, | ||||
|                               META_KEYBINDING_ACTION_NONE, | ||||
|                               handle_switch_vt, 4); | ||||
|  | ||||
|       add_builtin_keybinding (display, | ||||
|                               "switch-to-session-5", | ||||
|                               mutter_wayland_keybindings, | ||||
|                               META_KEY_BINDING_NONE, | ||||
|                               META_KEYBINDING_ACTION_NONE, | ||||
|                               handle_switch_vt, 5); | ||||
|  | ||||
|       add_builtin_keybinding (display, | ||||
|                               "switch-to-session-6", | ||||
|                               mutter_wayland_keybindings, | ||||
|                               META_KEY_BINDING_NONE, | ||||
|                               META_KEYBINDING_ACTION_NONE, | ||||
|                               handle_switch_vt, 6); | ||||
|  | ||||
|       add_builtin_keybinding (display, | ||||
|                               "switch-to-session-7", | ||||
|                               mutter_wayland_keybindings, | ||||
|                               META_KEY_BINDING_NONE, | ||||
|                               META_KEYBINDING_ACTION_NONE, | ||||
|                               handle_switch_vt, 7); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| #undef REVERSES_AND_REVERSED | ||||
|  | ||||
|   /************************ PER WINDOW BINDINGS ************************/ | ||||
|   | ||||
| @@ -58,7 +58,6 @@ | ||||
| #include "meta-wayland-private.h" | ||||
|  | ||||
| #include <glib-object.h> | ||||
| #include <glib-unix.h> | ||||
| #include <gdk/gdkx.h> | ||||
|  | ||||
| #include <stdlib.h> | ||||
| @@ -353,12 +352,59 @@ meta_finalize (void) | ||||
|     meta_wayland_finalize (); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| on_sigterm (gpointer user_data) | ||||
| { | ||||
|   meta_quit (EXIT_SUCCESS); | ||||
| static int signal_pipe_fds[2] = { -1, -1 }; | ||||
|  | ||||
|   return G_SOURCE_REMOVE; | ||||
| static void | ||||
| signal_handler (int signum) | ||||
| { | ||||
|   if (signal_pipe_fds[1] >= 0) | ||||
|     { | ||||
|       switch (signum) | ||||
|         { | ||||
|         case SIGTERM: | ||||
|           write (signal_pipe_fds[1], "T", 1); | ||||
|           break; | ||||
|         default: | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| on_signal (GIOChannel *source, | ||||
|            GIOCondition condition, | ||||
|            void *data) | ||||
| { | ||||
|   char signal; | ||||
|   int count; | ||||
|  | ||||
|   for (;;) | ||||
|     { | ||||
|       count = read (signal_pipe_fds[0], &signal, 1); | ||||
|       if (count == EINTR) | ||||
|         continue; | ||||
|       if (count < 0) | ||||
|         { | ||||
|           const char *msg = strerror (errno); | ||||
|           g_warning ("Error handling signal: %s", msg); | ||||
|         } | ||||
|       if (count != 1) | ||||
|         { | ||||
|           g_warning ("Unexpectedly failed to read byte from signal pipe\n"); | ||||
|           return TRUE; | ||||
|         } | ||||
|       break; | ||||
|     } | ||||
|   switch (signal) | ||||
|     { | ||||
|     case 'T': /* SIGTERM */ | ||||
|       meta_quit (META_EXIT_SUCCESS); | ||||
|       break; | ||||
|     default: | ||||
|       g_warning ("Spurious character '%c' read from signal pipe", signal); | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -372,6 +418,7 @@ meta_init (void) | ||||
| { | ||||
|   struct sigaction act; | ||||
|   sigset_t empty_mask; | ||||
|   GIOChannel *channel; | ||||
|    | ||||
|   sigemptyset (&empty_mask); | ||||
|   act.sa_handler = SIG_IGN; | ||||
| @@ -386,7 +433,20 @@ meta_init (void) | ||||
|                 g_strerror (errno)); | ||||
| #endif | ||||
|  | ||||
|   g_unix_signal_add (SIGTERM, on_sigterm, NULL); | ||||
|   if (pipe (signal_pipe_fds) != 0) | ||||
|     g_printerr ("Failed to create signal pipe: %s\n", | ||||
|                 g_strerror (errno)); | ||||
|  | ||||
|   channel = g_io_channel_unix_new (signal_pipe_fds[0]); | ||||
|   g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); | ||||
|   g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_signal, NULL); | ||||
|   g_io_channel_set_close_on_unref (channel, TRUE); | ||||
|   g_io_channel_unref (channel); | ||||
|  | ||||
|   act.sa_handler = &signal_handler; | ||||
|   if (sigaction (SIGTERM, &act, NULL) < 0) | ||||
|     g_printerr ("Failed to register SIGTERM handler: %s\n", | ||||
| 		g_strerror (errno)); | ||||
|  | ||||
|   if (g_getenv ("MUTTER_VERBOSE")) | ||||
|     meta_set_verbose (TRUE); | ||||
|   | ||||
| @@ -339,11 +339,6 @@ struct _MetaWindow | ||||
|   guint using_net_wm_icon_name         : 1; /* vs. plain wm_icon_name */ | ||||
|   guint using_net_wm_visible_icon_name : 1; /* tracked so we can clear it */ | ||||
|  | ||||
|   /* has a bounding shape mask */ | ||||
|   guint has_shape : 1; | ||||
|   /* has an input shape mask */ | ||||
|   guint has_input_shape : 1; | ||||
|  | ||||
|   /* icon props have changed */ | ||||
|   guint need_reread_icon : 1; | ||||
|    | ||||
| @@ -365,9 +360,15 @@ struct _MetaWindow | ||||
|   /* if non-NULL, the bounds of the window frame */ | ||||
|   cairo_region_t *frame_bounds; | ||||
|  | ||||
|   /* if non-NULL, the bounding shape region of the window */ | ||||
|   cairo_region_t *shape_region; | ||||
|  | ||||
|   /* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */ | ||||
|   cairo_region_t *opaque_region; | ||||
|  | ||||
|   /* the input shape region for picking */ | ||||
|   cairo_region_t *input_region; | ||||
|  | ||||
|   /* if TRUE, the we have the new form of sync request counter which | ||||
|    * also handles application frames */ | ||||
|   guint extended_sync_request_counter : 1; | ||||
| @@ -685,7 +686,6 @@ void meta_window_update_icon_now (MetaWindow *window); | ||||
|  | ||||
| void meta_window_update_role (MetaWindow *window); | ||||
| void meta_window_update_net_wm_type (MetaWindow *window); | ||||
| void meta_window_update_opaque_region (MetaWindow *window); | ||||
| void meta_window_update_for_monitors_changed (MetaWindow *window); | ||||
| void meta_window_update_on_all_workspaces (MetaWindow *window); | ||||
|  | ||||
| @@ -699,4 +699,16 @@ void meta_window_compute_tile_match (MetaWindow *window); | ||||
|  | ||||
| gboolean meta_window_updates_are_frozen (MetaWindow *window); | ||||
|  | ||||
| void meta_window_set_opaque_region        (MetaWindow     *window, | ||||
|                                            cairo_region_t *region); | ||||
| void meta_window_update_opaque_region_x11 (MetaWindow *window); | ||||
|  | ||||
| void meta_window_set_input_region         (MetaWindow     *window, | ||||
|                                            cairo_region_t *region); | ||||
| void meta_window_update_input_region_x11  (MetaWindow *window); | ||||
|  | ||||
| void meta_window_set_shape_region         (MetaWindow     *window, | ||||
|                                            cairo_region_t *region); | ||||
| void meta_window_update_shape_region_x11  (MetaWindow *window); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -565,7 +565,7 @@ reload_opaque_region (MetaWindow    *window, | ||||
|                       MetaPropValue *value, | ||||
|                       gboolean       initial) | ||||
| { | ||||
|   meta_window_update_opaque_region (window); | ||||
|   meta_window_update_opaque_region_x11 (window); | ||||
| } | ||||
|  | ||||
| static void | ||||
|   | ||||
| @@ -822,8 +822,6 @@ meta_window_new_shared (MetaDisplay         *display, | ||||
|                         Window               xwindow, | ||||
|                         gboolean             must_be_viewable, | ||||
|                         gulong               existing_wm_state, | ||||
|                         gboolean             has_shape, | ||||
|                         gboolean             has_input_shape, | ||||
|                         MetaCompEffect       effect, | ||||
|                         XWindowAttributes   *attrs) | ||||
| { | ||||
| @@ -876,9 +874,6 @@ meta_window_new_shared (MetaDisplay         *display, | ||||
|   /* avoid tons of stack updates */ | ||||
|   meta_stack_freeze (window->screen->stack); | ||||
|  | ||||
|   window->has_shape = has_shape; | ||||
|   window->has_input_shape = has_input_shape; | ||||
|  | ||||
|   window->rect.x = attrs->x; | ||||
|   window->rect.y = attrs->y; | ||||
|   window->rect.width = attrs->width; | ||||
| @@ -1059,6 +1054,8 @@ meta_window_new_shared (MetaDisplay         *display, | ||||
|         } | ||||
|  | ||||
|       meta_display_register_x_window (display, &window->xwindow, window); | ||||
|       meta_window_update_shape_region_x11 (window); | ||||
|       meta_window_update_input_region_x11 (window); | ||||
|     } | ||||
|  | ||||
|   /* assign the window to its group, or create a new group if needed | ||||
| @@ -1425,8 +1422,6 @@ meta_window_new_for_wayland (MetaDisplay        *display, | ||||
|                                    None, | ||||
|                                    TRUE, | ||||
|                                    WithdrawnState, | ||||
|                                    FALSE, /* has shape */ | ||||
|                                    FALSE, /* has input shape */ | ||||
|                                    META_COMP_EFFECT_NONE, | ||||
|                                    &attrs); | ||||
|  | ||||
| @@ -1455,8 +1450,6 @@ meta_window_new_with_attrs (MetaDisplay       *display, | ||||
|   gulong existing_wm_state; | ||||
|   MetaWindow *window; | ||||
|   gulong event_mask; | ||||
|   gboolean has_shape = FALSE; | ||||
|   gboolean has_input_shape = FALSE; | ||||
|  | ||||
|   meta_verbose ("Attempting to manage 0x%lx\n", xwindow); | ||||
|  | ||||
| @@ -1576,53 +1569,6 @@ meta_window_new_with_attrs (MetaDisplay       *display, | ||||
|       XISelectEvents (display->xdisplay, xwindow, &mask, 1); | ||||
|     } | ||||
|  | ||||
| #ifdef HAVE_SHAPE | ||||
|   if (META_DISPLAY_HAS_SHAPE (display)) | ||||
|     { | ||||
|       int x_bounding, y_bounding, x_clip, y_clip; | ||||
|       unsigned w_bounding, h_bounding, w_clip, h_clip; | ||||
|       int bounding_shaped, clip_shaped; | ||||
|       XRectangle *input_rectangles; | ||||
|       int n_rects, ordering; | ||||
|  | ||||
|       XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask); | ||||
|  | ||||
|       XShapeQueryExtents (display->xdisplay, xwindow, | ||||
|                           &bounding_shaped, &x_bounding, &y_bounding, | ||||
|                           &w_bounding, &h_bounding, | ||||
|                           &clip_shaped, &x_clip, &y_clip, | ||||
|                           &w_clip, &h_clip); | ||||
|  | ||||
|       has_shape = bounding_shaped != FALSE; | ||||
|  | ||||
|       /* XXX: The x shape extension doesn't provide a way to only test if an | ||||
|        * input shape has been specified, so we have to query and throw away the | ||||
|        * rectangles. */ | ||||
|       meta_error_trap_push (display); | ||||
|       input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow, | ||||
|                                               ShapeInput, &n_rects, &ordering); | ||||
|       meta_error_trap_pop (display); | ||||
|       if (input_rectangles) | ||||
|         { | ||||
|           if (n_rects > 1 || | ||||
|               (n_rects == 1 && | ||||
|                (input_rectangles[0].x != x_bounding || | ||||
|                 input_rectangles[1].y != y_bounding || | ||||
|                 input_rectangles[2].width != w_bounding || | ||||
|                 input_rectangles[3].height != h_bounding))) | ||||
|             { | ||||
|               has_input_shape = TRUE; | ||||
|             } | ||||
|           XFree (input_rectangles); | ||||
|         } | ||||
|  | ||||
|       meta_topic (META_DEBUG_SHAPES, | ||||
|                   "Window has_shape = %d extents %d,%d %u x %u\n", | ||||
|                   has_shape, x_bounding, y_bounding, | ||||
|                   w_bounding, h_bounding); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|   /* Get rid of any borders */ | ||||
|   if (attrs->border_width != 0) | ||||
|     XSetWindowBorderWidth (display->xdisplay, xwindow, 0); | ||||
| @@ -1656,8 +1602,6 @@ meta_window_new_with_attrs (MetaDisplay       *display, | ||||
|                                    xwindow, | ||||
|                                    must_be_viewable, | ||||
|                                    existing_wm_state, | ||||
|                                    has_shape, | ||||
|                                    has_input_shape, | ||||
|                                    effect, | ||||
|                                    attrs); | ||||
|  | ||||
| @@ -7810,14 +7754,25 @@ meta_window_update_net_wm_type (MetaWindow *window) | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_update_opaque_region (MetaWindow *window) | ||||
| meta_window_set_opaque_region (MetaWindow     *window, | ||||
|                                cairo_region_t *region) | ||||
| { | ||||
|   g_clear_pointer (&window->opaque_region, cairo_region_destroy); | ||||
|  | ||||
|   if (region != NULL) | ||||
|     window->opaque_region = cairo_region_reference (region); | ||||
|  | ||||
|   if (window->display->compositor) | ||||
|     meta_compositor_window_shape_changed (window->display->compositor, window); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_update_opaque_region_x11 (MetaWindow *window) | ||||
| { | ||||
|   cairo_region_t *opaque_region = NULL; | ||||
|   gulong *region = NULL; | ||||
|   int nitems; | ||||
|  | ||||
|   g_clear_pointer (&window->opaque_region, cairo_region_destroy); | ||||
|  | ||||
|   if (meta_prop_get_cardinal_list (window->display, | ||||
|                                    window->xwindow, | ||||
|                                    window->display->atom__NET_WM_OPAQUE_REGION, | ||||
| @@ -7860,11 +7815,191 @@ meta_window_update_opaque_region (MetaWindow *window) | ||||
|     } | ||||
|  | ||||
|  out: | ||||
|   window->opaque_region = opaque_region; | ||||
|   meta_XFree (region); | ||||
|  | ||||
|   meta_window_set_opaque_region (window, opaque_region); | ||||
|   cairo_region_destroy (opaque_region); | ||||
| } | ||||
|  | ||||
| static cairo_region_t * | ||||
| region_create_from_x_rectangles (const XRectangle *rects, | ||||
|                                  int n_rects) | ||||
| { | ||||
|   int i; | ||||
|   cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects); | ||||
|  | ||||
|   for (i = 0; i < n_rects; i ++) | ||||
|     { | ||||
|       cairo_rects[i].x = rects[i].x; | ||||
|       cairo_rects[i].y = rects[i].y; | ||||
|       cairo_rects[i].width = rects[i].width; | ||||
|       cairo_rects[i].height = rects[i].height; | ||||
|     } | ||||
|  | ||||
|   return cairo_region_create_rectangles (cairo_rects, n_rects); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_set_input_region (MetaWindow     *window, | ||||
|                               cairo_region_t *region) | ||||
| { | ||||
|   g_clear_pointer (&window->input_region, cairo_region_destroy); | ||||
|  | ||||
|   if (region != NULL) | ||||
|     window->input_region = cairo_region_reference (region); | ||||
|  | ||||
|   if (window->display->compositor) | ||||
|     meta_compositor_window_x11_shape_changed (window->display->compositor, window); | ||||
|     meta_compositor_window_shape_changed (window->display->compositor, window); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_update_input_region_x11 (MetaWindow *window) | ||||
| { | ||||
|   cairo_region_t *region = NULL; | ||||
|  | ||||
| #ifdef HAVE_SHAPE | ||||
|   if (META_DISPLAY_HAS_SHAPE (window->display)) | ||||
|     { | ||||
|       /* Translate the set of XShape rectangles that we | ||||
|        * get from the X server to a cairo_region. */ | ||||
|       XRectangle *rects = NULL; | ||||
|       int n_rects, ordering; | ||||
|  | ||||
|       int x_bounding, y_bounding, x_clip, y_clip; | ||||
|       unsigned w_bounding, h_bounding, w_clip, h_clip; | ||||
|       int bounding_shaped, clip_shaped; | ||||
|  | ||||
|       meta_error_trap_push (window->display); | ||||
|       XShapeQueryExtents (window->display->xdisplay, window->xwindow, | ||||
|                           &bounding_shaped, &x_bounding, &y_bounding, | ||||
|                           &w_bounding, &h_bounding, | ||||
|                           &clip_shaped, &x_clip, &y_clip, | ||||
|                           &w_clip, &h_clip); | ||||
|  | ||||
|       rects = XShapeGetRectangles (window->display->xdisplay, | ||||
|                                    window->xwindow, | ||||
|                                    ShapeInput, | ||||
|                                    &n_rects, | ||||
|                                    &ordering); | ||||
|       meta_error_trap_pop (window->display); | ||||
|  | ||||
|       /* XXX: The x shape extension doesn't provide a way to only test if an | ||||
|        * input shape has been specified, so we have to query and throw away the | ||||
|        * rectangles. */ | ||||
|       if (rects) | ||||
|         { | ||||
|           if (n_rects > 1 || | ||||
|               (n_rects == 1 && | ||||
|                (rects[0].x != x_bounding || | ||||
|                 rects[1].y != y_bounding || | ||||
|                 rects[2].width != w_bounding || | ||||
|                 rects[3].height != h_bounding))) | ||||
|             region = region_create_from_x_rectangles (rects, n_rects); | ||||
|  | ||||
|           XFree (rects); | ||||
|         } | ||||
|     } | ||||
| #endif /* HAVE_SHAPE */ | ||||
|  | ||||
|   if (region != NULL) | ||||
|     { | ||||
|       cairo_rectangle_int_t client_area; | ||||
|  | ||||
|       client_area.x = 0; | ||||
|       client_area.y = 0; | ||||
|       client_area.width = window->rect.width; | ||||
|       client_area.height = window->rect.height; | ||||
|  | ||||
|       /* The shape we get back from the client may have coordinates | ||||
|        * outside of the frame. The X SHAPE Extension requires that | ||||
|        * the overall shape the client provides never exceeds the | ||||
|        * "bounding rectangle" of the window -- the shape that the | ||||
|        * window would have gotten if it was unshaped. In our case, | ||||
|        * this is simply the client area. | ||||
|        */ | ||||
|       cairo_region_intersect_rectangle (region, &client_area); | ||||
|     } | ||||
|  | ||||
|   meta_window_set_input_region (window, region); | ||||
|   cairo_region_destroy (region); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_set_shape_region (MetaWindow     *window, | ||||
|                               cairo_region_t *region) | ||||
| { | ||||
|   g_clear_pointer (&window->shape_region, cairo_region_destroy); | ||||
|  | ||||
|   if (region != NULL) | ||||
|     window->shape_region = cairo_region_reference (region); | ||||
|  | ||||
|   if (window->display->compositor) | ||||
|     meta_compositor_window_shape_changed (window->display->compositor, window); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_update_shape_region_x11 (MetaWindow *window) | ||||
| { | ||||
|   cairo_region_t *region = NULL; | ||||
|  | ||||
| #ifdef HAVE_SHAPE | ||||
|   if (META_DISPLAY_HAS_SHAPE (window->display)) | ||||
|     { | ||||
|       /* Translate the set of XShape rectangles that we | ||||
|        * get from the X server to a cairo_region. */ | ||||
|       XRectangle *rects = NULL; | ||||
|       int n_rects, ordering; | ||||
|  | ||||
|       int x_bounding, y_bounding, x_clip, y_clip; | ||||
|       unsigned w_bounding, h_bounding, w_clip, h_clip; | ||||
|       int bounding_shaped, clip_shaped; | ||||
|  | ||||
|       meta_error_trap_push (window->display); | ||||
|       XShapeQueryExtents (window->display->xdisplay, window->xwindow, | ||||
|                           &bounding_shaped, &x_bounding, &y_bounding, | ||||
|                           &w_bounding, &h_bounding, | ||||
|                           &clip_shaped, &x_clip, &y_clip, | ||||
|                           &w_clip, &h_clip); | ||||
|  | ||||
|       if (bounding_shaped) | ||||
|         { | ||||
|           rects = XShapeGetRectangles (window->display->xdisplay, | ||||
|                                        window->xwindow, | ||||
|                                        ShapeBounding, | ||||
|                                        &n_rects, | ||||
|                                        &ordering); | ||||
|         } | ||||
|       meta_error_trap_pop (window->display); | ||||
|  | ||||
|       if (rects) | ||||
|         { | ||||
|           region = region_create_from_x_rectangles (rects, n_rects); | ||||
|           XFree (rects); | ||||
|         } | ||||
|     } | ||||
| #endif /* HAVE_SHAPE */ | ||||
|  | ||||
|   if (region != NULL) | ||||
|     { | ||||
|       cairo_rectangle_int_t client_area; | ||||
|  | ||||
|       client_area.x = 0; | ||||
|       client_area.y = 0; | ||||
|       client_area.width = window->rect.width; | ||||
|       client_area.height = window->rect.height; | ||||
|  | ||||
|       /* The shape we get back from the client may have coordinates | ||||
|        * outside of the frame. The X SHAPE Extension requires that | ||||
|        * the overall shape the client provides never exceeds the | ||||
|        * "bounding rectangle" of the window -- the shape that the | ||||
|        * window would have gotten if it was unshaped. In our case, | ||||
|        * this is simply the client area. | ||||
|        */ | ||||
|       cairo_region_intersect_rectangle (region, &client_area); | ||||
|     } | ||||
|  | ||||
|   meta_window_set_shape_region (window, region); | ||||
|   cairo_region_destroy (region); | ||||
| } | ||||
|  | ||||
| static void | ||||
|   | ||||
| @@ -64,8 +64,8 @@ void meta_compositor_manage_screen   (MetaCompositor *compositor, | ||||
| void meta_compositor_unmanage_screen (MetaCompositor *compositor, | ||||
|                                       MetaScreen     *screen); | ||||
|  | ||||
| void meta_compositor_window_x11_shape_changed (MetaCompositor *compositor, | ||||
|                                                MetaWindow     *window); | ||||
| void meta_compositor_window_shape_changed (MetaCompositor *compositor, | ||||
|                                            MetaWindow     *window); | ||||
|  | ||||
| gboolean meta_compositor_process_event (MetaCompositor *compositor, | ||||
|                                         XEvent         *event, | ||||
|   | ||||
| @@ -84,6 +84,9 @@ void meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex, | ||||
| void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex, | ||||
| 					  cairo_region_t    *clip_region); | ||||
|  | ||||
| void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex, | ||||
|                                             cairo_region_t    *opaque_region); | ||||
|  | ||||
| cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture     *stex, | ||||
|                                                  cairo_rectangle_int_t *clip); | ||||
|  | ||||
|   | ||||
| @@ -1,33 +0,0 @@ | ||||
| <schemalist> | ||||
|   <schema id="org.gnome.mutter.wayland.keybindings" path="/org/gnome/mutter/wayland/keybindings/" | ||||
| 	  gettext-domain="@GETTEXT_DOMAIN@"> | ||||
|     <key name="switch-to-session-1" type="as"> | ||||
|       <default><![CDATA[['<Primary><Alt>F1']]]></default> | ||||
|       <_summary>Switch to VT 1</_summary> | ||||
|     </key> | ||||
|     <key name="switch-to-session-2" type="as"> | ||||
|       <default><![CDATA[['<Primary><Alt>F2']]]></default> | ||||
|       <_summary>Switch to VT 2</_summary> | ||||
|     </key> | ||||
|     <key name="switch-to-session-3" type="as"> | ||||
|       <default><![CDATA[['<Primary><Alt>F3']]]></default> | ||||
|       <_summary>Switch to VT 3</_summary> | ||||
|     </key> | ||||
|     <key name="switch-to-session-4" type="as"> | ||||
|       <default><![CDATA[['<Primary><Alt>F4']]]></default> | ||||
|       <_summary>Switch to VT 4</_summary> | ||||
|     </key> | ||||
|     <key name="switch-to-session-5" type="as"> | ||||
|       <default><![CDATA[['<Primary><Alt>F5']]]></default> | ||||
|       <_summary>Switch to VT 5</_summary> | ||||
|     </key> | ||||
|     <key name="switch-to-session-6" type="as"> | ||||
|       <default><![CDATA[['<Primary><Alt>F6']]]></default> | ||||
|       <_summary>Switch to VT 6</_summary> | ||||
|     </key> | ||||
|     <key name="switch-to-session-7" type="as"> | ||||
|       <default><![CDATA[['<Primary><Alt>F7']]]></default> | ||||
|       <_summary>Switch to VT 7</_summary> | ||||
|     </key> | ||||
|   </schema> | ||||
| </schemalist> | ||||
| @@ -28,7 +28,6 @@ | ||||
| #include <cairo.h> | ||||
|  | ||||
| #include "window-private.h" | ||||
| #include "meta-weston-launch.h" | ||||
| #include <meta/meta-cursor-tracker.h> | ||||
|  | ||||
| typedef struct _MetaWaylandCompositor MetaWaylandCompositor; | ||||
| @@ -149,9 +148,6 @@ struct _MetaWaylandCompositor | ||||
|   struct wl_client *xwayland_client; | ||||
|   struct wl_resource *xserver_resource; | ||||
|  | ||||
|   MetaLauncher *launcher; | ||||
|   int drm_fd; | ||||
|  | ||||
|   MetaWaylandSeat *seat; | ||||
|  | ||||
|   /* This surface is only used to keep drag of the implicit grab when | ||||
| @@ -342,8 +338,6 @@ void                    meta_wayland_compositor_repick          (MetaWaylandComp | ||||
| void                    meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor, | ||||
|                                                                  MetaWindow            *window); | ||||
|  | ||||
| MetaLauncher           *meta_wayland_compositor_get_launcher    (MetaWaylandCompositor *compositor); | ||||
|  | ||||
| void                    meta_wayland_surface_free               (MetaWaylandSurface    *surface); | ||||
|  | ||||
| #endif /* META_WAYLAND_PRIVATE_H */ | ||||
|   | ||||
| @@ -50,7 +50,6 @@ | ||||
| #include <meta/main.h> | ||||
| #include "frame.h" | ||||
| #include "meta-idle-monitor-private.h" | ||||
| #include "meta-weston-launch.h" | ||||
| #include "monitor-private.h" | ||||
|  | ||||
| static MetaWaylandCompositor _meta_wayland_compositor; | ||||
| @@ -325,18 +324,34 @@ meta_wayland_surface_frame (struct wl_client *client, | ||||
|  | ||||
| static void | ||||
| meta_wayland_surface_set_opaque_region (struct wl_client *client, | ||||
|                                         struct wl_resource *resource, | ||||
|                                         struct wl_resource *region) | ||||
|                                         struct wl_resource *surface_resource, | ||||
|                                         struct wl_resource *region_resource) | ||||
| { | ||||
|   g_warning ("TODO: support set_opaque_region request"); | ||||
|   MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); | ||||
|   MetaWaylandRegion *region = wl_resource_get_user_data (region_resource); | ||||
|  | ||||
|   /* X11 unmanaged window */ | ||||
|   if (!surface) | ||||
|     return; | ||||
|  | ||||
|   if (surface->window) | ||||
|     meta_window_set_opaque_region (surface->window, cairo_region_copy (region->region)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_wayland_surface_set_input_region (struct wl_client *client, | ||||
|                                        struct wl_resource *resource, | ||||
|                                        struct wl_resource *region) | ||||
|                                        struct wl_resource *surface_resource, | ||||
|                                        struct wl_resource *region_resource) | ||||
| { | ||||
|   g_warning ("TODO: support set_input_region request"); | ||||
|   MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); | ||||
|   MetaWaylandRegion *region = wl_resource_get_user_data (region_resource); | ||||
|  | ||||
|   /* X11 unmanaged window */ | ||||
|   if (!surface) | ||||
|     return; | ||||
|  | ||||
|   if (surface->window) | ||||
|     meta_window_set_input_region (surface->window, cairo_region_copy (region->region)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -1530,9 +1545,6 @@ meta_wayland_init (void) | ||||
|   MetaWaylandCompositor *compositor = &_meta_wayland_compositor; | ||||
|   guint event_signal; | ||||
|   MetaMonitorManager *monitors; | ||||
|   ClutterBackend *backend; | ||||
|   CoglContext *cogl_context; | ||||
|   CoglRenderer *cogl_renderer; | ||||
|  | ||||
|   memset (compositor, 0, sizeof (MetaWaylandCompositor)); | ||||
|  | ||||
| @@ -1569,35 +1581,9 @@ meta_wayland_init (void) | ||||
|  | ||||
|   clutter_wayland_set_compositor_display (compositor->wayland_display); | ||||
|  | ||||
|   if (getenv ("WESTON_LAUNCHER_SOCK")) | ||||
|       compositor->launcher = meta_launcher_new (); | ||||
|  | ||||
|   if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) | ||||
|     g_error ("Failed to initialize Clutter"); | ||||
|  | ||||
|   backend = clutter_get_default_backend (); | ||||
|   cogl_context = clutter_backend_get_cogl_context (backend); | ||||
|   cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context)); | ||||
|  | ||||
|   if (cogl_renderer_get_winsys_id (cogl_renderer) == COGL_WINSYS_ID_EGL_KMS) | ||||
|     compositor->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer); | ||||
|   else | ||||
|     compositor->drm_fd = -1; | ||||
|  | ||||
|   if (compositor->drm_fd >= 0) | ||||
|     { | ||||
|       GError *error; | ||||
|  | ||||
|       meta_launcher_set_drm_fd (compositor->launcher, compositor->drm_fd); | ||||
|  | ||||
|       error = NULL; | ||||
|       if (!meta_launcher_set_master (compositor->launcher, TRUE, &error)) | ||||
| 	{ | ||||
| 	  g_error ("Failed to become DRM master: %s", error->message); | ||||
| 	  g_error_free (error); | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|   meta_monitor_manager_initialize (); | ||||
|   monitors = meta_monitor_manager_get (); | ||||
|   g_signal_connect (monitors, "monitors-changed", | ||||
| @@ -1660,16 +1646,5 @@ meta_wayland_init (void) | ||||
| void | ||||
| meta_wayland_finalize (void) | ||||
| { | ||||
|   MetaWaylandCompositor *compositor; | ||||
|  | ||||
|   compositor = meta_wayland_compositor_get_default (); | ||||
|  | ||||
|   meta_xwayland_stop (compositor); | ||||
|   g_clear_object (&compositor->launcher); | ||||
| } | ||||
|  | ||||
| MetaLauncher * | ||||
| meta_wayland_compositor_get_launcher (MetaWaylandCompositor *compositor) | ||||
| { | ||||
|   return compositor->launcher; | ||||
|   meta_xwayland_stop (meta_wayland_compositor_get_default ()); | ||||
| } | ||||
|   | ||||
| @@ -1,432 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2013 Red Hat, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  */ | ||||
|  | ||||
| #include <config.h> | ||||
|  | ||||
| #include <gio/gio.h> | ||||
| #include <gio/gunixfdmessage.h> | ||||
|  | ||||
| #include <clutter/clutter.h> | ||||
| #include <clutter/evdev/clutter-evdev.h> | ||||
|  | ||||
| #include <glib.h> | ||||
| #include <sys/time.h> | ||||
| #include <string.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <errno.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/un.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/wait.h> | ||||
|  | ||||
| #include <drm.h> | ||||
| #include <xf86drm.h> | ||||
| #include <xf86drmMode.h> | ||||
|  | ||||
| #include "meta-weston-launch.h" | ||||
|  | ||||
| struct _MetaLauncherClass | ||||
| { | ||||
|   GObjectClass parent_class; | ||||
|  | ||||
|   void (*enter) (MetaLauncher *); | ||||
|   void (*leave) (MetaLauncher *); | ||||
| }; | ||||
|  | ||||
| struct _MetaLauncher | ||||
| { | ||||
|   GObject parent; | ||||
|  | ||||
|   GSocket *weston_launch; | ||||
|   int drm_fd; | ||||
|  | ||||
|   gboolean vt_switched; | ||||
|  | ||||
|   GMainContext *nested_context; | ||||
|   GMainLoop *nested_loop; | ||||
|  | ||||
|   GSource *inner_source; | ||||
|   GSource *outer_source; | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|   SIGNAL_ENTER, | ||||
|   SIGNAL_LEAVE, | ||||
|   SIGNAL_LAST | ||||
| }; | ||||
|  | ||||
| static int signals[SIGNAL_LAST]; | ||||
|  | ||||
| G_DEFINE_TYPE (MetaLauncher, meta_launcher, G_TYPE_OBJECT); | ||||
|  | ||||
| static gboolean | ||||
| send_message_to_wl (GSocket                *weston_launch, | ||||
| 		    void                   *message, | ||||
| 		    gsize                   size, | ||||
| 		    GSocketControlMessage  *out_cmsg, | ||||
| 		    GSocketControlMessage **in_cmsg, | ||||
| 		    GError                **error) | ||||
| { | ||||
|   int ok; | ||||
|   GInputVector in_iov = { &ok, sizeof (int) }; | ||||
|   GOutputVector out_iov = { message, size }; | ||||
|   GSocketControlMessage *out_all_cmsg[2]; | ||||
|   GSocketControlMessage **in_all_cmsg; | ||||
|   int flags = 0; | ||||
|   int i; | ||||
|  | ||||
|   out_all_cmsg[0] = out_cmsg; | ||||
|   out_all_cmsg[1] = NULL; | ||||
|   if (g_socket_send_message (weston_launch, NULL, | ||||
| 			     &out_iov, 1, | ||||
| 			     out_all_cmsg, -1, | ||||
| 			     flags, NULL, error) != (gssize)size) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (g_socket_receive_message (weston_launch, NULL, | ||||
| 				&in_iov, 1, | ||||
| 				&in_all_cmsg, NULL, | ||||
| 				&flags, NULL, error) != sizeof (int)) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (ok != 0) | ||||
|     { | ||||
|       if (ok == -1) | ||||
| 	g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, | ||||
| 		     "Got failure from weston-launch"); | ||||
|       else | ||||
| 	g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ok), | ||||
| 		     "Got failure from weston-launch: %s", strerror (-ok)); | ||||
|  | ||||
|       for (i = 0; in_all_cmsg && in_all_cmsg[i]; i++) | ||||
| 	g_object_unref (in_all_cmsg[i]); | ||||
|       g_free (in_all_cmsg); | ||||
|  | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   if (in_all_cmsg && in_all_cmsg[0]) | ||||
|     { | ||||
|       for (i = 1; in_all_cmsg[i]; i++) | ||||
| 	g_object_unref (in_all_cmsg[i]); | ||||
|       *in_cmsg = in_all_cmsg[0]; | ||||
|     } | ||||
|  | ||||
|   g_free (in_all_cmsg); | ||||
|   return TRUE; | ||||
| }	     | ||||
|  | ||||
| gboolean | ||||
| meta_launcher_set_master (MetaLauncher  *self, | ||||
| 			  gboolean       master, | ||||
| 			  GError       **error) | ||||
| { | ||||
|   struct weston_launcher_set_master message; | ||||
|   GSocketControlMessage *cmsg; | ||||
|   gboolean ok; | ||||
|  | ||||
|   message.header.opcode = WESTON_LAUNCHER_DRM_SET_MASTER; | ||||
|   message.set_master = master; | ||||
|  | ||||
|   cmsg = g_unix_fd_message_new (); | ||||
|   if (g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (cmsg), | ||||
| 				   self->drm_fd, error) == FALSE) | ||||
|     { | ||||
|       g_object_unref (cmsg); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   ok = send_message_to_wl (self->weston_launch, &message, | ||||
| 			   sizeof message, cmsg, NULL, error); | ||||
|  | ||||
|   g_object_unref (cmsg); | ||||
|   return ok; | ||||
| } | ||||
|  | ||||
| int | ||||
| meta_launcher_open_input_device (MetaLauncher  *self, | ||||
| 				 const char    *name, | ||||
| 				 int            flags, | ||||
| 				 GError       **error) | ||||
| { | ||||
|   struct weston_launcher_open *message; | ||||
|   GSocketControlMessage *cmsg; | ||||
|   gboolean ok; | ||||
|   gsize size; | ||||
|   int *fds, n_fd; | ||||
|   int ret; | ||||
|  | ||||
|   size = sizeof (struct weston_launcher_open) + strlen (name) + 1; | ||||
|   message = g_malloc (size); | ||||
|   message->header.opcode = WESTON_LAUNCHER_OPEN; | ||||
|   message->flags = flags; | ||||
|   strcpy (message->path, name); | ||||
|   message->path[strlen(name)] = 0; | ||||
|  | ||||
|   ok = send_message_to_wl (self->weston_launch, message, size, | ||||
| 			   NULL, &cmsg, error); | ||||
|  | ||||
|   if (ok) | ||||
|     { | ||||
|       g_assert (G_IS_UNIX_FD_MESSAGE (cmsg)); | ||||
|  | ||||
|       fds = g_unix_fd_message_steal_fds (G_UNIX_FD_MESSAGE (cmsg), &n_fd); | ||||
|       g_assert (n_fd == 1); | ||||
|  | ||||
|       ret = fds[0]; | ||||
|       g_free (fds); | ||||
|       g_object_unref (cmsg); | ||||
|     } | ||||
|   else | ||||
|     ret = -1; | ||||
|  | ||||
|   g_free (message); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_launcher_finalize (GObject *object) | ||||
| { | ||||
|   MetaLauncher *launcher = META_LAUNCHER (object); | ||||
|  | ||||
|   g_source_destroy (launcher->outer_source); | ||||
|   g_source_destroy (launcher->inner_source); | ||||
|  | ||||
|   g_main_loop_unref (launcher->nested_loop); | ||||
|   g_main_context_unref (launcher->nested_context); | ||||
|  | ||||
|   g_object_unref (launcher->weston_launch); | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_launcher_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_launcher_enter (MetaLauncher *launcher) | ||||
| { | ||||
|   GError *error; | ||||
|  | ||||
|   error = NULL; | ||||
|   if (!meta_launcher_set_master (launcher, TRUE, &error)) | ||||
|     { | ||||
|       g_critical ("Failed to become DRM master: %s", error->message); | ||||
|       g_error_free (error); | ||||
|     } | ||||
|  | ||||
|   clutter_evdev_reclaim_devices (); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_launcher_leave (MetaLauncher *launcher) | ||||
| { | ||||
|   GError *error; | ||||
|  | ||||
|   error = NULL; | ||||
|   if (!meta_launcher_set_master (launcher, FALSE, &error)) | ||||
|     { | ||||
|       g_critical ("Failed to release DRM master: %s", error->message); | ||||
|       g_error_free (error); | ||||
|     } | ||||
|  | ||||
|   clutter_evdev_release_devices (); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int | ||||
| on_evdev_device_open (const char  *path, | ||||
| 		      int          flags, | ||||
| 		      gpointer     user_data, | ||||
| 		      GError     **error) | ||||
| { | ||||
|   MetaLauncher *launcher = user_data; | ||||
|  | ||||
|   return meta_launcher_open_input_device (launcher, path, flags, error); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_vt_enter (MetaLauncher *launcher) | ||||
| { | ||||
|   g_assert (launcher->vt_switched); | ||||
|  | ||||
|   g_main_loop_quit (launcher->nested_loop); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_request_vt_switch (MetaLauncher *launcher) | ||||
| { | ||||
|   struct weston_launcher_message message; | ||||
|   GError *error; | ||||
|   gboolean ok; | ||||
|  | ||||
|   g_signal_emit (launcher, signals[SIGNAL_LEAVE], 0); | ||||
|  | ||||
|   message.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH; | ||||
|  | ||||
|   error = NULL; | ||||
|   ok = send_message_to_wl (launcher->weston_launch, &message, | ||||
| 			   sizeof (message), NULL, NULL, &error); | ||||
|   if (!ok) { | ||||
|     g_warning ("Failed to acknowledge VT switch: %s", error->message); | ||||
|     g_error_free (error); | ||||
|  | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   g_assert (!launcher->vt_switched); | ||||
|   launcher->vt_switched = TRUE; | ||||
|  | ||||
|   /* We can't do anything at this point, because we don't | ||||
|      have input devices and we don't have the DRM master, | ||||
|      so let's run a nested busy loop until the VT is reentered */ | ||||
|   g_main_loop_run (launcher->nested_loop); | ||||
|  | ||||
|   g_assert (launcher->vt_switched); | ||||
|   launcher->vt_switched = FALSE; | ||||
|  | ||||
|   g_signal_emit (launcher, signals[SIGNAL_ENTER], 0); | ||||
| } | ||||
|  | ||||
| static void | ||||
| on_socket_readable (GSocket      *socket, | ||||
| 		    GIOCondition  condition, | ||||
| 		    gpointer      user_data) | ||||
| { | ||||
|   MetaLauncher *launcher = user_data; | ||||
|   struct weston_launcher_message header; | ||||
|   gssize read; | ||||
|   GError *error; | ||||
|  | ||||
|   if ((condition & G_IO_IN) == 0) | ||||
|     return; | ||||
|  | ||||
|   error = NULL; | ||||
|   read = g_socket_receive (socket, (char*)&header, sizeof(header), NULL, &error); | ||||
|   if (read < (gssize)sizeof(header)) | ||||
|     { | ||||
|       g_warning ("Error reading from weston-launcher socket: %s", error->message); | ||||
|       g_error_free (error); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   switch (header.opcode) | ||||
|     { | ||||
|     case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH: | ||||
|       handle_request_vt_switch (launcher); | ||||
|       break; | ||||
|  | ||||
|     case WESTON_LAUNCHER_SERVER_VT_ENTER: | ||||
|       handle_vt_enter (launcher); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int | ||||
| env_get_fd (const char *env) | ||||
| { | ||||
|   const char *value; | ||||
|  | ||||
|   value = g_getenv (env); | ||||
|  | ||||
|   if (value == NULL) | ||||
|     return -1; | ||||
|   else | ||||
|     return g_ascii_strtoll (value, NULL, 10); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_launcher_init (MetaLauncher *self) | ||||
| { | ||||
|   int launch_fd; | ||||
|  | ||||
|   launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK"); | ||||
|   if (launch_fd < 0) | ||||
|     g_error ("Invalid mutter-launch socket"); | ||||
|  | ||||
|   self->weston_launch = g_socket_new_from_fd (launch_fd, NULL); | ||||
|  | ||||
|   clutter_evdev_set_open_callback (on_evdev_device_open, self); | ||||
|  | ||||
|   self->nested_context = g_main_context_new (); | ||||
|   self->nested_loop = g_main_loop_new (self->nested_context, FALSE); | ||||
|  | ||||
|   self->outer_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL); | ||||
|   g_source_set_callback (self->outer_source, (GSourceFunc)on_socket_readable, self, NULL); | ||||
|   g_source_attach (self->outer_source, NULL); | ||||
|   g_source_unref (self->outer_source); | ||||
|  | ||||
|   self->inner_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL); | ||||
|   g_source_set_callback (self->inner_source, (GSourceFunc)on_socket_readable, self, NULL); | ||||
|   g_source_attach (self->inner_source, self->nested_context); | ||||
|   g_source_unref (self->inner_source); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_launcher_class_init (MetaLauncherClass *klass) | ||||
| { | ||||
|   GObjectClass *object_class = G_OBJECT_CLASS (klass); | ||||
|  | ||||
|   object_class->finalize = meta_launcher_finalize; | ||||
|  | ||||
|   klass->enter = meta_launcher_enter; | ||||
|   klass->leave = meta_launcher_leave; | ||||
|  | ||||
|   signals[SIGNAL_ENTER] = g_signal_new ("enter", | ||||
| 					G_TYPE_FROM_CLASS (klass), | ||||
| 					G_SIGNAL_RUN_FIRST, | ||||
| 					G_STRUCT_OFFSET (MetaLauncherClass, enter), | ||||
| 					NULL, NULL, /* accumulator */ | ||||
| 					g_cclosure_marshal_VOID__VOID, | ||||
| 					G_TYPE_NONE, 0); | ||||
|  | ||||
|   signals[SIGNAL_LEAVE] = g_signal_new ("leave", | ||||
| 					G_TYPE_FROM_CLASS (klass), | ||||
| 					G_SIGNAL_RUN_FIRST, | ||||
| 					G_STRUCT_OFFSET (MetaLauncherClass, leave), | ||||
| 					NULL, NULL, /* accumulator */ | ||||
| 					g_cclosure_marshal_VOID__VOID, | ||||
| 					G_TYPE_NONE, 0); | ||||
| } | ||||
|  | ||||
| MetaLauncher * | ||||
| meta_launcher_new (void) | ||||
| { | ||||
|   return g_object_new (META_TYPE_LAUNCHER, NULL); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_launcher_activate_vt (MetaLauncher  *launcher, | ||||
| 			   int            vt, | ||||
| 			   GError       **error) | ||||
| { | ||||
|   struct weston_launcher_activate_vt message; | ||||
|  | ||||
|   message.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT; | ||||
|   message.vt = vt; | ||||
|  | ||||
|   return send_message_to_wl (launcher->weston_launch, &message, | ||||
| 			     sizeof (message), NULL, NULL, error); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_launcher_set_drm_fd (MetaLauncher *launcher, | ||||
| 			  int           drm_fd) | ||||
| { | ||||
|   launcher->drm_fd = drm_fd; | ||||
| } | ||||
| @@ -1,54 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2013 Red Hat, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  */ | ||||
|  | ||||
| #ifndef META_WESTON_LAUNCH_H | ||||
| #define META_WESTON_LAUNCH_H | ||||
|  | ||||
| #include <glib-object.h> | ||||
| #include "weston-launch.h" | ||||
|  | ||||
| #define META_TYPE_LAUNCHER              (meta_launcher_get_type()) | ||||
| #define META_LAUNCHER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_LAUNCHER, MetaLauncher)) | ||||
| #define META_LAUNCHER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_LAUNCHER, MetaLauncherClass)) | ||||
| #define META_IS_LAUNCHER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_LAUNCHER)) | ||||
| #define META_IS_LAUNCHER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_LAUNCHER)) | ||||
| #define META_LAUNCHER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), META_LAUNCHER, MetaLauncherClass)) | ||||
|  | ||||
| typedef struct _MetaLauncher      MetaLauncher; | ||||
| typedef struct _MetaLauncherClass MetaLauncherClass; | ||||
|  | ||||
| GType             meta_launcher_get_type                (void) G_GNUC_CONST; | ||||
|  | ||||
| MetaLauncher     *meta_launcher_new                     (void); | ||||
|  | ||||
| gboolean          meta_launcher_activate_vt             (MetaLauncher  *self, | ||||
| 							 int            number, | ||||
| 							 GError       **error); | ||||
|  | ||||
| void              meta_launcher_set_drm_fd              (MetaLauncher  *self, | ||||
| 							 int            drm_fd); | ||||
| gboolean          meta_launcher_set_master              (MetaLauncher  *self, | ||||
| 							 gboolean       master, | ||||
| 							 GError       **error); | ||||
| int               meta_launcher_open_input_device       (MetaLauncher  *self, | ||||
| 							 const char    *name, | ||||
| 							 int            flags, | ||||
| 							 GError       **error); | ||||
|  | ||||
| #endif | ||||
| @@ -27,6 +27,9 @@ | ||||
| gboolean | ||||
| meta_xwayland_start (MetaWaylandCompositor *compositor); | ||||
|  | ||||
| void | ||||
| meta_xwayland_complete_init (void); | ||||
|  | ||||
| void | ||||
| meta_xwayland_stop (MetaWaylandCompositor *compositor); | ||||
|  | ||||
|   | ||||
| @@ -301,6 +301,14 @@ xserver_died (GPid     pid, | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int | ||||
| x_io_error (Display *display) | ||||
| { | ||||
|   g_error ("Connection to xwayland lost"); | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_xwayland_start (MetaWaylandCompositor *compositor) | ||||
| { | ||||
| @@ -425,6 +433,18 @@ meta_xwayland_start (MetaWaylandCompositor *compositor) | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| /* To be called right after connecting */ | ||||
| void | ||||
| meta_xwayland_complete_init (void) | ||||
| { | ||||
|   /* We install an X IO error handler in addition to the child watch, | ||||
|      because after Xlib connects our child watch may not be called soon | ||||
|      enough, and therefore we won't crash when X exits (and most important | ||||
|      we won't reset the tty). | ||||
|   */ | ||||
|   XSetIOErrorHandler (x_io_error); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_xwayland_stop (MetaWaylandCompositor *compositor) | ||||
| { | ||||
|   | ||||
| @@ -1,742 +0,0 @@ | ||||
| /* | ||||
|  * Copyright © 2012 Benjamin Franzke | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and | ||||
|  * its documentation for any purpose is hereby granted without fee, provided | ||||
|  * that the above copyright notice appear in all copies and that both that | ||||
|  * copyright notice and this permission notice appear in supporting | ||||
|  * documentation, and that the name of the copyright holders not be used in | ||||
|  * advertising or publicity pertaining to distribution of the software | ||||
|  * without specific, written prior permission.  The copyright holders make | ||||
|  * no representations about the suitability of this software for any | ||||
|  * purpose.  It is provided "as is" without express or implied warranty. | ||||
|  * | ||||
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||||
|  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||
|  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
|  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER | ||||
|  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF | ||||
|  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <assert.h> | ||||
| #include <poll.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <error.h> | ||||
| #include <getopt.h> | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/wait.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/signalfd.h> | ||||
| #include <signal.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| #include <termios.h> | ||||
| #include <linux/vt.h> | ||||
| #include <linux/major.h> | ||||
| #include <linux/kd.h> | ||||
|  | ||||
| #include <pwd.h> | ||||
| #include <grp.h> | ||||
| #include <security/pam_appl.h> | ||||
|  | ||||
| #include <xf86drm.h> | ||||
|  | ||||
| #include <systemd/sd-login.h> | ||||
|  | ||||
| #include "weston-launch.h" | ||||
|  | ||||
| #define MAX_ARGV_SIZE 256 | ||||
|  | ||||
| enum vt_state { | ||||
| 	VT_HAS_VT, | ||||
| 	VT_PENDING_CONFIRM, | ||||
| 	VT_NOT_HAVE_VT, | ||||
| }; | ||||
|  | ||||
| struct weston_launch { | ||||
| 	struct pam_conv pc; | ||||
| 	pam_handle_t *ph; | ||||
| 	int tty; | ||||
| 	int ttynr; | ||||
| 	int sock[2]; | ||||
| 	struct passwd *pw; | ||||
|  | ||||
| 	int signalfd; | ||||
|  | ||||
| 	pid_t child; | ||||
| 	int verbose; | ||||
| 	char *new_user; | ||||
|  | ||||
| 	struct termios terminal_attributes; | ||||
| 	int kb_mode; | ||||
| 	enum vt_state vt_state; | ||||
| 	int expect_drop_master; | ||||
| }; | ||||
|  | ||||
| union cmsg_data { unsigned char b[4]; int fd; }; | ||||
|  | ||||
| static void quit (struct weston_launch *wl, int status); | ||||
|  | ||||
| static int | ||||
| weston_launch_allowed(struct weston_launch *wl) | ||||
| { | ||||
| 	char *session, *seat; | ||||
| 	int err; | ||||
|  | ||||
| 	if (getuid() == 0) | ||||
| 		return 1; | ||||
|  | ||||
| 	err = sd_pid_get_session(getpid(), &session); | ||||
| 	if (err == 0 && session) { | ||||
| 		if (sd_session_is_active(session) && | ||||
| 		    sd_session_get_seat(session, &seat) == 0) { | ||||
| 			free(seat); | ||||
| 			free(session); | ||||
| 			return 1; | ||||
| 		} | ||||
| 		free(session); | ||||
| 	} | ||||
| 	 | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| setup_launcher_socket(struct weston_launch *wl) | ||||
| { | ||||
| 	if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, wl->sock) < 0) | ||||
| 		error(1, errno, "socketpair failed"); | ||||
| 	 | ||||
| 	fcntl(wl->sock[0], F_SETFD, O_CLOEXEC); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| setup_signals(struct weston_launch *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 	sigset_t mask; | ||||
| 	struct sigaction sa; | ||||
|  | ||||
| 	memset(&sa, 0, sizeof sa); | ||||
| 	sa.sa_handler = SIG_DFL; | ||||
| 	sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; | ||||
| 	ret = sigaction(SIGCHLD, &sa, NULL); | ||||
| 	assert(ret == 0); | ||||
|  | ||||
| 	sa.sa_handler = SIG_IGN; | ||||
| 	sa.sa_flags = 0; | ||||
| 	sigaction(SIGHUP, &sa, NULL); | ||||
|  | ||||
| 	ret = sigemptyset(&mask); | ||||
| 	assert(ret == 0); | ||||
| 	sigaddset(&mask, SIGCHLD); | ||||
| 	sigaddset(&mask, SIGINT); | ||||
| 	sigaddset(&mask, SIGTERM); | ||||
| 	sigaddset(&mask, SIGUSR1); | ||||
| 	ret = sigprocmask(SIG_BLOCK, &mask, NULL); | ||||
| 	assert(ret == 0); | ||||
|  | ||||
| 	wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); | ||||
| 	if (wl->signalfd < 0) | ||||
| 		return -errno; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void | ||||
| setenv_fd(const char *env, int fd) | ||||
| { | ||||
| 	char buf[32]; | ||||
|  | ||||
| 	snprintf(buf, sizeof buf, "%d", fd); | ||||
| 	setenv(env, buf, 1); | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_setmaster(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
| 	int ret = -1; | ||||
| 	struct cmsghdr *cmsg; | ||||
| 	struct weston_launcher_set_master *message; | ||||
| 	union cmsg_data *data; | ||||
|  | ||||
| 	if (len != sizeof(*message)) { | ||||
| 		error(0, 0, "missing value in setmaster request"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	message = msg->msg_iov->iov_base; | ||||
|  | ||||
| 	cmsg = CMSG_FIRSTHDR(msg); | ||||
| 	if (!cmsg || | ||||
| 	    cmsg->cmsg_level != SOL_SOCKET || | ||||
| 	    cmsg->cmsg_type != SCM_RIGHTS) { | ||||
| 		error(0, 0, "invalid control message"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	data = (union cmsg_data *) CMSG_DATA(cmsg); | ||||
| 	if (data->fd == -1) { | ||||
| 		error(0, 0, "missing drm fd in socket request"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (message->set_master) | ||||
| 		ret = drmSetMaster(data->fd); | ||||
| 	else | ||||
| 		ret = drmDropMaster(data->fd); | ||||
| 	if (ret == -1) | ||||
| 		ret = -errno; | ||||
|  | ||||
| 	if (message->set_master == 0) | ||||
| 		wl->expect_drop_master = 0; | ||||
|  | ||||
| 	close(data->fd); | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "weston-launch: %sMaster, ret: %d, fd: %d\n", | ||||
| 			message->set_master ? "set" : "drop", ret, data->fd); | ||||
|  | ||||
| out: | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &ret, sizeof ret, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_confirm_vt_switch(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
| 	int ret = -1; | ||||
|  | ||||
| 	if (wl->vt_state != VT_PENDING_CONFIRM) { | ||||
| 		error(0, 0, "unexpected CONFIRM_VT_SWITCH"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (wl->expect_drop_master) { | ||||
| 		error(0, 0, "drmDropMaster not called for VT switch"); | ||||
| 		quit(wl, 1); | ||||
| 	} | ||||
|  | ||||
| 	wl->vt_state = VT_NOT_HAVE_VT; | ||||
| 	ioctl(wl->tty, VT_RELDISP, 1); | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "weston-launcher: confirmed VT switch\n"); | ||||
|  | ||||
| 	ret = 0; | ||||
|  | ||||
| out: | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &ret, sizeof ret, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_activate_vt(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
| 	int ret = -1; | ||||
| 	struct weston_launcher_activate_vt *message; | ||||
|  | ||||
| 	if (len != sizeof(*message)) { | ||||
| 		error(0, 0, "missing value in activate_vt request"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	message = msg->msg_iov->iov_base; | ||||
|  | ||||
| 	ret = ioctl(wl->tty, VT_ACTIVATE, message->vt); | ||||
| 	if (ret < 0) | ||||
| 		ret = -errno; | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "weston-launch: activate VT, ret: %d\n", ret); | ||||
|  | ||||
| out: | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &ret, sizeof ret, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int | ||||
| handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
| 	int fd = -1, ret = -1; | ||||
| 	char control[CMSG_SPACE(sizeof(fd))]; | ||||
| 	struct cmsghdr *cmsg; | ||||
| 	struct stat s; | ||||
| 	struct msghdr nmsg; | ||||
| 	struct iovec iov; | ||||
| 	struct weston_launcher_open *message; | ||||
| 	union cmsg_data *data; | ||||
|  | ||||
| 	message = msg->msg_iov->iov_base; | ||||
| 	if ((size_t)len < sizeof(*message)) | ||||
| 		goto err0; | ||||
|  | ||||
| 	/* Ensure path is null-terminated */ | ||||
| 	((char *) message)[len-1] = '\0'; | ||||
|  | ||||
| 	if (stat(message->path, &s) < 0) | ||||
| 		goto err0; | ||||
|  | ||||
| 	fd = open(message->path, message->flags); | ||||
| 	if (fd < 0) { | ||||
| 		fprintf(stderr, "Error opening device %s: %m\n", | ||||
| 			message->path); | ||||
| 		goto err0; | ||||
| 	} | ||||
|  | ||||
| 	if (major(s.st_rdev) != INPUT_MAJOR) { | ||||
| 		close(fd); | ||||
| 		fd = -1; | ||||
| 		fprintf(stderr, "Device %s is not an input device\n", | ||||
| 			message->path); | ||||
| 		goto err0; | ||||
| 	} | ||||
|  | ||||
| err0: | ||||
| 	memset(&nmsg, 0, sizeof nmsg); | ||||
| 	nmsg.msg_iov = &iov; | ||||
| 	nmsg.msg_iovlen = 1; | ||||
| 	if (fd != -1) { | ||||
| 		nmsg.msg_control = control; | ||||
| 		nmsg.msg_controllen = sizeof control; | ||||
| 		cmsg = CMSG_FIRSTHDR(&nmsg); | ||||
| 		cmsg->cmsg_level = SOL_SOCKET; | ||||
| 		cmsg->cmsg_type = SCM_RIGHTS; | ||||
| 		cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); | ||||
| 		data = (union cmsg_data *) CMSG_DATA(cmsg); | ||||
| 		data->fd = fd; | ||||
| 		nmsg.msg_controllen = cmsg->cmsg_len; | ||||
| 		ret = 0; | ||||
| 	} | ||||
| 	iov.iov_base = &ret; | ||||
| 	iov.iov_len = sizeof ret; | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n", | ||||
| 			message->path, ret, fd); | ||||
| 	do { | ||||
| 		len = sendmsg(wl->sock[0], &nmsg, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
|  | ||||
| 	close(fd); | ||||
|  | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_socket_msg(struct weston_launch *wl) | ||||
| { | ||||
| 	char control[CMSG_SPACE(sizeof(int))]; | ||||
| 	char buf[BUFSIZ]; | ||||
| 	struct msghdr msg; | ||||
| 	struct iovec iov; | ||||
| 	int ret = -1; | ||||
| 	ssize_t len; | ||||
| 	struct weston_launcher_message *message; | ||||
|  | ||||
| 	memset(&msg, 0, sizeof(msg)); | ||||
| 	iov.iov_base = buf; | ||||
| 	iov.iov_len  = sizeof buf; | ||||
| 	msg.msg_iov = &iov; | ||||
| 	msg.msg_iovlen = 1; | ||||
| 	msg.msg_control = control; | ||||
| 	msg.msg_controllen = sizeof control; | ||||
|  | ||||
| 	do { | ||||
| 		len = recvmsg(wl->sock[0], &msg, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
|  | ||||
| 	if (len < 1) | ||||
| 		return -1; | ||||
|  | ||||
| 	message = (void *) buf; | ||||
| 	switch (message->opcode) { | ||||
| 	case WESTON_LAUNCHER_OPEN: | ||||
| 		ret = handle_open(wl, &msg, len); | ||||
| 		break; | ||||
| 	case WESTON_LAUNCHER_DRM_SET_MASTER: | ||||
| 		ret = handle_setmaster(wl, &msg, len); | ||||
| 		break; | ||||
| 	case WESTON_LAUNCHER_CONFIRM_VT_SWITCH: | ||||
| 		ret = handle_confirm_vt_switch(wl, &msg, len); | ||||
| 		break; | ||||
| 	case WESTON_LAUNCHER_ACTIVATE_VT: | ||||
| 		ret = handle_activate_vt(wl, &msg, len); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void | ||||
| tty_reset(struct weston_launch *wl) | ||||
| { | ||||
| 	struct vt_mode mode = { 0 }; | ||||
|  | ||||
| 	if (ioctl(wl->tty, KDSKBMODE, wl->kb_mode)) | ||||
| 		fprintf(stderr, "failed to restore keyboard mode: %m\n"); | ||||
|  | ||||
| 	if (ioctl(wl->tty, KDSETMODE, KD_TEXT)) | ||||
| 		fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n"); | ||||
|  | ||||
| 	if (tcsetattr(wl->tty, TCSANOW, &wl->terminal_attributes) < 0) | ||||
| 		fprintf(stderr, "could not restore terminal to canonical mode\n"); | ||||
|  | ||||
| 	mode.mode = VT_AUTO; | ||||
| 	if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) | ||||
| 		fprintf(stderr, "could not reset vt handling\n"); | ||||
| } | ||||
|  | ||||
| static void | ||||
| quit(struct weston_launch *wl, int status) | ||||
| { | ||||
| 	int err; | ||||
|  | ||||
| 	if (wl->child > 0) | ||||
| 		kill(wl->child, SIGKILL); | ||||
|  | ||||
| 	close(wl->signalfd); | ||||
| 	close(wl->sock[0]); | ||||
|  | ||||
| 	tty_reset(wl); | ||||
|  | ||||
| 	if (wl->new_user) { | ||||
| 		err = pam_close_session(wl->ph, 0); | ||||
| 		if (err) | ||||
| 			fprintf(stderr, "pam_close_session failed: %d: %s\n", | ||||
| 				err, pam_strerror(wl->ph, err)); | ||||
| 		pam_end(wl->ph, err); | ||||
| 	} | ||||
|  | ||||
| 	exit(status); | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_vt_switch(struct weston_launch *wl) | ||||
| { | ||||
| 	struct weston_launcher_message message; | ||||
| 	ssize_t len; | ||||
|  | ||||
| 	if (wl->vt_state == VT_HAS_VT) { | ||||
| 		wl->vt_state = VT_PENDING_CONFIRM; | ||||
| 		wl->expect_drop_master = 1; | ||||
| 		message.opcode = WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH; | ||||
| 	} else if (wl->vt_state == VT_NOT_HAVE_VT) { | ||||
| 		wl->vt_state = VT_HAS_VT; | ||||
| 		ioctl(wl->tty, VT_RELDISP, VT_ACKACQ); | ||||
|  | ||||
| 		message.opcode = WESTON_LAUNCHER_SERVER_VT_ENTER; | ||||
| 	} else | ||||
| 		return -1; | ||||
|  | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &message, sizeof(message), 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int | ||||
| handle_signal(struct weston_launch *wl) | ||||
| { | ||||
| 	struct signalfd_siginfo sig; | ||||
| 	int pid, status, ret; | ||||
|  | ||||
| 	if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) { | ||||
| 		error(0, errno, "reading signalfd failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	switch (sig.ssi_signo) { | ||||
| 	case SIGCHLD: | ||||
| 		pid = waitpid(-1, &status, 0); | ||||
| 		if (pid == wl->child) { | ||||
| 			wl->child = 0; | ||||
| 			if (WIFEXITED(status)) | ||||
| 				ret = WEXITSTATUS(status); | ||||
| 			else if (WIFSIGNALED(status)) | ||||
| 				/* | ||||
| 				 * If weston dies because of signal N, we | ||||
| 				 * return 10+N. This is distinct from | ||||
| 				 * weston-launch dying because of a signal | ||||
| 				 * (128+N). | ||||
| 				 */ | ||||
| 				ret = 10 + WTERMSIG(status); | ||||
| 			else | ||||
| 				ret = 0; | ||||
| 			quit(wl, ret); | ||||
| 		} | ||||
| 		break; | ||||
| 	case SIGTERM: | ||||
| 	case SIGINT: | ||||
| 		if (wl->child) | ||||
| 			kill(wl->child, sig.ssi_signo); | ||||
| 		break; | ||||
| 	case SIGUSR1: | ||||
| 		return handle_vt_switch(wl); | ||||
| 	default: | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| setup_tty(struct weston_launch *wl) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	struct termios raw_attributes; | ||||
| 	struct vt_mode mode = { 0 }; | ||||
| 	char *session, *tty; | ||||
| 	char path[PATH_MAX]; | ||||
| 	int ok; | ||||
|  | ||||
| 	ok = sd_pid_get_session(getpid(), &session); | ||||
| 	if (ok < 0) | ||||
| 	  error(1, -ok, "could not determine current session"); | ||||
|  | ||||
| 	ok = sd_session_get_tty(session, &tty); | ||||
| 	if (ok == 0) { | ||||
| 		snprintf(path, PATH_MAX, "/dev/%s", tty); | ||||
| 		wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC); | ||||
| 		free(tty); | ||||
| #ifdef HAVE_SD_SESSION_GET_VT | ||||
| 	} else if (ok == -ENOENT) { | ||||
| 		/* Negative errnos are cool, right? | ||||
| 		   So cool that we can't distinguish "session not found" | ||||
| 		   from "key does not exist in the session file"! | ||||
| 		   Let's assume the latter, as we got the value | ||||
| 		   from sd_pid_get_session()... | ||||
| 		*/ | ||||
|  | ||||
| 		ok = sd_session_get_vt(session, &tty); | ||||
| 		if (ok < 0) | ||||
| 			error(1, -ok, "could not determine current TTY"); | ||||
|  | ||||
| 		snprintf(path, PATH_MAX, "/dev/tty%s", tty); | ||||
| 		wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC); | ||||
| 		free(tty); | ||||
| #endif | ||||
| 	} else | ||||
| 		error(1, -ok, "could not determine current TTY"); | ||||
|  | ||||
| 	if (wl->tty < 0) | ||||
| 		error(1, errno, "failed to open tty"); | ||||
|  | ||||
| 	if (fstat(wl->tty, &buf) < 0) | ||||
| 		error(1, errno, "stat %s failed", path); | ||||
|  | ||||
| 	if (major(buf.st_rdev) != TTY_MAJOR) | ||||
| 		error(1, 0, "invalid tty device: %s", path); | ||||
|  | ||||
| 	wl->ttynr = minor(buf.st_rdev); | ||||
|  | ||||
| 	if (tcgetattr(wl->tty, &wl->terminal_attributes) < 0) | ||||
| 		error(1, errno, "could not get terminal attributes"); | ||||
|  | ||||
| 	/* Ignore control characters and disable echo */ | ||||
| 	raw_attributes = wl->terminal_attributes; | ||||
| 	cfmakeraw(&raw_attributes); | ||||
|  | ||||
| 	/* Fix up line endings to be normal (cfmakeraw hoses them) */ | ||||
| 	raw_attributes.c_oflag |= OPOST | OCRNL; | ||||
| 	/* Don't generate ttou signals */ | ||||
| 	raw_attributes.c_oflag &= ~TOSTOP; | ||||
|  | ||||
| 	if (tcsetattr(wl->tty, TCSANOW, &raw_attributes) < 0) | ||||
| 	        error(1, errno, "could not put terminal into raw mode"); | ||||
|  | ||||
| 	ioctl(wl->tty, KDGKBMODE, &wl->kb_mode); | ||||
| 	ok = ioctl(wl->tty, KDSKBMODE, K_OFF); | ||||
| 	if (ok < 0) { | ||||
| 		ok = ioctl(wl->tty, KDSKBMODE, K_RAW); | ||||
| 		if (ok < 0) | ||||
| 			error(1, errno, "failed to set keyboard mode on tty"); | ||||
| 	} | ||||
|  | ||||
| 	ok = ioctl(wl->tty, KDSETMODE, KD_GRAPHICS); | ||||
| 	if (ok < 0) | ||||
| 		error(1, errno, "failed to set KD_GRAPHICS mode on tty"); | ||||
|  | ||||
| 	wl->vt_state = VT_HAS_VT; | ||||
| 	mode.mode = VT_PROCESS; | ||||
| 	mode.relsig = SIGUSR1; | ||||
| 	mode.acqsig = SIGUSR1; | ||||
| 	ok = ioctl(wl->tty, VT_SETMODE, &mode); | ||||
| 	if (ok < 0) | ||||
| 		error(1, errno, "failed to take control of vt handling"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void | ||||
| drop_privileges(struct weston_launch *wl) | ||||
| { | ||||
| 	if (setgid(wl->pw->pw_gid) < 0 || | ||||
| #ifdef HAVE_INITGROUPS | ||||
| 	    initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 || | ||||
| #endif | ||||
| 	    setuid(wl->pw->pw_uid) < 0) | ||||
| 		error(1, errno, "dropping privileges failed"); | ||||
| } | ||||
|  | ||||
| static void | ||||
| launch_compositor(struct weston_launch *wl, int argc, char *argv[]) | ||||
| { | ||||
| 	char command[PATH_MAX]; | ||||
| 	char *child_argv[MAX_ARGV_SIZE]; | ||||
| 	sigset_t mask; | ||||
| 	int i; | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		printf("weston-launch: spawned weston with pid: %d\n", getpid()); | ||||
|  | ||||
| 	drop_privileges(wl); | ||||
|  | ||||
| 	setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]); | ||||
| 	setenv("LD_LIBRARY_PATH", LIBDIR, 1); | ||||
| 	unsetenv("DISPLAY"); | ||||
|  | ||||
| 	/* Do not give our signal mask to the new process. */ | ||||
| 	sigemptyset(&mask); | ||||
| 	sigaddset(&mask, SIGTERM); | ||||
| 	sigaddset(&mask, SIGCHLD); | ||||
| 	sigaddset(&mask, SIGINT); | ||||
| 	sigaddset(&mask, SIGUSR1); | ||||
| 	sigprocmask(SIG_UNBLOCK, &mask, NULL); | ||||
|  | ||||
| 	snprintf (command, PATH_MAX, "%s \"$@\"", argv[0]); | ||||
|  | ||||
| 	child_argv[0] = wl->pw->pw_shell; | ||||
| 	child_argv[1] = "-l"; | ||||
| 	child_argv[2] = "-c"; | ||||
| 	child_argv[3] = command; | ||||
| 	for (i = 0; i < argc; ++i) | ||||
| 		child_argv[4 + i] = argv[i]; | ||||
| 	child_argv[4 + i] = NULL; | ||||
|  | ||||
| 	execv(child_argv[0], child_argv); | ||||
| 	error(1, errno, "exec failed"); | ||||
| } | ||||
|  | ||||
| static void | ||||
| help(const char *name) | ||||
| { | ||||
| 	fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name); | ||||
| 	fprintf(stderr, "  -u, --user      Start session as specified username\n"); | ||||
| 	fprintf(stderr, "  -v, --verbose   Be verbose\n"); | ||||
| 	fprintf(stderr, "  -h, --help      Display this help message\n"); | ||||
| } | ||||
|  | ||||
| int | ||||
| main(int argc, char *argv[]) | ||||
| { | ||||
| 	struct weston_launch wl; | ||||
| 	int i, c; | ||||
| 	struct option opts[] = { | ||||
| 		{ "verbose", no_argument,       NULL, 'v' }, | ||||
| 		{ "help",    no_argument,       NULL, 'h' }, | ||||
| 		{ 0,         0,                 NULL,  0  } | ||||
| 	};	 | ||||
|  | ||||
| 	memset(&wl, 0, sizeof wl); | ||||
|  | ||||
| 	while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) { | ||||
| 		switch (c) { | ||||
| 		case 'v': | ||||
| 			wl.verbose = 1; | ||||
| 			break; | ||||
| 		case 'h': | ||||
| 			help("weston-launch"); | ||||
| 			exit(EXIT_FAILURE); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if ((argc - optind) > (MAX_ARGV_SIZE - 6)) | ||||
| 		error(1, E2BIG, "Too many arguments to pass to weston"); | ||||
|  | ||||
| 	if (strcmp (argv[optind], "mutter-wayland") && | ||||
| 	    strcmp (argv[optind], "gnome-shell-wayland")) | ||||
| 	  error(1, 0, "mutter-launch can only be used to launch mutter or gnome-shell"); | ||||
|  | ||||
| 	wl.pw = getpwuid(getuid()); | ||||
| 	if (wl.pw == NULL) | ||||
| 		error(1, errno, "failed to get username"); | ||||
|  | ||||
| 	if (!weston_launch_allowed(&wl)) | ||||
| 		error(1, 0, "Permission denied. You must run from an active and local (systemd) session."); | ||||
|  | ||||
| 	if (setup_tty(&wl) < 0) | ||||
| 		exit(EXIT_FAILURE); | ||||
|  | ||||
| 	if (setup_launcher_socket(&wl) < 0) | ||||
| 		exit(EXIT_FAILURE); | ||||
|  | ||||
| 	if (setup_signals(&wl) < 0) | ||||
| 		exit(EXIT_FAILURE); | ||||
|  | ||||
| 	wl.child = fork(); | ||||
| 	if (wl.child == -1) { | ||||
| 		error(1, errno, "fork failed"); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	if (wl.child == 0) | ||||
| 		launch_compositor(&wl, argc - optind, argv + optind); | ||||
|  | ||||
| 	close(wl.sock[1]); | ||||
|  | ||||
| 	while (1) { | ||||
| 		struct pollfd fds[2]; | ||||
| 		int n; | ||||
|  | ||||
| 		fds[0].fd = wl.sock[0]; | ||||
| 		fds[0].events = POLLIN; | ||||
| 		fds[1].fd = wl.signalfd; | ||||
| 		fds[1].events = POLLIN; | ||||
|  | ||||
| 		n = poll(fds, 2, -1); | ||||
| 		if (n < 0) | ||||
| 			error(0, errno, "poll failed"); | ||||
| 		if (fds[0].revents & POLLIN) | ||||
| 			handle_socket_msg(&wl); | ||||
| 		if (fds[1].revents) | ||||
| 			handle_signal(&wl); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1,59 +0,0 @@ | ||||
| /* | ||||
|  * Copyright © 2012 Benjamin Franzke | ||||
|  *             2013 Red Hat, Inc. | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and | ||||
|  * its documentation for any purpose is hereby granted without fee, provided | ||||
|  * that the above copyright notice appear in all copies and that both that | ||||
|  * copyright notice and this permission notice appear in supporting | ||||
|  * documentation, and that the name of the copyright holders not be used in | ||||
|  * advertising or publicity pertaining to distribution of the software | ||||
|  * without specific, written prior permission.  The copyright holders make | ||||
|  * no representations about the suitability of this software for any | ||||
|  * purpose.  It is provided "as is" without express or implied warranty. | ||||
|  * | ||||
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||||
|  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||
|  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
|  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER | ||||
|  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF | ||||
|  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #ifndef _WESTON_LAUNCH_H_ | ||||
| #define _WESTON_LAUNCH_H_ | ||||
|  | ||||
| enum weston_launcher_opcode { | ||||
| 	WESTON_LAUNCHER_OPEN, | ||||
| 	WESTON_LAUNCHER_DRM_SET_MASTER, | ||||
| 	WESTON_LAUNCHER_ACTIVATE_VT, | ||||
| 	WESTON_LAUNCHER_CONFIRM_VT_SWITCH,  | ||||
| }; | ||||
|  | ||||
| enum weston_launcher_server_opcode { | ||||
| 	WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH, | ||||
| 	WESTON_LAUNCHER_SERVER_VT_ENTER, | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_message { | ||||
| 	int opcode; | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_open { | ||||
| 	struct weston_launcher_message header; | ||||
| 	int flags; | ||||
| 	char path[0]; | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_set_master { | ||||
| 	struct weston_launcher_message header; | ||||
| 	int set_master; | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_activate_vt { | ||||
| 	struct weston_launcher_message header; | ||||
| 	int vt; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user