Compare commits
27 Commits
wip/carlos
...
wip/lantw/
Author | SHA1 | Date | |
---|---|---|---|
![]() |
af0478ae1e | ||
![]() |
aedf692e0c | ||
![]() |
5dfd86ebe7 | ||
![]() |
9e41f687a0 | ||
![]() |
61356caa06 | ||
![]() |
4300f1f91d | ||
![]() |
d26dc4ae44 | ||
![]() |
bd45a00fa3 | ||
![]() |
793a9d45e1 | ||
![]() |
43295bdcc4 | ||
![]() |
43e12dab7b | ||
![]() |
c4535fdf85 | ||
![]() |
d2c3272eb7 | ||
![]() |
90c4b6492f | ||
![]() |
b7bf42e778 | ||
![]() |
e849667be6 | ||
![]() |
424016d66c | ||
![]() |
a4f55d4986 | ||
![]() |
1b33a5a3a7 | ||
![]() |
36111270aa | ||
![]() |
6e0cfd3e55 | ||
![]() |
5671f0a284 | ||
![]() |
a7e63bea6c | ||
![]() |
94b3c334e5 | ||
![]() |
efb0addb62 | ||
![]() |
988da215c8 | ||
![]() |
551a57ed7f |
33
NEWS
33
NEWS
@@ -1,3 +1,36 @@
|
||||
3.37.1
|
||||
======
|
||||
* Fix screencasting non-maximized windows [Jonas Å.; !1174]
|
||||
* Make window-aliveness checks less aggressive [Jonas Å.; !1182]
|
||||
* Fix stylus coordinates when using screen rotation [Jonas T.; #1118]
|
||||
* Preserve keyboard state on VT switch [Olivier; !1185]
|
||||
* Remove Clutter's drag and drop actions [Jonas D.; !789]
|
||||
* Cancel clicks/gestures actions on disable [Georges; !1188]
|
||||
* Fix various clipboard issues [Carlos; !1186, !1198, !1203, !1204, !1206]
|
||||
* Fix trackball button scrolling [Phillip; #1120]
|
||||
* Fix tiled monitor support [Jonas; !1199]
|
||||
* Support unredirecting fullscreen wayland surfaces [Jonas Å.; !798]
|
||||
* Support area screencasts [Jonas Å.; !1207]
|
||||
* Synchronize shadows to server-side decorations [Olivier; !1214]
|
||||
* Allow inhibiting remote access [Jonas Å.; !1212]
|
||||
* Fix overview key on X11 when using multiple keyboard layouts [Olivier; !1219]
|
||||
* Fixed crashes [Jonas, D., Carlos; !1173, !1183, !1012]
|
||||
* Misc. bug fixes and cleanups [Andre, Georges, Christian, Jonas Å., Andre,
|
||||
Simon, Florian, Carlos, Adam, Marco, Thomas, Elias, Pekka, Jonas D.,
|
||||
Laurent; !1169, !1168, !1166, !1170, !1167, !1172, !1175, !1176, !1184,
|
||||
!1126, !1187, !1191, !1195, !1179, !1200, !1193, !1209, !1213, !1208,
|
||||
#1074, !1223]
|
||||
|
||||
Contributors:
|
||||
Marco Trevisan (Treviño), Elias Aebi, Thomas Hindoe Paaboel Andersen,
|
||||
Laurent Bigonville, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
|
||||
Adam Jackson, Andre Moreira Magalhaes, Simon McVittie, Florian Müllner,
|
||||
Georges Basile Stavracas Neto, Pekka Paalanen, Christian Rauch, Jonas Troeger,
|
||||
Phillip Wood, Jonas Ådahl
|
||||
|
||||
Translators:
|
||||
Dušan Kazik [sk], Christian Kirbach [de]
|
||||
|
||||
3.36.0
|
||||
======
|
||||
* Fix placement of popup windows in multi-monitor setups [Jonas; !1110]
|
||||
|
@@ -36,6 +36,9 @@
|
||||
#include "cogl/clutter-stage-cogl.h"
|
||||
#include "clutter/x11/clutter-backend-x11.h"
|
||||
|
||||
CLUTTER_EXPORT
|
||||
GList * clutter_stage_peek_stage_views (ClutterStage *stage);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_set_custom_backend_func (ClutterBackend *(* func) (void));
|
||||
|
||||
@@ -48,6 +51,23 @@ void clutter_stage_capture_into (ClutterStage *stage,
|
||||
cairo_rectangle_int_t *rect,
|
||||
uint8_t *data);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_stage_paint_to_framebuffer (ClutterStage *stage,
|
||||
CoglFramebuffer *framebuffer,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
float scale,
|
||||
ClutterPaintFlag paint_flags);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
gboolean clutter_stage_paint_to_buffer (ClutterStage *stage,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
float scale,
|
||||
uint8_t *data,
|
||||
int stride,
|
||||
CoglPixelFormat format,
|
||||
ClutterPaintFlag paint_flags,
|
||||
GError **error);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_stage_freeze_updates (ClutterStage *stage);
|
||||
|
||||
|
@@ -21,7 +21,8 @@
|
||||
#include "clutter-paint-context.h"
|
||||
|
||||
ClutterPaintContext * clutter_paint_context_new_for_view (ClutterStageView *view,
|
||||
const cairo_region_t *redraw_clip);
|
||||
const cairo_region_t *redraw_clip,
|
||||
ClutterPaintFlag paint_flags);
|
||||
|
||||
gboolean clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context);
|
||||
|
||||
|
@@ -23,6 +23,8 @@ struct _ClutterPaintContext
|
||||
{
|
||||
grefcount ref_count;
|
||||
|
||||
ClutterPaintFlag paint_flags;
|
||||
|
||||
GList *framebuffers;
|
||||
|
||||
ClutterStageView *view;
|
||||
@@ -36,7 +38,8 @@ G_DEFINE_BOXED_TYPE (ClutterPaintContext, clutter_paint_context,
|
||||
|
||||
ClutterPaintContext *
|
||||
clutter_paint_context_new_for_view (ClutterStageView *view,
|
||||
const cairo_region_t *redraw_clip)
|
||||
const cairo_region_t *redraw_clip,
|
||||
ClutterPaintFlag paint_flags)
|
||||
{
|
||||
ClutterPaintContext *paint_context;
|
||||
CoglFramebuffer *framebuffer;
|
||||
@@ -45,6 +48,7 @@ clutter_paint_context_new_for_view (ClutterStageView *view,
|
||||
g_ref_count_init (&paint_context->ref_count);
|
||||
paint_context->view = view;
|
||||
paint_context->redraw_clip = cairo_region_copy (redraw_clip);
|
||||
paint_context->paint_flags = paint_flags;
|
||||
|
||||
framebuffer = clutter_stage_view_get_framebuffer (view);
|
||||
clutter_paint_context_push_framebuffer (paint_context, framebuffer);
|
||||
@@ -56,12 +60,16 @@ clutter_paint_context_new_for_view (ClutterStageView *view,
|
||||
* clutter_paint_context_new_for_framebuffer: (skip)
|
||||
*/
|
||||
ClutterPaintContext *
|
||||
clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer)
|
||||
clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer,
|
||||
const cairo_region_t *redraw_clip,
|
||||
ClutterPaintFlag paint_flags)
|
||||
{
|
||||
ClutterPaintContext *paint_context;
|
||||
|
||||
paint_context = g_new0 (ClutterPaintContext, 1);
|
||||
g_ref_count_init (&paint_context->ref_count);
|
||||
paint_context->redraw_clip = cairo_region_copy (redraw_clip);
|
||||
paint_context->paint_flags = paint_flags;
|
||||
|
||||
clutter_paint_context_push_framebuffer (paint_context, framebuffer);
|
||||
|
||||
@@ -170,3 +178,12 @@ clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context)
|
||||
|
||||
return !paint_context->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_paint_context_get_paint_flags: (skip)
|
||||
*/
|
||||
ClutterPaintFlag
|
||||
clutter_paint_context_get_paint_flags (ClutterPaintContext *paint_context)
|
||||
{
|
||||
return paint_context->paint_flags;
|
||||
}
|
||||
|
@@ -29,13 +29,21 @@
|
||||
|
||||
typedef struct _ClutterPaintContext ClutterPaintContext;
|
||||
|
||||
typedef enum _ClutterPaintFlag
|
||||
{
|
||||
CLUTTER_PAINT_FLAG_NONE = 0,
|
||||
CLUTTER_PAINT_FLAG_NO_CURSORS = 1 << 0,
|
||||
} ClutterPaintFlag;
|
||||
|
||||
#define CLUTTER_TYPE_PAINT_CONTEXT (clutter_paint_context_get_type ())
|
||||
|
||||
CLUTTER_EXPORT
|
||||
GType clutter_paint_context_get_type (void);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
ClutterPaintContext * clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer);
|
||||
ClutterPaintContext * clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer,
|
||||
const cairo_region_t *redraw_clip,
|
||||
ClutterPaintFlag paint_flags);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
ClutterPaintContext * clutter_paint_context_ref (ClutterPaintContext *paint_context);
|
||||
@@ -62,4 +70,7 @@ void clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context);
|
||||
CLUTTER_EXPORT
|
||||
const cairo_region_t * clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
ClutterPaintFlag clutter_paint_context_get_paint_flags (ClutterPaintContext *paint_context);
|
||||
|
||||
#endif /* CLUTTER_PAINT_CONTEXT_H */
|
||||
|
@@ -139,8 +139,6 @@ void _clutter_stage_presented (ClutterStage *stag
|
||||
CoglFrameEvent frame_event,
|
||||
ClutterFrameInfo *frame_info);
|
||||
|
||||
GList * _clutter_stage_peek_stage_views (ClutterStage *stage);
|
||||
|
||||
void clutter_stage_queue_actor_relayout (ClutterStage *stage,
|
||||
ClutterActor *actor);
|
||||
|
||||
|
@@ -552,7 +552,7 @@ clutter_stage_add_redraw_clip (ClutterStage *stage,
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next)
|
||||
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *view = l->data;
|
||||
|
||||
@@ -924,7 +924,8 @@ clutter_stage_do_paint_view (ClutterStage *stage,
|
||||
ClutterPaintContext *paint_context;
|
||||
cairo_rectangle_int_t clip_rect;
|
||||
|
||||
paint_context = clutter_paint_context_new_for_view (view, redraw_clip);
|
||||
paint_context = clutter_paint_context_new_for_view (view, redraw_clip,
|
||||
CLUTTER_PAINT_FLAG_NONE);
|
||||
|
||||
cairo_region_get_extents (redraw_clip, &clip_rect);
|
||||
setup_view_for_pick_or_paint (stage, view, &clip_rect);
|
||||
@@ -1572,7 +1573,7 @@ is_full_stage_redraw_queued (ClutterStage *stage)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next)
|
||||
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *view = l->data;
|
||||
|
||||
@@ -4163,6 +4164,97 @@ clutter_stage_get_capture_final_size (ClutterStage *stage,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_paint_to_framebuffer (ClutterStage *stage,
|
||||
CoglFramebuffer *framebuffer,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
float scale,
|
||||
ClutterPaintFlag paint_flags)
|
||||
{
|
||||
ClutterStagePrivate *priv = stage->priv;
|
||||
ClutterPaintContext *paint_context;
|
||||
cairo_region_t *redraw_clip;
|
||||
|
||||
redraw_clip = cairo_region_create_rectangle (rect);
|
||||
paint_context =
|
||||
clutter_paint_context_new_for_framebuffer (framebuffer,
|
||||
redraw_clip,
|
||||
paint_flags);
|
||||
cairo_region_destroy (redraw_clip);
|
||||
|
||||
cogl_framebuffer_push_matrix (framebuffer);
|
||||
cogl_framebuffer_set_projection_matrix (framebuffer, &priv->projection);
|
||||
cogl_framebuffer_set_viewport (framebuffer,
|
||||
-(rect->x * scale),
|
||||
-(rect->y * scale),
|
||||
priv->viewport[2] * scale,
|
||||
priv->viewport[3] * scale);
|
||||
clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context);
|
||||
cogl_framebuffer_pop_matrix (framebuffer);
|
||||
|
||||
clutter_paint_context_destroy (paint_context);
|
||||
}
|
||||
|
||||
gboolean
|
||||
clutter_stage_paint_to_buffer (ClutterStage *stage,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
float scale,
|
||||
uint8_t *data,
|
||||
int stride,
|
||||
CoglPixelFormat format,
|
||||
ClutterPaintFlag paint_flags,
|
||||
GError **error)
|
||||
{
|
||||
ClutterBackend *clutter_backend = clutter_get_default_backend ();
|
||||
CoglContext *cogl_context =
|
||||
clutter_backend_get_cogl_context (clutter_backend);
|
||||
int texture_width, texture_height;
|
||||
CoglTexture2D *texture;
|
||||
CoglOffscreen *offscreen;
|
||||
CoglFramebuffer *framebuffer;
|
||||
CoglBitmap *bitmap;
|
||||
|
||||
texture_width = (int) ceilf (rect->width * scale);
|
||||
texture_height = (int) ceilf (rect->height * scale);
|
||||
texture = cogl_texture_2d_new_with_size (cogl_context,
|
||||
texture_width,
|
||||
texture_height);
|
||||
if (!texture)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Failed to create %dx%d texture",
|
||||
texture_width, texture_height);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture));
|
||||
framebuffer = COGL_FRAMEBUFFER (offscreen);
|
||||
|
||||
cogl_object_unref (texture);
|
||||
|
||||
if (!cogl_framebuffer_allocate (framebuffer, error))
|
||||
return FALSE;
|
||||
|
||||
clutter_stage_paint_to_framebuffer (stage, framebuffer,
|
||||
rect, scale, paint_flags);
|
||||
|
||||
bitmap = cogl_bitmap_new_for_data (cogl_context,
|
||||
texture_width, texture_height,
|
||||
format,
|
||||
stride,
|
||||
data);
|
||||
|
||||
cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
|
||||
0, 0,
|
||||
COGL_READ_PIXELS_COLOR_BUFFER,
|
||||
bitmap);
|
||||
|
||||
cogl_object_unref (bitmap);
|
||||
cogl_object_unref (framebuffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
capture_view_into (ClutterStage *stage,
|
||||
gboolean paint,
|
||||
@@ -4310,8 +4402,11 @@ clutter_stage_thaw_updates (ClutterStage *stage)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_stage_peek_stage_views: (skip)
|
||||
*/
|
||||
GList *
|
||||
_clutter_stage_peek_stage_views (ClutterStage *stage)
|
||||
clutter_stage_peek_stage_views (ClutterStage *stage)
|
||||
{
|
||||
ClutterStagePrivate *priv = stage->priv;
|
||||
|
||||
|
@@ -375,15 +375,11 @@ static gboolean
|
||||
swap_framebuffer (ClutterStageWindow *stage_window,
|
||||
ClutterStageView *view,
|
||||
cairo_region_t *swap_region,
|
||||
gboolean swap_with_damage,
|
||||
cairo_region_t *queued_redraw_clip)
|
||||
gboolean swap_with_damage)
|
||||
{
|
||||
CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
|
||||
int *damage, n_rects, i;
|
||||
|
||||
if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION)))
|
||||
paint_damage_region (stage_window, view, swap_region, queued_redraw_clip);
|
||||
|
||||
n_rects = cairo_region_num_rectangles (swap_region);
|
||||
damage = g_newa (int, n_rects * 4);
|
||||
for (i = 0; i < n_rects; i++)
|
||||
@@ -620,7 +616,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
gboolean swap_with_damage;
|
||||
ClutterActor *wrapper;
|
||||
cairo_region_t *redraw_clip;
|
||||
cairo_region_t *queued_redraw_clip;
|
||||
cairo_region_t *queued_redraw_clip = NULL;
|
||||
cairo_region_t *fb_clip_region;
|
||||
cairo_region_t *swap_region;
|
||||
cairo_rectangle_int_t redraw_rect;
|
||||
@@ -644,6 +640,8 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
has_buffer_age = cogl_is_onscreen (fb) && is_buffer_age_enabled ();
|
||||
|
||||
redraw_clip = clutter_stage_view_take_redraw_clip (view);
|
||||
if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))
|
||||
queued_redraw_clip = cairo_region_copy (redraw_clip);
|
||||
|
||||
/* NB: a NULL redraw clip == full stage redraw */
|
||||
if (!redraw_clip)
|
||||
@@ -711,8 +709,6 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
redraw_clip = cairo_region_create_rectangle (&view_rect);
|
||||
}
|
||||
|
||||
queued_redraw_clip = cairo_region_copy (redraw_clip);
|
||||
|
||||
if (may_use_clipped_redraw &&
|
||||
G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
|
||||
use_clipped_redraw = TRUE;
|
||||
@@ -922,7 +918,6 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
}
|
||||
|
||||
g_clear_pointer (&redraw_clip, cairo_region_destroy);
|
||||
g_clear_pointer (&queued_redraw_clip, cairo_region_destroy);
|
||||
g_clear_pointer (&fb_clip_region, cairo_region_destroy);
|
||||
|
||||
if (do_swap_buffer)
|
||||
@@ -943,11 +938,17 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
swap_region = transformed_swap_region;
|
||||
}
|
||||
|
||||
if (queued_redraw_clip)
|
||||
{
|
||||
paint_damage_region (stage_window, view,
|
||||
swap_region, queued_redraw_clip);
|
||||
cairo_region_destroy (queued_redraw_clip);
|
||||
}
|
||||
|
||||
res = swap_framebuffer (stage_window,
|
||||
view,
|
||||
swap_region,
|
||||
swap_with_damage,
|
||||
queued_redraw_clip);
|
||||
swap_with_damage);
|
||||
|
||||
cairo_region_destroy (swap_region);
|
||||
|
||||
@@ -955,6 +956,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&queued_redraw_clip, cairo_region_destroy);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@@ -70,3 +70,12 @@
|
||||
|
||||
/* Whether Xwayland has -initfd option */
|
||||
#mesondefine HAVE_XWAYLAND_INITFD
|
||||
|
||||
/* Whether the mkostemp function exists */
|
||||
#mesondefine HAVE_MKOSTEMP
|
||||
|
||||
/* Whether the posix_fallocate function exists */
|
||||
#mesondefine HAVE_POSIX_FALLOCATE
|
||||
|
||||
/* Whether the memfd_create function exists */
|
||||
#mesondefine HAVE_MEMFD_CREATE
|
||||
|
16
meson.build
16
meson.build
@@ -1,5 +1,5 @@
|
||||
project('mutter', 'c',
|
||||
version: '3.37.0',
|
||||
version: '3.37.1',
|
||||
meson_version: '>= 0.50.0',
|
||||
license: 'GPLv2+'
|
||||
)
|
||||
@@ -408,6 +408,20 @@ if have_wayland
|
||||
endif
|
||||
endif
|
||||
|
||||
optional_functions = [
|
||||
'mkostemp',
|
||||
'posix_fallocate',
|
||||
'memfd_create',
|
||||
]
|
||||
|
||||
foreach function : optional_functions
|
||||
if cc.has_function(function)
|
||||
cdata.set('HAVE_' + function.to_upper(), 1)
|
||||
else
|
||||
message('Optional function ' + function + ' missing')
|
||||
endif
|
||||
endforeach
|
||||
|
||||
xwayland_grab_default_access_rules = get_option('xwayland_grab_default_access_rules')
|
||||
cdata.set_quoted('XWAYLAND_GRAB_DEFAULT_ACCESS_RULES',
|
||||
xwayland_grab_default_access_rules)
|
||||
|
85
po/de.po
85
po/de.po
@@ -13,8 +13,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: mutter master\n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/mutter/issues\n"
|
||||
"POT-Creation-Date: 2019-08-06 00:49+0000\n"
|
||||
"PO-Revision-Date: 2019-09-05 23:42+0200\n"
|
||||
"POT-Creation-Date: 2020-03-30 20:11+0000\n"
|
||||
"PO-Revision-Date: 2020-04-06 23:13+0200\n"
|
||||
"Last-Translator: Christian Kirbach <christian.kirbach@gmail.com>\n"
|
||||
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
|
||||
"Language: de\n"
|
||||
@@ -22,7 +22,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
"X-Generator: Poedit 2.3\n"
|
||||
|
||||
#: data/50-mutter-navigation.xml:6
|
||||
msgid "Navigation"
|
||||
@@ -435,20 +435,33 @@ msgstr "Zusatztaste zum Finden des Zeigers"
|
||||
msgid "This key will initiate the “locate pointer” action."
|
||||
msgstr "Diese Taste wird die Aktion »Zeiger finden« auslösen."
|
||||
|
||||
#: data/org.gnome.mutter.gschema.xml.in:155
|
||||
#: data/org.gnome.mutter.gschema.xml.in:142
|
||||
msgid "Timeout for check-alive ping"
|
||||
msgstr "Reaktionsschwellwert bei Kontaktkontrolle"
|
||||
|
||||
#: data/org.gnome.mutter.gschema.xml.in:143
|
||||
msgid ""
|
||||
"Number of milliseconds a client has to respond to a ping request in order to "
|
||||
"not be detected as frozen. Using 0 will disable the alive check completely."
|
||||
msgstr ""
|
||||
"Zeit in Millisekunden, innerhalb welcher ein Client auf eine "
|
||||
"Kontaktkontrolle antworten muss, um nicht als abgestürzt zu gelten. »0« "
|
||||
"bedeutet, dass die Kontaktkontrolle ausgeschaltet wird."
|
||||
|
||||
#: data/org.gnome.mutter.gschema.xml.in:165
|
||||
msgid "Select window from tab popup"
|
||||
msgstr "Fenster aus Tab-Anzeige auswählen"
|
||||
|
||||
#: data/org.gnome.mutter.gschema.xml.in:160
|
||||
#: data/org.gnome.mutter.gschema.xml.in:170
|
||||
msgid "Cancel tab popup"
|
||||
msgstr "Tab-Anzeige abbrechen"
|
||||
|
||||
#: data/org.gnome.mutter.gschema.xml.in:165
|
||||
#: data/org.gnome.mutter.gschema.xml.in:175
|
||||
msgid "Switch monitor configurations"
|
||||
msgstr "Bildschirmkonfigurationen wechseln"
|
||||
|
||||
# Ich denke nicht, dass »rotate« hier die Bildschirmdrehung meint, sondern eher eine Liste aus Konfigurationen rotiert (d.h. umgewälzt) wird.
|
||||
#: data/org.gnome.mutter.gschema.xml.in:170
|
||||
#: data/org.gnome.mutter.gschema.xml.in:180
|
||||
msgid "Rotates the built-in monitor configuration"
|
||||
msgstr "Wechselt die Konfiguration des eingebauten Bildschirms"
|
||||
|
||||
@@ -569,7 +582,7 @@ msgstr ""
|
||||
#. TRANSLATORS: This string refers to a button that switches between
|
||||
#. * different modes.
|
||||
#.
|
||||
#: src/backends/meta-input-settings.c:2531
|
||||
#: src/backends/meta-input-settings.c:2631
|
||||
#, c-format
|
||||
msgid "Mode Switch (Group %d)"
|
||||
msgstr "Moduswechsel (Gruppe %d)"
|
||||
@@ -577,34 +590,34 @@ msgstr "Moduswechsel (Gruppe %d)"
|
||||
#. TRANSLATORS: This string refers to an action, cycles drawing tablets'
|
||||
#. * mapping through the available outputs.
|
||||
#.
|
||||
#: src/backends/meta-input-settings.c:2554
|
||||
#: src/backends/meta-input-settings.c:2654
|
||||
msgid "Switch monitor"
|
||||
msgstr "Bildschirm wechseln"
|
||||
|
||||
#: src/backends/meta-input-settings.c:2556
|
||||
#: src/backends/meta-input-settings.c:2656
|
||||
msgid "Show on-screen help"
|
||||
msgstr "Bildschirmhilfe anzeigen"
|
||||
|
||||
#: src/backends/meta-monitor.c:223
|
||||
#: src/backends/meta-monitor.c:226
|
||||
msgid "Built-in display"
|
||||
msgstr "Eingebaute Anzeige"
|
||||
|
||||
#: src/backends/meta-monitor.c:252
|
||||
#: src/backends/meta-monitor.c:255
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: src/backends/meta-monitor.c:254
|
||||
#: src/backends/meta-monitor.c:257
|
||||
msgid "Unknown Display"
|
||||
msgstr "Unbekannte Anzeige"
|
||||
|
||||
#: src/backends/meta-monitor.c:262
|
||||
#: src/backends/meta-monitor.c:265
|
||||
#, c-format
|
||||
msgctxt ""
|
||||
"This is a monitor vendor name, followed by a size in inches, like 'Dell 15\"'"
|
||||
msgid "%s %s"
|
||||
msgstr "%s %s"
|
||||
|
||||
#: src/backends/meta-monitor.c:270
|
||||
#: src/backends/meta-monitor.c:273
|
||||
#, c-format
|
||||
msgctxt ""
|
||||
"This is a monitor vendor name followed by product/model name where size in "
|
||||
@@ -614,13 +627,13 @@ msgstr "%s %s"
|
||||
|
||||
# https://de.wikipedia.org/wiki/Composition-Manager
|
||||
#. Translators: this string will appear in Sysprof
|
||||
#: src/backends/meta-profiler.c:82
|
||||
#: src/backends/meta-profiler.c:79
|
||||
msgid "Compositor"
|
||||
msgstr "Compositor"
|
||||
|
||||
#. This probably means that a non-WM compositor like xcompmgr is running;
|
||||
#. * we have no way to get it to exit
|
||||
#: src/compositor/compositor.c:510
|
||||
#: src/compositor/compositor.c:533
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Another compositing manager is already running on screen %i on display “%s”."
|
||||
@@ -632,47 +645,47 @@ msgstr ""
|
||||
msgid "Bell event"
|
||||
msgstr "Klangereignis"
|
||||
|
||||
#: src/core/main.c:185
|
||||
#: src/core/main.c:190
|
||||
msgid "Disable connection to session manager"
|
||||
msgstr "Verbindung zur Sitzungsverwaltung deaktivieren"
|
||||
|
||||
#: src/core/main.c:191
|
||||
#: src/core/main.c:196
|
||||
msgid "Replace the running window manager"
|
||||
msgstr "Den aktuellen Fensterverwalter ersetzen"
|
||||
|
||||
#: src/core/main.c:197
|
||||
#: src/core/main.c:202
|
||||
msgid "Specify session management ID"
|
||||
msgstr "Kennung der Sitzungsverwaltung angeben"
|
||||
|
||||
#: src/core/main.c:202
|
||||
#: src/core/main.c:207
|
||||
msgid "X Display to use"
|
||||
msgstr "Zu verwendende X-Anzeige"
|
||||
|
||||
#: src/core/main.c:208
|
||||
#: src/core/main.c:213
|
||||
msgid "Initialize session from savefile"
|
||||
msgstr "Sitzung anhand gespeicherter Datei starten"
|
||||
|
||||
#: src/core/main.c:214
|
||||
#: src/core/main.c:219
|
||||
msgid "Make X calls synchronous"
|
||||
msgstr "X-Aufrufe abgleichen"
|
||||
|
||||
#: src/core/main.c:221
|
||||
#: src/core/main.c:226
|
||||
msgid "Run as a wayland compositor"
|
||||
msgstr "Als Wayland-Compositor ausführen"
|
||||
|
||||
#: src/core/main.c:227
|
||||
#: src/core/main.c:232
|
||||
msgid "Run as a nested compositor"
|
||||
msgstr "Als eingebetteten Compositor ausführen"
|
||||
|
||||
#: src/core/main.c:233
|
||||
#: src/core/main.c:238
|
||||
msgid "Run wayland compositor without starting Xwayland"
|
||||
msgstr "Wayland-Compositor ausführen, ohne Xwayland zu starten"
|
||||
|
||||
#: src/core/main.c:241
|
||||
#: src/core/main.c:246
|
||||
msgid "Run as a full display server, rather than nested"
|
||||
msgstr "Als vollwertigen Display-Server verwenden (nicht eingebettet)"
|
||||
|
||||
#: src/core/main.c:247
|
||||
#: src/core/main.c:252
|
||||
msgid "Run with X11 backend"
|
||||
msgstr "Mit X11-Backend ausführen"
|
||||
|
||||
@@ -728,21 +741,21 @@ msgstr "Version ausgeben"
|
||||
msgid "Mutter plugin to use"
|
||||
msgstr "Zu benutzendes Mutter-Plugin"
|
||||
|
||||
#: src/core/prefs.c:1849
|
||||
#: src/core/prefs.c:1911
|
||||
#, c-format
|
||||
msgid "Workspace %d"
|
||||
msgstr "Arbeitsfläche %d"
|
||||
|
||||
#: src/core/util.c:121
|
||||
#: src/core/util.c:122
|
||||
msgid "Mutter was compiled without support for verbose mode\n"
|
||||
msgstr "Mutter wurde ohne Unterstützung für den redseligen Modus kompiliert\n"
|
||||
|
||||
#: src/wayland/meta-wayland-tablet-pad.c:567
|
||||
#: src/wayland/meta-wayland-tablet-pad.c:568
|
||||
#, c-format
|
||||
msgid "Mode Switch: Mode %d"
|
||||
msgstr "Moduswechsel: Modus %d"
|
||||
|
||||
#: src/x11/meta-x11-display.c:671
|
||||
#: src/x11/meta-x11-display.c:676
|
||||
#, c-format
|
||||
msgid ""
|
||||
"Display “%s” already has a window manager; try using the --replace option to "
|
||||
@@ -751,21 +764,21 @@ msgstr ""
|
||||
"Bildschirm »%s« hat bereits einen Fensterverwalter. Versuchen Sie die Option "
|
||||
"»--replace«, um den aktuellen Fensterverwalter zu ersetzen."
|
||||
|
||||
#: src/x11/meta-x11-display.c:1032
|
||||
#: src/x11/meta-x11-display.c:1089
|
||||
msgid "Failed to initialize GDK\n"
|
||||
msgstr "GDK konnte nicht initialisiert werden\n"
|
||||
|
||||
#: src/x11/meta-x11-display.c:1056
|
||||
#: src/x11/meta-x11-display.c:1113
|
||||
#, c-format
|
||||
msgid "Failed to open X Window System display “%s”\n"
|
||||
msgstr "X-Window-Systemanzeige »%s« konnte nicht geöffnet werden\n"
|
||||
|
||||
#: src/x11/meta-x11-display.c:1140
|
||||
#: src/x11/meta-x11-display.c:1196
|
||||
#, c-format
|
||||
msgid "Screen %d on display “%s” is invalid\n"
|
||||
msgstr "Bildschirm %d auf Anzeige »%s« ist ungültig\n"
|
||||
|
||||
#: src/x11/meta-x11-selection-input-stream.c:445
|
||||
#: src/x11/meta-x11-selection-input-stream.c:460
|
||||
#, c-format
|
||||
msgid "Format %s not supported"
|
||||
msgstr "Format %s wird nicht unterstützt"
|
||||
|
@@ -49,6 +49,7 @@ typedef struct _MetaTileInfo MetaTileInfo;
|
||||
typedef struct _MetaRenderer MetaRenderer;
|
||||
typedef struct _MetaRendererView MetaRendererView;
|
||||
|
||||
typedef struct _MetaRemoteDesktop MetaRemoteDesktop;
|
||||
typedef struct _MetaScreenCast MetaScreenCast;
|
||||
typedef struct _MetaScreenCastSession MetaScreenCastSession;
|
||||
typedef struct _MetaScreenCastStream MetaScreenCastStream;
|
||||
|
@@ -552,12 +552,12 @@ meta_backend_real_post_init (MetaBackend *backend)
|
||||
}
|
||||
|
||||
#ifdef HAVE_REMOTE_DESKTOP
|
||||
priv->remote_access_controller =
|
||||
g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL);
|
||||
priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL);
|
||||
priv->screen_cast = meta_screen_cast_new (backend,
|
||||
priv->dbus_session_watcher);
|
||||
priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
|
||||
priv->remote_access_controller =
|
||||
meta_remote_access_controller_new (priv->remote_desktop, priv->screen_cast);
|
||||
#endif /* HAVE_REMOTE_DESKTOP */
|
||||
|
||||
if (!meta_monitor_manager_is_headless (priv->monitor_manager))
|
||||
@@ -809,9 +809,6 @@ static MetaMonitorManager *
|
||||
meta_backend_create_monitor_manager (MetaBackend *backend,
|
||||
GError **error)
|
||||
{
|
||||
if (g_getenv ("META_DUMMY_MONITORS"))
|
||||
return g_object_new (META_TYPE_MONITOR_MANAGER_DUMMY, NULL);
|
||||
|
||||
return META_BACKEND_GET_CLASS (backend)->create_monitor_manager (backend,
|
||||
error);
|
||||
}
|
||||
|
@@ -54,8 +54,8 @@ meta_input_device_init (MetaInputDevice *input_device)
|
||||
static void
|
||||
meta_input_device_constructed (GObject *object)
|
||||
{
|
||||
MetaInputDevice *input_device = META_INPUT_DEVICE (object);
|
||||
#ifdef HAVE_LIBWACOM
|
||||
MetaInputDevice *input_device;
|
||||
WacomDeviceDatabase *wacom_db;
|
||||
MetaInputDevicePrivate *priv;
|
||||
const char *node;
|
||||
@@ -64,6 +64,7 @@ meta_input_device_constructed (GObject *object)
|
||||
G_OBJECT_CLASS (meta_input_device_parent_class)->constructed (object);
|
||||
|
||||
#ifdef HAVE_LIBWACOM
|
||||
input_device = META_INPUT_DEVICE (object);
|
||||
priv = meta_input_device_get_instance_private (input_device);
|
||||
wacom_db = meta_backend_get_wacom_database (meta_get_backend ());
|
||||
node = clutter_input_device_get_device_node (CLUTTER_INPUT_DEVICE (input_device));
|
||||
|
@@ -21,8 +21,12 @@
|
||||
#ifndef META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H
|
||||
#define META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H
|
||||
|
||||
#include "backends/meta-backend-types.h"
|
||||
#include "meta/meta-remote-access-controller.h"
|
||||
|
||||
MetaRemoteAccessController * meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop,
|
||||
MetaScreenCast *screen_cast);
|
||||
|
||||
void meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller,
|
||||
MetaRemoteAccessHandle *handle);
|
||||
|
||||
|
@@ -22,6 +22,11 @@
|
||||
|
||||
#include "backends/meta-remote-access-controller-private.h"
|
||||
|
||||
#ifdef HAVE_REMOTE_DESKTOP
|
||||
#include "backends/meta-remote-desktop.h"
|
||||
#include "backends/meta-screen-cast.h"
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
HANDLE_STOPPED,
|
||||
@@ -54,6 +59,9 @@ G_DEFINE_TYPE_WITH_PRIVATE (MetaRemoteAccessHandle,
|
||||
struct _MetaRemoteAccessController
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
MetaRemoteDesktop *remote_desktop;
|
||||
MetaScreenCast *screen_cast;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (MetaRemoteAccessController,
|
||||
@@ -122,6 +130,53 @@ meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *con
|
||||
handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_remote_access_controller_inhibit_remote_access:
|
||||
* @controller: a #MetaRemoteAccessController
|
||||
*
|
||||
* Inhibits remote access sessions from being created and running. Any active
|
||||
* remote access session will be terminated.
|
||||
*/
|
||||
void
|
||||
meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController *controller)
|
||||
{
|
||||
#ifdef HAVE_REMOTE_DESKTOP
|
||||
meta_remote_desktop_inhibit (controller->remote_desktop);
|
||||
meta_screen_cast_inhibit (controller->screen_cast);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_remote_access_controller_uninhibit_remote_access:
|
||||
* @controller: a #MetaRemoteAccessController
|
||||
*
|
||||
* Uninhibits remote access sessions from being created and running. If this was
|
||||
* the last inhibitation that was inhibited, new remote access sessions can now
|
||||
* be created.
|
||||
*/
|
||||
void
|
||||
meta_remote_access_controller_uninhibit_remote_access (MetaRemoteAccessController *controller)
|
||||
{
|
||||
#ifdef HAVE_REMOTE_DESKTOP
|
||||
meta_screen_cast_uninhibit (controller->screen_cast);
|
||||
meta_remote_desktop_uninhibit (controller->remote_desktop);
|
||||
#endif
|
||||
}
|
||||
|
||||
MetaRemoteAccessController *
|
||||
meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop,
|
||||
MetaScreenCast *screen_cast)
|
||||
{
|
||||
MetaRemoteAccessController *remote_access_controller;
|
||||
|
||||
remote_access_controller = g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER,
|
||||
NULL);
|
||||
remote_access_controller->remote_desktop = remote_desktop;
|
||||
remote_access_controller->screen_cast = screen_cast;
|
||||
|
||||
return remote_access_controller;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_remote_access_handle_init (MetaRemoteAccessHandle *handle)
|
||||
{
|
||||
|
@@ -56,6 +56,8 @@ struct _MetaRemoteDesktop
|
||||
|
||||
int dbus_name_id;
|
||||
|
||||
int inhibit_count;
|
||||
|
||||
GHashTable *sessions;
|
||||
|
||||
MetaDbusSessionWatcher *session_watcher;
|
||||
@@ -70,6 +72,34 @@ G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktop,
|
||||
G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_REMOTE_DESKTOP,
|
||||
meta_remote_desktop_init_iface));
|
||||
|
||||
void
|
||||
meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop)
|
||||
{
|
||||
remote_desktop->inhibit_count++;
|
||||
if (remote_desktop->inhibit_count == 1)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
g_hash_table_iter_init (&iter, remote_desktop->sessions);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
MetaRemoteDesktopSession *session = value;
|
||||
|
||||
g_hash_table_iter_steal (&iter);
|
||||
meta_remote_desktop_session_close (session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop)
|
||||
{
|
||||
g_return_if_fail (remote_desktop->inhibit_count > 0);
|
||||
|
||||
remote_desktop->inhibit_count--;
|
||||
}
|
||||
|
||||
GDBusConnection *
|
||||
meta_remote_desktop_get_connection (MetaRemoteDesktop *remote_desktop)
|
||||
{
|
||||
@@ -108,6 +138,15 @@ handle_create_session (MetaDBusRemoteDesktop *skeleton,
|
||||
char *session_path;
|
||||
const char *client_dbus_name;
|
||||
|
||||
if (remote_desktop->inhibit_count > 0)
|
||||
{
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_ACCESS_DENIED,
|
||||
"Session creation inhibited");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
peer_name = g_dbus_method_invocation_get_sender (invocation);
|
||||
session = meta_remote_desktop_session_new (remote_desktop,
|
||||
peer_name,
|
||||
|
@@ -36,6 +36,10 @@ G_DECLARE_FINAL_TYPE (MetaRemoteDesktop, meta_remote_desktop,
|
||||
META, REMOTE_DESKTOP,
|
||||
MetaDBusRemoteDesktopSkeleton)
|
||||
|
||||
void meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop);
|
||||
|
||||
void meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop);
|
||||
|
||||
MetaRemoteDesktopSession * meta_remote_desktop_get_session (MetaRemoteDesktop *remote_desktop,
|
||||
const char *session_id);
|
||||
|
||||
|
570
src/backends/meta-screen-cast-area-stream-src.c
Normal file
570
src/backends/meta-screen-cast-area-stream-src.c
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 "backends/meta-screen-cast-area-stream-src.h"
|
||||
|
||||
#include <spa/buffer/meta.h>
|
||||
|
||||
#include "backends/meta-backend-private.h"
|
||||
#include "backends/meta-cursor-tracker-private.h"
|
||||
#include "backends/meta-screen-cast-area-stream.h"
|
||||
#include "backends/meta-screen-cast-session.h"
|
||||
#include "backends/meta-stage-private.h"
|
||||
#include "clutter/clutter.h"
|
||||
#include "clutter/clutter-mutter.h"
|
||||
#include "core/boxes-private.h"
|
||||
|
||||
struct _MetaScreenCastAreaStreamSrc
|
||||
{
|
||||
MetaScreenCastStreamSrc parent;
|
||||
|
||||
gboolean cursor_bitmap_invalid;
|
||||
gboolean hw_cursor_inhibited;
|
||||
|
||||
GList *watches;
|
||||
|
||||
gulong cursor_moved_handler_id;
|
||||
gulong cursor_changed_handler_id;
|
||||
|
||||
guint maybe_record_idle_id;
|
||||
};
|
||||
|
||||
static void
|
||||
hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (MetaScreenCastAreaStreamSrc,
|
||||
meta_screen_cast_area_stream_src,
|
||||
META_TYPE_SCREEN_CAST_STREAM_SRC,
|
||||
G_IMPLEMENT_INTERFACE (META_TYPE_HW_CURSOR_INHIBITOR,
|
||||
hw_cursor_inhibitor_iface_init))
|
||||
|
||||
static ClutterStage *
|
||||
get_stage (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
MetaScreenCastStreamSrc *src;
|
||||
MetaScreenCastStream *stream;
|
||||
MetaScreenCastAreaStream *area_stream;
|
||||
|
||||
src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
|
||||
return meta_screen_cast_area_stream_get_stage (area_stream);
|
||||
}
|
||||
|
||||
static MetaBackend *
|
||||
get_backend (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
|
||||
MetaScreenCast *screen_cast =
|
||||
meta_screen_cast_session_get_screen_cast (session);
|
||||
|
||||
return meta_screen_cast_get_backend (screen_cast);
|
||||
}
|
||||
|
||||
static MetaCursorRenderer *
|
||||
get_cursor_renderer (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
|
||||
MetaScreenCast *screen_cast =
|
||||
meta_screen_cast_session_get_screen_cast (session);
|
||||
MetaBackend *backend = meta_screen_cast_get_backend (screen_cast);
|
||||
|
||||
return meta_backend_get_cursor_renderer (backend);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_src_get_specs (MetaScreenCastStreamSrc *src,
|
||||
int *width,
|
||||
int *height,
|
||||
float *frame_rate)
|
||||
{
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
MetaRectangle *area;
|
||||
float scale;
|
||||
|
||||
area = meta_screen_cast_area_stream_get_area (area_stream);
|
||||
scale = meta_screen_cast_area_stream_get_scale (area_stream);
|
||||
|
||||
*width = (int) roundf (area->width * scale);
|
||||
*height = (int) roundf (area->height * scale);
|
||||
*frame_rate = 60.0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_cursor_in_stream (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
MetaBackend *backend = get_backend (area_src);
|
||||
MetaCursorRenderer *cursor_renderer =
|
||||
meta_backend_get_cursor_renderer (backend);
|
||||
MetaRectangle *area;
|
||||
graphene_rect_t area_rect;
|
||||
MetaCursorSprite *cursor_sprite;
|
||||
|
||||
area = meta_screen_cast_area_stream_get_area (area_stream);
|
||||
area_rect = meta_rectangle_to_graphene_rect (area);
|
||||
|
||||
cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
|
||||
if (cursor_sprite)
|
||||
{
|
||||
graphene_rect_t cursor_rect;
|
||||
|
||||
cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer,
|
||||
cursor_sprite);
|
||||
return graphene_rect_intersection (&cursor_rect, &area_rect, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_point_t cursor_position;
|
||||
|
||||
cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
|
||||
return graphene_rect_contains_point (&area_rect, &cursor_position);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sync_cursor_state (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
ClutterStage *stage = get_stage (area_src);
|
||||
|
||||
if (!is_cursor_in_stream (area_src))
|
||||
return;
|
||||
|
||||
if (clutter_stage_is_redraw_queued (stage))
|
||||
return;
|
||||
|
||||
meta_screen_cast_stream_src_maybe_record_frame (src);
|
||||
}
|
||||
|
||||
static void
|
||||
cursor_moved (MetaCursorTracker *cursor_tracker,
|
||||
float x,
|
||||
float y,
|
||||
MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
sync_cursor_state (area_src);
|
||||
}
|
||||
|
||||
static void
|
||||
cursor_changed (MetaCursorTracker *cursor_tracker,
|
||||
MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
area_src->cursor_bitmap_invalid = TRUE;
|
||||
sync_cursor_state (area_src);
|
||||
}
|
||||
|
||||
static void
|
||||
inhibit_hw_cursor (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
MetaCursorRenderer *cursor_renderer;
|
||||
MetaHwCursorInhibitor *inhibitor;
|
||||
|
||||
g_return_if_fail (!area_src->hw_cursor_inhibited);
|
||||
|
||||
cursor_renderer = get_cursor_renderer (area_src);
|
||||
inhibitor = META_HW_CURSOR_INHIBITOR (area_src);
|
||||
meta_cursor_renderer_add_hw_cursor_inhibitor (cursor_renderer, inhibitor);
|
||||
|
||||
area_src->hw_cursor_inhibited = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
uninhibit_hw_cursor (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
MetaCursorRenderer *cursor_renderer;
|
||||
MetaHwCursorInhibitor *inhibitor;
|
||||
|
||||
g_return_if_fail (area_src->hw_cursor_inhibited);
|
||||
|
||||
cursor_renderer = get_cursor_renderer (area_src);
|
||||
inhibitor = META_HW_CURSOR_INHIBITOR (area_src);
|
||||
meta_cursor_renderer_remove_hw_cursor_inhibitor (cursor_renderer, inhibitor);
|
||||
|
||||
area_src->hw_cursor_inhibited = FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
maybe_record_frame_on_idle (gpointer user_data)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (user_data);
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
|
||||
area_src->maybe_record_idle_id = 0;
|
||||
|
||||
meta_screen_cast_stream_src_maybe_record_frame (src);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
stage_painted (MetaStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterPaintContext *paint_context,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (user_data);
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
const cairo_region_t *redraw_clip;
|
||||
MetaRectangle *area;
|
||||
|
||||
if (area_src->maybe_record_idle_id)
|
||||
return;
|
||||
|
||||
area = meta_screen_cast_area_stream_get_area (area_stream);
|
||||
redraw_clip = clutter_paint_context_get_redraw_clip (paint_context);
|
||||
|
||||
if (redraw_clip)
|
||||
{
|
||||
switch (cairo_region_contains_rectangle (redraw_clip, area))
|
||||
{
|
||||
case CAIRO_REGION_OVERLAP_IN:
|
||||
case CAIRO_REGION_OVERLAP_PART:
|
||||
break;
|
||||
case CAIRO_REGION_OVERLAP_OUT:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
area_src->maybe_record_idle_id = g_idle_add (maybe_record_frame_on_idle, src);
|
||||
}
|
||||
|
||||
static void
|
||||
add_view_painted_watches (MetaScreenCastAreaStreamSrc *area_src,
|
||||
MetaStageWatchPhase watch_phase)
|
||||
{
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
MetaBackend *backend = get_backend (area_src);
|
||||
MetaRenderer *renderer = meta_backend_get_renderer (backend);
|
||||
ClutterStage *stage;
|
||||
MetaStage *meta_stage;
|
||||
MetaRectangle *area;
|
||||
GList *l;
|
||||
|
||||
stage = get_stage (area_src);
|
||||
meta_stage = META_STAGE (stage);
|
||||
area = meta_screen_cast_area_stream_get_area (area_stream);
|
||||
|
||||
for (l = meta_renderer_get_views (renderer); l; l = l->next)
|
||||
{
|
||||
MetaRendererView *view = l->data;
|
||||
MetaRectangle view_layout;
|
||||
|
||||
clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout);
|
||||
if (meta_rectangle_overlap (area, &view_layout))
|
||||
{
|
||||
MetaStageWatch *watch;
|
||||
|
||||
watch = meta_stage_watch_view (meta_stage,
|
||||
CLUTTER_STAGE_VIEW (view),
|
||||
watch_phase,
|
||||
stage_painted,
|
||||
area_src);
|
||||
|
||||
area_src->watches = g_list_prepend (area_src->watches, watch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_src_enable (MetaScreenCastStreamSrc *src)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (src);
|
||||
MetaBackend *backend = get_backend (area_src);
|
||||
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
|
||||
ClutterStage *stage;
|
||||
MetaScreenCastStream *stream;
|
||||
|
||||
stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
stage = get_stage (area_src);
|
||||
|
||||
switch (meta_screen_cast_stream_get_cursor_mode (stream))
|
||||
{
|
||||
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
|
||||
area_src->cursor_moved_handler_id =
|
||||
g_signal_connect_after (cursor_tracker, "cursor-moved",
|
||||
G_CALLBACK (cursor_moved),
|
||||
area_src);
|
||||
area_src->cursor_changed_handler_id =
|
||||
g_signal_connect_after (cursor_tracker, "cursor-changed",
|
||||
G_CALLBACK (cursor_changed),
|
||||
area_src);
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
|
||||
add_view_painted_watches (area_src,
|
||||
META_STAGE_WATCH_AFTER_ACTOR_PAINT);
|
||||
break;
|
||||
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
|
||||
inhibit_hw_cursor (area_src);
|
||||
add_view_painted_watches (area_src,
|
||||
META_STAGE_WATCH_AFTER_ACTOR_PAINT);
|
||||
break;
|
||||
}
|
||||
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_src_disable (MetaScreenCastStreamSrc *src)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (src);
|
||||
MetaBackend *backend = get_backend (area_src);
|
||||
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
|
||||
ClutterStage *stage;
|
||||
MetaStage *meta_stage;
|
||||
GList *l;
|
||||
|
||||
stage = get_stage (area_src);
|
||||
meta_stage = META_STAGE (stage);
|
||||
|
||||
for (l = area_src->watches; l; l = l->next)
|
||||
{
|
||||
MetaStageWatch *watch = l->data;
|
||||
|
||||
meta_stage_remove_watch (meta_stage, watch);
|
||||
}
|
||||
g_clear_pointer (&area_src->watches, g_list_free);
|
||||
|
||||
if (area_src->hw_cursor_inhibited)
|
||||
uninhibit_hw_cursor (area_src);
|
||||
|
||||
g_clear_signal_handler (&area_src->cursor_moved_handler_id,
|
||||
cursor_tracker);
|
||||
g_clear_signal_handler (&area_src->cursor_changed_handler_id,
|
||||
cursor_tracker);
|
||||
|
||||
g_clear_handle_id (&area_src->maybe_record_idle_id, g_source_remove);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_screen_cast_area_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
||||
uint8_t *data)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
ClutterStage *stage;
|
||||
MetaRectangle *area;
|
||||
float scale;
|
||||
int stride;
|
||||
ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
stage = get_stage (area_src);
|
||||
area = meta_screen_cast_area_stream_get_area (area_stream);
|
||||
scale = meta_screen_cast_area_stream_get_scale (area_stream);
|
||||
stride = meta_screen_cast_stream_src_get_stride (src);
|
||||
|
||||
switch (meta_screen_cast_stream_get_cursor_mode (stream))
|
||||
{
|
||||
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
|
||||
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
|
||||
paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
|
||||
break;
|
||||
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!clutter_stage_paint_to_buffer (stage, area, scale,
|
||||
data,
|
||||
stride,
|
||||
CLUTTER_CAIRO_FORMAT_ARGB32,
|
||||
paint_flags,
|
||||
&error))
|
||||
{
|
||||
g_warning ("Failed to record area: %s", error->message);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_screen_cast_area_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src,
|
||||
CoglFramebuffer *framebuffer)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
MetaBackend *backend = get_backend (area_src);
|
||||
ClutterStage *stage;
|
||||
MetaRectangle *area;
|
||||
float scale;
|
||||
ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
|
||||
|
||||
stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
|
||||
area = meta_screen_cast_area_stream_get_area (area_stream);
|
||||
scale = meta_screen_cast_area_stream_get_scale (area_stream);
|
||||
|
||||
switch (meta_screen_cast_stream_get_cursor_mode (stream))
|
||||
{
|
||||
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
|
||||
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
|
||||
paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
|
||||
break;
|
||||
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
|
||||
break;
|
||||
}
|
||||
clutter_stage_paint_to_framebuffer (stage, framebuffer,
|
||||
area, scale,
|
||||
paint_flags);
|
||||
|
||||
cogl_framebuffer_finish (framebuffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
|
||||
struct spa_meta_cursor *spa_meta_cursor)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (src);
|
||||
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||
MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
MetaBackend *backend = get_backend (area_src);
|
||||
MetaCursorRenderer *cursor_renderer =
|
||||
meta_backend_get_cursor_renderer (backend);
|
||||
MetaCursorSprite *cursor_sprite;
|
||||
MetaRectangle *area;
|
||||
float scale;
|
||||
graphene_point_t cursor_position;
|
||||
int x, y;
|
||||
|
||||
cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
|
||||
|
||||
if (!is_cursor_in_stream (area_src))
|
||||
{
|
||||
meta_screen_cast_stream_src_unset_cursor_metadata (src,
|
||||
spa_meta_cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
area = meta_screen_cast_area_stream_get_area (area_stream);
|
||||
scale = meta_screen_cast_area_stream_get_scale (area_stream);
|
||||
|
||||
cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
|
||||
cursor_position.x -= area->x;
|
||||
cursor_position.y -= area->y;
|
||||
cursor_position.x *= scale;
|
||||
cursor_position.y *= scale;
|
||||
|
||||
x = (int) roundf (cursor_position.x);
|
||||
y = (int) roundf (cursor_position.y);
|
||||
|
||||
if (area_src->cursor_bitmap_invalid)
|
||||
{
|
||||
if (cursor_sprite)
|
||||
{
|
||||
float cursor_scale;
|
||||
float metadata_scale;
|
||||
|
||||
cursor_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite);
|
||||
metadata_scale = scale * cursor_scale;
|
||||
meta_screen_cast_stream_src_set_cursor_sprite_metadata (src,
|
||||
spa_meta_cursor,
|
||||
cursor_sprite,
|
||||
x, y,
|
||||
metadata_scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (src,
|
||||
spa_meta_cursor,
|
||||
x, y);
|
||||
}
|
||||
|
||||
area_src->cursor_bitmap_invalid = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_screen_cast_stream_src_set_cursor_position_metadata (src,
|
||||
spa_meta_cursor,
|
||||
x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_screen_cast_area_stream_src_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor,
|
||||
MetaCursorSprite *cursor_sprite)
|
||||
{
|
||||
MetaScreenCastAreaStreamSrc *area_src =
|
||||
META_SCREEN_CAST_AREA_STREAM_SRC (inhibitor);
|
||||
|
||||
return is_cursor_in_stream (area_src);
|
||||
}
|
||||
|
||||
static void
|
||||
hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface)
|
||||
{
|
||||
iface->is_cursor_sprite_inhibited =
|
||||
meta_screen_cast_area_stream_src_is_cursor_sprite_inhibited;
|
||||
}
|
||||
|
||||
MetaScreenCastAreaStreamSrc *
|
||||
meta_screen_cast_area_stream_src_new (MetaScreenCastAreaStream *area_stream,
|
||||
GError **error)
|
||||
{
|
||||
return g_initable_new (META_TYPE_SCREEN_CAST_AREA_STREAM_SRC, NULL, error,
|
||||
"stream", area_stream,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_src_init (MetaScreenCastAreaStreamSrc *area_src)
|
||||
{
|
||||
area_src->cursor_bitmap_invalid = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_src_class_init (MetaScreenCastAreaStreamSrcClass *klass)
|
||||
{
|
||||
MetaScreenCastStreamSrcClass *src_class =
|
||||
META_SCREEN_CAST_STREAM_SRC_CLASS (klass);
|
||||
|
||||
src_class->get_specs = meta_screen_cast_area_stream_src_get_specs;
|
||||
src_class->enable = meta_screen_cast_area_stream_src_enable;
|
||||
src_class->disable = meta_screen_cast_area_stream_src_disable;
|
||||
src_class->record_frame = meta_screen_cast_area_stream_src_record_frame;
|
||||
src_class->blit_to_framebuffer =
|
||||
meta_screen_cast_area_stream_src_blit_to_framebuffer;
|
||||
src_class->set_cursor_metadata =
|
||||
meta_screen_cast_area_stream_src_set_cursor_metadata;
|
||||
}
|
37
src/backends/meta-screen-cast-area-stream-src.h
Normal file
37
src/backends/meta-screen-cast-area-stream-src.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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_SCREEN_CAST_AREA_STREAM_SRC_H
|
||||
#define META_SCREEN_CAST_AREA_STREAM_SRC_H
|
||||
|
||||
#include "backends/meta-screen-cast-stream-src.h"
|
||||
|
||||
typedef struct _MetaScreenCastAreaStream MetaScreenCastAreaStream;
|
||||
|
||||
#define META_TYPE_SCREEN_CAST_AREA_STREAM_SRC (meta_screen_cast_area_stream_src_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (MetaScreenCastAreaStreamSrc,
|
||||
meta_screen_cast_area_stream_src,
|
||||
META, SCREEN_CAST_AREA_STREAM_SRC,
|
||||
MetaScreenCastStreamSrc)
|
||||
|
||||
MetaScreenCastAreaStreamSrc * meta_screen_cast_area_stream_src_new (MetaScreenCastAreaStream *area_stream,
|
||||
GError **error);
|
||||
|
||||
#endif /* META_SCREEN_CAST_AREA_STREAM_SRC_H */
|
177
src/backends/meta-screen-cast-area-stream.c
Normal file
177
src/backends/meta-screen-cast-area-stream.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 "backends/meta-screen-cast-area-stream.h"
|
||||
|
||||
#include "backends/meta-screen-cast-area-stream-src.h"
|
||||
|
||||
struct _MetaScreenCastAreaStream
|
||||
{
|
||||
MetaScreenCastStream parent;
|
||||
|
||||
ClutterStage *stage;
|
||||
|
||||
MetaRectangle area;
|
||||
float scale;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (MetaScreenCastAreaStream,
|
||||
meta_screen_cast_area_stream,
|
||||
META_TYPE_SCREEN_CAST_STREAM)
|
||||
|
||||
ClutterStage *
|
||||
meta_screen_cast_area_stream_get_stage (MetaScreenCastAreaStream *area_stream)
|
||||
{
|
||||
return area_stream->stage;
|
||||
}
|
||||
|
||||
MetaRectangle *
|
||||
meta_screen_cast_area_stream_get_area (MetaScreenCastAreaStream *area_stream)
|
||||
{
|
||||
return &area_stream->area;
|
||||
}
|
||||
|
||||
float
|
||||
meta_screen_cast_area_stream_get_scale (MetaScreenCastAreaStream *area_stream)
|
||||
{
|
||||
return area_stream->scale;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
calculate_scale (ClutterStage *stage,
|
||||
MetaRectangle *area,
|
||||
float *out_scale)
|
||||
{
|
||||
GList *l;
|
||||
float scale = 0.0;
|
||||
|
||||
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
|
||||
{
|
||||
ClutterStageView *stage_view = l->data;
|
||||
MetaRectangle view_layout;
|
||||
|
||||
clutter_stage_view_get_layout (stage_view, &view_layout);
|
||||
if (meta_rectangle_overlap (area, &view_layout))
|
||||
scale = MAX (clutter_stage_view_get_scale (stage_view), scale);
|
||||
}
|
||||
|
||||
if (scale == 0.0)
|
||||
return FALSE;
|
||||
|
||||
*out_scale = scale;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
MetaScreenCastAreaStream *
|
||||
meta_screen_cast_area_stream_new (MetaScreenCastSession *session,
|
||||
GDBusConnection *connection,
|
||||
MetaRectangle *area,
|
||||
ClutterStage *stage,
|
||||
MetaScreenCastCursorMode cursor_mode,
|
||||
GError **error)
|
||||
{
|
||||
MetaScreenCastAreaStream *area_stream;
|
||||
float scale;
|
||||
|
||||
if (!calculate_scale (stage, area, &scale))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Area is off-screen");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
area_stream = g_initable_new (META_TYPE_SCREEN_CAST_AREA_STREAM,
|
||||
NULL,
|
||||
error,
|
||||
"session", session,
|
||||
"connection", connection,
|
||||
"cursor-mode", cursor_mode,
|
||||
NULL);
|
||||
if (!area_stream)
|
||||
return NULL;
|
||||
|
||||
area_stream->area = *area;
|
||||
area_stream->scale = scale;
|
||||
area_stream->stage = stage;
|
||||
|
||||
return area_stream;
|
||||
}
|
||||
|
||||
static MetaScreenCastStreamSrc *
|
||||
meta_screen_cast_area_stream_create_src (MetaScreenCastStream *stream,
|
||||
GError **error)
|
||||
{
|
||||
MetaScreenCastAreaStream *area_stream =
|
||||
META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
MetaScreenCastAreaStreamSrc *area_stream_src;
|
||||
|
||||
area_stream_src = meta_screen_cast_area_stream_src_new (area_stream,
|
||||
error);
|
||||
if (!area_stream_src)
|
||||
return NULL;
|
||||
|
||||
return META_SCREEN_CAST_STREAM_SRC (area_stream_src);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_set_parameters (MetaScreenCastStream *stream,
|
||||
GVariantBuilder *parameters_builder)
|
||||
{
|
||||
MetaScreenCastAreaStream *area_stream =
|
||||
META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
|
||||
g_variant_builder_add (parameters_builder, "{sv}",
|
||||
"size",
|
||||
g_variant_new ("(ii)",
|
||||
area_stream->area.width,
|
||||
area_stream->area.height));
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_transform_position (MetaScreenCastStream *stream,
|
||||
double stream_x,
|
||||
double stream_y,
|
||||
double *x,
|
||||
double *y)
|
||||
{
|
||||
MetaScreenCastAreaStream *area_stream =
|
||||
META_SCREEN_CAST_AREA_STREAM (stream);
|
||||
|
||||
*x = area_stream->area.x + (int) roundf (stream_x / area_stream->scale);
|
||||
*y = area_stream->area.y + (int) roundf (stream_y / area_stream->scale);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_init (MetaScreenCastAreaStream *area_stream)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_area_stream_class_init (MetaScreenCastAreaStreamClass *klass)
|
||||
{
|
||||
MetaScreenCastStreamClass *stream_class =
|
||||
META_SCREEN_CAST_STREAM_CLASS (klass);
|
||||
|
||||
stream_class->create_src = meta_screen_cast_area_stream_create_src;
|
||||
stream_class->set_parameters = meta_screen_cast_area_stream_set_parameters;
|
||||
stream_class->transform_position = meta_screen_cast_area_stream_transform_position;
|
||||
}
|
48
src/backends/meta-screen-cast-area-stream.h
Normal file
48
src/backends/meta-screen-cast-area-stream.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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_SCREEN_CAST_AREA_STREAM_H
|
||||
#define META_SCREEN_CAST_AREA_STREAM_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "backends/meta-screen-cast-stream.h"
|
||||
#include "backends/meta-screen-cast.h"
|
||||
|
||||
#define META_TYPE_SCREEN_CAST_AREA_STREAM (meta_screen_cast_area_stream_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (MetaScreenCastAreaStream,
|
||||
meta_screen_cast_area_stream,
|
||||
META, SCREEN_CAST_AREA_STREAM,
|
||||
MetaScreenCastStream)
|
||||
|
||||
MetaScreenCastAreaStream * meta_screen_cast_area_stream_new (MetaScreenCastSession *session,
|
||||
GDBusConnection *connection,
|
||||
MetaRectangle *area,
|
||||
ClutterStage *stage,
|
||||
MetaScreenCastCursorMode cursor_mode,
|
||||
GError **error);
|
||||
|
||||
ClutterStage * meta_screen_cast_area_stream_get_stage (MetaScreenCastAreaStream *area_stream);
|
||||
|
||||
MetaRectangle * meta_screen_cast_area_stream_get_area (MetaScreenCastAreaStream *area_stream);
|
||||
|
||||
float meta_screen_cast_area_stream_get_scale (MetaScreenCastAreaStream *area_stream);
|
||||
|
||||
#endif /* META_SCREEN_CAST_AREA_STREAM_H */
|
@@ -115,9 +115,10 @@ meta_screen_cast_monitor_stream_src_get_specs (MetaScreenCastStreamSrc *src,
|
||||
}
|
||||
|
||||
static void
|
||||
stage_painted (MetaStage *stage,
|
||||
ClutterStageView *view,
|
||||
gpointer user_data)
|
||||
stage_painted (MetaStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterPaintContext *paint_context,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (user_data);
|
||||
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "backends/meta-backend-private.h"
|
||||
#include "backends/meta-dbus-session-watcher.h"
|
||||
#include "backends/meta-remote-access-controller-private.h"
|
||||
#include "backends/meta-screen-cast-area-stream.h"
|
||||
#include "backends/meta-screen-cast-monitor-stream.h"
|
||||
#include "backends/meta-screen-cast-stream.h"
|
||||
#include "backends/meta-screen-cast-window-stream.h"
|
||||
@@ -485,6 +486,90 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_record_area (MetaDBusScreenCastSession *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
GVariant *properties_variant)
|
||||
{
|
||||
MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (skeleton);
|
||||
GDBusInterfaceSkeleton *interface_skeleton;
|
||||
GDBusConnection *connection;
|
||||
MetaBackend *backend;
|
||||
ClutterStage *stage;
|
||||
MetaScreenCastCursorMode cursor_mode;
|
||||
g_autoptr (GError) error = NULL;
|
||||
MetaRectangle rect;
|
||||
MetaScreenCastAreaStream *area_stream;
|
||||
MetaScreenCastStream *stream;
|
||||
char *stream_path;
|
||||
|
||||
if (!check_permission (session, invocation))
|
||||
{
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_ACCESS_DENIED,
|
||||
"Permission denied");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!g_variant_lookup (properties_variant, "cursor-mode", "u", &cursor_mode))
|
||||
{
|
||||
cursor_mode = META_SCREEN_CAST_CURSOR_MODE_HIDDEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!is_valid_cursor_mode (cursor_mode))
|
||||
{
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_FAILED,
|
||||
"Unknown cursor mode");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
|
||||
connection = g_dbus_interface_skeleton_get_connection (interface_skeleton);
|
||||
backend = meta_screen_cast_get_backend (session->screen_cast);
|
||||
stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
|
||||
|
||||
rect = (MetaRectangle) {
|
||||
.x = x,
|
||||
.y = y,
|
||||
.width = width,
|
||||
.height = height
|
||||
};
|
||||
area_stream = meta_screen_cast_area_stream_new (session,
|
||||
connection,
|
||||
&rect,
|
||||
stage,
|
||||
cursor_mode,
|
||||
&error);
|
||||
if (!area_stream)
|
||||
{
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_FAILED,
|
||||
"Failed to record area: %s",
|
||||
error->message);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
stream = META_SCREEN_CAST_STREAM (area_stream);
|
||||
stream_path = meta_screen_cast_stream_get_object_path (stream);
|
||||
|
||||
session->streams = g_list_append (session->streams, stream);
|
||||
|
||||
g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed), session);
|
||||
|
||||
meta_dbus_screen_cast_session_complete_record_area (skeleton,
|
||||
invocation,
|
||||
stream_path);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface)
|
||||
{
|
||||
@@ -492,6 +577,7 @@ meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface)
|
||||
iface->handle_stop = handle_stop;
|
||||
iface->handle_record_monitor = handle_record_monitor;
|
||||
iface->handle_record_window = handle_record_window;
|
||||
iface->handle_record_area = handle_record_area;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -956,6 +956,15 @@ meta_screen_cast_stream_src_init_initable_iface (GInitableIface *iface)
|
||||
iface->init = meta_screen_cast_stream_src_initable_init;
|
||||
}
|
||||
|
||||
int
|
||||
meta_screen_cast_stream_src_get_stride (MetaScreenCastStreamSrc *src)
|
||||
{
|
||||
MetaScreenCastStreamSrcPrivate *priv =
|
||||
meta_screen_cast_stream_src_get_instance_private (src);
|
||||
|
||||
return priv->video_stride;
|
||||
}
|
||||
|
||||
MetaScreenCastStream *
|
||||
meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src)
|
||||
{
|
||||
|
@@ -65,6 +65,8 @@ struct _MetaScreenCastStreamSrcClass
|
||||
|
||||
void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
|
||||
|
||||
int meta_screen_cast_stream_src_get_stride (MetaScreenCastStreamSrc *src);
|
||||
|
||||
MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src);
|
||||
|
||||
gboolean meta_screen_cast_stream_src_draw_cursor_into (MetaScreenCastStreamSrc *src,
|
||||
|
@@ -40,6 +40,8 @@ struct _MetaScreenCast
|
||||
|
||||
int dbus_name_id;
|
||||
|
||||
int inhibit_count;
|
||||
|
||||
GList *sessions;
|
||||
|
||||
MetaDbusSessionWatcher *session_watcher;
|
||||
@@ -54,6 +56,29 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCast, meta_screen_cast,
|
||||
G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SCREEN_CAST,
|
||||
meta_screen_cast_init_iface))
|
||||
|
||||
void
|
||||
meta_screen_cast_inhibit (MetaScreenCast *screen_cast)
|
||||
{
|
||||
screen_cast->inhibit_count++;
|
||||
if (screen_cast->inhibit_count == 1)
|
||||
{
|
||||
while (screen_cast->sessions)
|
||||
{
|
||||
MetaScreenCastSession *session = screen_cast->sessions->data;
|
||||
|
||||
meta_screen_cast_session_close (session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_screen_cast_uninhibit (MetaScreenCast *screen_cast)
|
||||
{
|
||||
g_return_if_fail (screen_cast->inhibit_count > 0);
|
||||
|
||||
screen_cast->inhibit_count--;
|
||||
}
|
||||
|
||||
GDBusConnection *
|
||||
meta_screen_cast_get_connection (MetaScreenCast *screen_cast)
|
||||
{
|
||||
@@ -119,6 +144,15 @@ handle_create_session (MetaDBusScreenCast *skeleton,
|
||||
gboolean disable_animations;
|
||||
MetaScreenCastSessionType session_type;
|
||||
|
||||
if (screen_cast->inhibit_count > 0)
|
||||
{
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_ACCESS_DENIED,
|
||||
"Session creation inhibited");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_variant_lookup (properties, "remote-desktop-session-id", "s",
|
||||
&remote_desktop_session_id);
|
||||
|
||||
|
@@ -42,6 +42,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
|
||||
META, SCREEN_CAST,
|
||||
MetaDBusScreenCastSkeleton)
|
||||
|
||||
void meta_screen_cast_inhibit (MetaScreenCast *screen_cast);
|
||||
|
||||
void meta_screen_cast_uninhibit (MetaScreenCast *screen_cast);
|
||||
|
||||
GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast);
|
||||
|
||||
MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast);
|
||||
|
@@ -38,9 +38,10 @@ typedef enum
|
||||
META_STAGE_WATCH_AFTER_PAINT,
|
||||
} MetaStageWatchPhase;
|
||||
|
||||
typedef void (* MetaStageWatchFunc) (MetaStage *stage,
|
||||
ClutterStageView *view,
|
||||
gpointer user_data);
|
||||
typedef void (* MetaStageWatchFunc) (MetaStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterPaintContext *paint_context,
|
||||
gpointer user_data);
|
||||
|
||||
ClutterActor *meta_stage_new (MetaBackend *backend);
|
||||
|
||||
|
@@ -65,7 +65,6 @@ struct _MetaStage
|
||||
ClutterStage parent;
|
||||
|
||||
GPtrArray *watchers[N_WATCH_MODES];
|
||||
ClutterStageView *current_view;
|
||||
|
||||
GList *overlays;
|
||||
gboolean is_active;
|
||||
@@ -169,6 +168,7 @@ meta_stage_finalize (GObject *object)
|
||||
static void
|
||||
notify_watchers_for_mode (MetaStage *stage,
|
||||
ClutterStageView *view,
|
||||
ClutterPaintContext *paint_context,
|
||||
MetaStageWatchPhase watch_phase)
|
||||
{
|
||||
GPtrArray *watchers;
|
||||
@@ -183,7 +183,7 @@ notify_watchers_for_mode (MetaStage *stage,
|
||||
if (watch->view && view != watch->view)
|
||||
continue;
|
||||
|
||||
watch->callback (stage, view, watch->user_data);
|
||||
watch->callback (stage, view, paint_context, watch->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,20 +192,32 @@ meta_stage_paint (ClutterActor *actor,
|
||||
ClutterPaintContext *paint_context)
|
||||
{
|
||||
MetaStage *stage = META_STAGE (actor);
|
||||
ClutterStageView *view;
|
||||
GList *l;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor, paint_context);
|
||||
|
||||
notify_watchers_for_mode (stage, stage->current_view,
|
||||
META_STAGE_WATCH_AFTER_ACTOR_PAINT);
|
||||
view = clutter_paint_context_get_stage_view (paint_context);
|
||||
if (view)
|
||||
{
|
||||
notify_watchers_for_mode (stage, view, paint_context,
|
||||
META_STAGE_WATCH_AFTER_ACTOR_PAINT);
|
||||
}
|
||||
|
||||
g_signal_emit (stage, signals[ACTORS_PAINTED], 0);
|
||||
|
||||
for (l = stage->overlays; l; l = l->next)
|
||||
meta_overlay_paint (l->data, paint_context);
|
||||
if (!(clutter_paint_context_get_paint_flags (paint_context) &
|
||||
CLUTTER_PAINT_FLAG_NO_CURSORS))
|
||||
{
|
||||
for (l = stage->overlays; l; l = l->next)
|
||||
meta_overlay_paint (l->data, paint_context);
|
||||
}
|
||||
|
||||
notify_watchers_for_mode (stage, stage->current_view,
|
||||
META_STAGE_WATCH_AFTER_OVERLAY_PAINT);
|
||||
if (view)
|
||||
{
|
||||
notify_watchers_for_mode (stage, view, paint_context,
|
||||
META_STAGE_WATCH_AFTER_OVERLAY_PAINT);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -215,13 +227,14 @@ meta_stage_paint_view (ClutterStage *stage,
|
||||
{
|
||||
MetaStage *meta_stage = META_STAGE (stage);
|
||||
|
||||
notify_watchers_for_mode (meta_stage, view, META_STAGE_WATCH_BEFORE_PAINT);
|
||||
notify_watchers_for_mode (meta_stage, view, NULL,
|
||||
META_STAGE_WATCH_BEFORE_PAINT);
|
||||
|
||||
meta_stage->current_view = view;
|
||||
CLUTTER_STAGE_CLASS (meta_stage_parent_class)->paint_view (stage, view,
|
||||
redraw_clip);
|
||||
|
||||
notify_watchers_for_mode (meta_stage, view, META_STAGE_WATCH_AFTER_PAINT);
|
||||
notify_watchers_for_mode (meta_stage, view, NULL,
|
||||
META_STAGE_WATCH_AFTER_PAINT);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2019 Red Hat
|
||||
* Copyright (C) 2019 DisplayLink (UK) Ltd.
|
||||
* Copyright (C) 2019-2020 DisplayLink (UK) Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -660,6 +660,9 @@ process_page_flip (MetaKmsImpl *impl,
|
||||
meta_kms_page_flip_data_ref (page_flip_data));
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
meta_kms_page_flip_data_unref (page_flip_data);
|
||||
|
||||
if (ret == -EBUSY)
|
||||
{
|
||||
CachedModeSet *cached_mode_set;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat
|
||||
* Copyright 2020 DisplayLink (UK) Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@@ -383,6 +384,7 @@ meta_kms_add_source_in_impl (MetaKms *kms,
|
||||
simple_impl_source->kms = kms;
|
||||
|
||||
g_source_set_callback (source, func, user_data, user_data_destroy);
|
||||
g_source_set_ready_time (source, 0);
|
||||
g_source_attach (source, g_main_context_get_thread_default ());
|
||||
|
||||
return source;
|
||||
|
@@ -64,7 +64,6 @@
|
||||
#include "cogl/cogl.h"
|
||||
#include "compositor/meta-later-private.h"
|
||||
#include "compositor/meta-window-actor-x11.h"
|
||||
#include "compositor/meta-window-actor-wayland.h"
|
||||
#include "compositor/meta-window-actor-private.h"
|
||||
#include "compositor/meta-window-group-private.h"
|
||||
#include "core/display-private.h"
|
||||
@@ -83,6 +82,7 @@
|
||||
#include "x11/meta-x11-display-private.h"
|
||||
|
||||
#ifdef HAVE_WAYLAND
|
||||
#include "compositor/meta-window-actor-wayland.h"
|
||||
#include "wayland/meta-wayland-private.h"
|
||||
#endif
|
||||
|
||||
|
@@ -1270,7 +1270,9 @@ get_image_via_offscreen (MetaShapedTexture *stex,
|
||||
root_node = clutter_root_node_new (fb, &clear_color, COGL_BUFFER_BIT_COLOR);
|
||||
clutter_paint_node_set_static_name (root_node, "MetaShapedTexture.offscreen");
|
||||
|
||||
paint_context = clutter_paint_context_new_for_framebuffer (fb);
|
||||
paint_context =
|
||||
clutter_paint_context_new_for_framebuffer (fb, NULL,
|
||||
CLUTTER_PAINT_FLAG_NONE);
|
||||
|
||||
do_paint_content (stex, root_node, paint_context,
|
||||
stex->texture,
|
||||
|
@@ -86,6 +86,8 @@ struct _MetaWindowActorX11
|
||||
cairo_region_t *shape_region;
|
||||
/* The region we should clip to when painting the shadow */
|
||||
cairo_region_t *shadow_clip;
|
||||
/* The frame region */
|
||||
cairo_region_t *frame_bounds;
|
||||
|
||||
/* Extracted size-invariant shape used for shadows */
|
||||
MetaWindowShape *shadow_shape;
|
||||
@@ -709,11 +711,8 @@ set_clip_region_beneath (MetaWindowActorX11 *actor_x11,
|
||||
|
||||
if (clip_shadow_under_window (actor_x11))
|
||||
{
|
||||
cairo_region_t *frame_bounds;
|
||||
|
||||
frame_bounds = meta_window_get_frame_bounds (window);
|
||||
if (frame_bounds)
|
||||
cairo_region_subtract (actor_x11->shadow_clip, frame_bounds);
|
||||
if (actor_x11->frame_bounds)
|
||||
cairo_region_subtract (actor_x11->shadow_clip, actor_x11->frame_bounds);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1133,6 +1132,17 @@ update_opaque_region (MetaWindowActorX11 *actor_x11)
|
||||
cairo_region_destroy (opaque_region);
|
||||
}
|
||||
|
||||
static void
|
||||
update_frame_bounds (MetaWindowActorX11 *actor_x11)
|
||||
{
|
||||
MetaWindow *window =
|
||||
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
||||
|
||||
g_clear_pointer (&actor_x11->frame_bounds, cairo_region_destroy);
|
||||
actor_x11->frame_bounds =
|
||||
cairo_region_copy (meta_window_get_frame_bounds (window));
|
||||
}
|
||||
|
||||
static void
|
||||
update_regions (MetaWindowActorX11 *actor_x11)
|
||||
{
|
||||
@@ -1204,6 +1214,7 @@ handle_updates (MetaWindowActorX11 *actor_x11)
|
||||
if (!meta_surface_actor_is_visible (surface))
|
||||
return;
|
||||
|
||||
update_frame_bounds (actor_x11);
|
||||
check_needs_reshape (actor_x11);
|
||||
check_needs_shadow (actor_x11);
|
||||
}
|
||||
@@ -1257,15 +1268,13 @@ meta_window_actor_x11_paint (ClutterActor *actor,
|
||||
*/
|
||||
if (!clip && clip_shadow_under_window (actor_x11))
|
||||
{
|
||||
cairo_region_t *frame_bounds;
|
||||
cairo_rectangle_int_t bounds;
|
||||
|
||||
get_shadow_bounds (actor_x11, appears_focused, &bounds);
|
||||
clip = cairo_region_create_rectangle (&bounds);
|
||||
|
||||
frame_bounds = meta_window_get_frame_bounds (window);
|
||||
if (frame_bounds)
|
||||
cairo_region_subtract (clip, frame_bounds);
|
||||
if (actor_x11->frame_bounds)
|
||||
cairo_region_subtract (clip, actor_x11->frame_bounds);
|
||||
}
|
||||
|
||||
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
|
||||
@@ -1552,6 +1561,7 @@ meta_window_actor_x11_dispose (GObject *object)
|
||||
|
||||
g_clear_pointer (&actor_x11->shape_region, cairo_region_destroy);
|
||||
g_clear_pointer (&actor_x11->shadow_clip, cairo_region_destroy);
|
||||
g_clear_pointer (&actor_x11->frame_bounds, cairo_region_destroy);
|
||||
|
||||
g_clear_pointer (&actor_x11->shadow_class, g_free);
|
||||
g_clear_pointer (&actor_x11->focused_shadow, meta_shadow_unref);
|
||||
|
@@ -1321,7 +1321,9 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window,
|
||||
cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1);
|
||||
cogl_framebuffer_translate (framebuffer, -x, -y, 0);
|
||||
|
||||
paint_context = clutter_paint_context_new_for_framebuffer (framebuffer);
|
||||
paint_context =
|
||||
clutter_paint_context_new_for_framebuffer (framebuffer, NULL,
|
||||
CLUTTER_PAINT_FLAG_NONE);
|
||||
clutter_actor_paint (actor, paint_context);
|
||||
clutter_paint_context_destroy (paint_context);
|
||||
|
||||
@@ -1479,7 +1481,9 @@ meta_window_actor_get_image (MetaWindowActor *self,
|
||||
cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1);
|
||||
cogl_framebuffer_translate (framebuffer, -x, -y, 0);
|
||||
|
||||
paint_context = clutter_paint_context_new_for_framebuffer (framebuffer);
|
||||
paint_context =
|
||||
clutter_paint_context_new_for_framebuffer (framebuffer, NULL,
|
||||
CLUTTER_PAINT_FLAG_NONE);
|
||||
clutter_actor_paint (actor, paint_context);
|
||||
clutter_paint_context_destroy (paint_context);
|
||||
|
||||
|
@@ -2129,7 +2129,7 @@ process_special_modifier_key (MetaDisplay *display,
|
||||
return TRUE;
|
||||
}
|
||||
else if (event->type == CLUTTER_KEY_PRESS &&
|
||||
(event->modifier_state & ~(IGNORED_MODIFIERS)) == 0 &&
|
||||
((event->modifier_state & ~(IGNORED_MODIFIERS)) & CLUTTER_MODIFIER_MASK) == 0 &&
|
||||
resolved_key_combo_has_keycode (resolved_key_combo,
|
||||
event->hardware_keycode))
|
||||
{
|
||||
|
368
src/core/meta-anonymous-file.c
Normal file
368
src/core/meta-anonymous-file.c
Normal file
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Sebastian Wick
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author: Sebastian Wick <sebastian@sebastianwick.net>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "core/meta-anonymous-file.h"
|
||||
|
||||
struct _MetaAnonymousFile
|
||||
{
|
||||
int fd;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
|
||||
|
||||
static int
|
||||
create_tmpfile_cloexec (char *tmpname)
|
||||
{
|
||||
int fd;
|
||||
|
||||
#if defined(HAVE_MKOSTEMP)
|
||||
fd = mkostemp (tmpname, O_CLOEXEC);
|
||||
if (fd >= 0)
|
||||
unlink (tmpname);
|
||||
#else
|
||||
fd = mkstemp (tmpname);
|
||||
if (fd >= 0)
|
||||
{
|
||||
long flags;
|
||||
|
||||
unlink (tmpname);
|
||||
|
||||
flags = fcntl (fd, F_GETFD);
|
||||
if (flags == -1 ||
|
||||
fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
{
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new, unique, anonymous file of the given size, and
|
||||
* return the file descriptor for it. The file descriptor is set
|
||||
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
||||
* the given size at offset zero.
|
||||
*
|
||||
* The file should not have a permanent backing store like a disk,
|
||||
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
||||
*
|
||||
* The file name is deleted from the file system.
|
||||
*
|
||||
* The file is suitable for buffer sharing between processes by
|
||||
* transmitting the file descriptor over Unix sockets using the
|
||||
* SCM_RIGHTS methods.
|
||||
*
|
||||
* If the C library implements posix_fallocate(), it is used to
|
||||
* guarantee that disk space is available for the file at the
|
||||
* given size. If disk space is insufficient, errno is set to ENOSPC.
|
||||
* If posix_fallocate() is not supported, program may receive
|
||||
* SIGBUS on accessing mmap()'ed file contents instead.
|
||||
*
|
||||
* If the C library implements memfd_create(), it is used to create the
|
||||
* file purely in memory, without any backing file name on the file
|
||||
* system, and then sealing off the possibility of shrinking it. This
|
||||
* can then be checked before accessing mmap()'ed file contents, to make
|
||||
* sure SIGBUS can't happen. It also avoids requiring XDG_RUNTIME_DIR.
|
||||
*/
|
||||
static int
|
||||
create_anonymous_file (off_t size)
|
||||
{
|
||||
int fd, ret;
|
||||
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
fd = memfd_create ("mutter-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (fd >= 0)
|
||||
{
|
||||
/* We can add this seal before calling posix_fallocate(), as
|
||||
* the file is currently zero-sized anyway.
|
||||
*
|
||||
* There is also no need to check for the return value, we
|
||||
* couldn't do anything with it anyway.
|
||||
*/
|
||||
fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
static const char template[] = "/mutter-shared-XXXXXX";
|
||||
const char *path;
|
||||
char *name;
|
||||
|
||||
path = getenv ("XDG_RUNTIME_DIR");
|
||||
if (!path)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
name = g_malloc (strlen (path) + sizeof (template));
|
||||
if (!name)
|
||||
return -1;
|
||||
|
||||
strcpy (name, path);
|
||||
strcat (name, template);
|
||||
|
||||
fd = create_tmpfile_cloexec (name);
|
||||
|
||||
g_free (name);
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(HAVE_POSIX_FALLOCATE)
|
||||
do
|
||||
{
|
||||
ret = posix_fallocate (fd, 0, size);
|
||||
}
|
||||
while (ret == EINTR);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
close (fd);
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
do
|
||||
{
|
||||
ret = ftruncate (fd, size);
|
||||
}
|
||||
while (ret < 0 && errno == EINTR);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_anonymous_file_new: (skip)
|
||||
* @size: The size of @data
|
||||
* @data: The data of the file with the size @size
|
||||
*
|
||||
* Create a new anonymous read-only file of the given size and the given data
|
||||
* The intended use-case is for sending mid-sized data from the compositor
|
||||
* to clients.
|
||||
*
|
||||
* When done, free the data using meta_anonymous_file_free().
|
||||
*
|
||||
* If this function fails errno is set.
|
||||
*
|
||||
* Returns: The newly created #MetaAnonymousFile, or NULL on failure. Use
|
||||
* meta_anonymous_file_free() to free the resources when done.
|
||||
*/
|
||||
MetaAnonymousFile *
|
||||
meta_anonymous_file_new (size_t size,
|
||||
const uint8_t *data)
|
||||
{
|
||||
MetaAnonymousFile *file;
|
||||
void *map;
|
||||
|
||||
file = g_malloc0 (sizeof *file);
|
||||
if (!file)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file->size = size;
|
||||
file->fd = create_anonymous_file (size);
|
||||
if (file->fd == -1)
|
||||
goto err_free;
|
||||
|
||||
map = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0);
|
||||
if (map == MAP_FAILED)
|
||||
goto err_close;
|
||||
|
||||
memcpy (map, data, size);
|
||||
|
||||
munmap (map, size);
|
||||
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
/* try to put seals on the file to make it read-only so that we can
|
||||
* return the fd later directly when MAPMODE_SHARED is not set.
|
||||
* meta_anonymous_file_open_fd can handle the fd even if it is not
|
||||
* sealed read-only and will instead create a new anonymous file on
|
||||
* each invocation.
|
||||
*/
|
||||
fcntl (file->fd, F_ADD_SEALS, READONLY_SEALS);
|
||||
#endif
|
||||
|
||||
return file;
|
||||
|
||||
err_close:
|
||||
close (file->fd);
|
||||
err_free:
|
||||
g_free (file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* meta_anonymous_file_free: (skip)
|
||||
* @file: the #MetaAnonymousFile
|
||||
*
|
||||
* Free the resources used by an anonymous read-only file.
|
||||
*/
|
||||
void
|
||||
meta_anonymous_file_free (MetaAnonymousFile *file)
|
||||
{
|
||||
close (file->fd);
|
||||
g_free (file);
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_anonymous_file_size: (skip)
|
||||
* @file: the #MetaAnonymousFile
|
||||
*
|
||||
* Get the size of an anonymous read-only file.
|
||||
*
|
||||
* Returns: The size of the anonymous read-only file.
|
||||
*/
|
||||
size_t
|
||||
meta_anonymous_file_size (MetaAnonymousFile *file)
|
||||
{
|
||||
return file->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_anonymous_file_open_fd: (skip)
|
||||
* @file: the #MetaAnonymousFile to get a file descriptor for
|
||||
* @mapmode: describes the ways in which the returned file descriptor can
|
||||
* be used with mmap
|
||||
*
|
||||
* Returns a file descriptor for the given file, ready to be sent to a client.
|
||||
* The returned file descriptor must not be shared between multiple clients.
|
||||
* If @mapmode is %META_ANONYMOUS_FILE_MAPMODE_PRIVATE the file descriptor is
|
||||
* only guaranteed to be mmapable with MAP_PRIVATE. If @mapmode is
|
||||
* %META_ANONYMOUS_FILE_MAPMODE_SHARED the file descriptor can be mmaped with
|
||||
* either MAP_PRIVATE or MAP_SHARED.
|
||||
*
|
||||
* In case %META_ANONYMOUS_FILE_MAPMODE_PRIVATE is used, it is important to
|
||||
* only read the returned fd using mmap() since using read() will move the
|
||||
* read cursor of the fd and thus may cause read() calls on other returned
|
||||
* fds to fail.
|
||||
*
|
||||
* When done using the fd, it is required to call meta_anonymous_file_close_fd()
|
||||
* instead of close().
|
||||
*
|
||||
* If this function fails errno is set.
|
||||
*
|
||||
* Returns: A file descriptor for the given file that can be sent to a client
|
||||
* or -1 on failure. Use meta_anonymous_file_close_fd() to release the fd
|
||||
* when done.
|
||||
*/
|
||||
int
|
||||
meta_anonymous_file_open_fd (MetaAnonymousFile *file,
|
||||
MetaAnonymousFileMapmode mapmode)
|
||||
{
|
||||
void *src, *dst;
|
||||
int fd;
|
||||
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
int seals;
|
||||
|
||||
seals = fcntl (file->fd, F_GET_SEALS);
|
||||
|
||||
/* file was sealed for read-only and we don't have to support MAP_SHARED
|
||||
* so we can simply pass the memfd fd
|
||||
*/
|
||||
if (seals != -1 && mapmode == META_ANONYMOUS_FILE_MAPMODE_PRIVATE &&
|
||||
(seals & READONLY_SEALS) == READONLY_SEALS)
|
||||
return file->fd;
|
||||
#endif
|
||||
|
||||
/* for all other cases we create a new anonymous file that can be mapped
|
||||
* with MAP_SHARED and copy the contents to it and return that instead
|
||||
*/
|
||||
fd = create_anonymous_file (file->size);
|
||||
if (fd == -1)
|
||||
return fd;
|
||||
|
||||
src = mmap (NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
|
||||
if (src == MAP_FAILED)
|
||||
{
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dst = mmap (NULL, file->size, PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (dst == MAP_FAILED)
|
||||
{
|
||||
close (fd);
|
||||
munmap (src, file->size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy (dst, src, file->size);
|
||||
munmap (src, file->size);
|
||||
munmap (dst, file->size);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_anonymous_file_close_fd: (skip)
|
||||
* @fd: A file descriptor obtained using meta_anonymous_file_open_fd()
|
||||
*
|
||||
* Release a file descriptor returned by meta_anonymous_file_open_fd().
|
||||
* This function must be called for every file descriptor created with
|
||||
* meta_anonymous_file_open_fd() to not leak any resources.
|
||||
*
|
||||
* If this function fails errno is set.
|
||||
*/
|
||||
void
|
||||
meta_anonymous_file_close_fd (int fd)
|
||||
{
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
int seals;
|
||||
|
||||
seals = fcntl (fd, F_GET_SEALS);
|
||||
if (seals == -1 && errno != EINVAL)
|
||||
{
|
||||
g_warning ("Reading seals of anonymous file %d failed", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The only case in which we do NOT have to close the file is when the file
|
||||
* was sealed for read-only
|
||||
*/
|
||||
if (seals != -1 && (seals & READONLY_SEALS) == READONLY_SEALS)
|
||||
return;
|
||||
#endif
|
||||
|
||||
close (fd);
|
||||
}
|
53
src/core/meta-anonymous-file.h
Normal file
53
src/core/meta-anonymous-file.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Sebastian Wick
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author: Sebastian Wick <sebastian@sebastianwick.net>
|
||||
*/
|
||||
|
||||
#ifndef META_ANONYMOUS_FILE_H
|
||||
#define META_ANONYMOUS_FILE_H
|
||||
|
||||
#include "meta/common.h"
|
||||
#include "core/util-private.h"
|
||||
|
||||
typedef struct _MetaAnonymousFile MetaAnonymousFile;
|
||||
|
||||
typedef enum _MetaAnonymousFileMapmode
|
||||
{
|
||||
META_ANONYMOUS_FILE_MAPMODE_PRIVATE,
|
||||
META_ANONYMOUS_FILE_MAPMODE_SHARED,
|
||||
} MetaAnonymousFileMapmode;
|
||||
|
||||
META_EXPORT_TEST
|
||||
MetaAnonymousFile * meta_anonymous_file_new (size_t size,
|
||||
const uint8_t *data);
|
||||
|
||||
META_EXPORT_TEST
|
||||
void meta_anonymous_file_free (MetaAnonymousFile *file);
|
||||
|
||||
META_EXPORT_TEST
|
||||
size_t meta_anonymous_file_size (MetaAnonymousFile *file);
|
||||
|
||||
META_EXPORT_TEST
|
||||
int meta_anonymous_file_open_fd (MetaAnonymousFile *file,
|
||||
MetaAnonymousFileMapmode mapmode);
|
||||
|
||||
META_EXPORT_TEST
|
||||
void meta_anonymous_file_close_fd (int fd);
|
||||
|
||||
#endif /* META_ANONYMOUS_FILE_H */
|
@@ -349,6 +349,8 @@ mutter_sources = [
|
||||
'core/main-private.h',
|
||||
'core/meta-accel-parse.c',
|
||||
'core/meta-accel-parse.h',
|
||||
'core/meta-anonymous-file.c',
|
||||
'core/meta-anonymous-file.h',
|
||||
'core/meta-border.c',
|
||||
'core/meta-border.h',
|
||||
'core/meta-clipboard-manager.c',
|
||||
@@ -456,6 +458,10 @@ if have_remote_desktop
|
||||
'backends/meta-remote-desktop-session.h',
|
||||
'backends/meta-screen-cast.c',
|
||||
'backends/meta-screen-cast.h',
|
||||
'backends/meta-screen-cast-area-stream.c',
|
||||
'backends/meta-screen-cast-area-stream.h',
|
||||
'backends/meta-screen-cast-area-stream-src.c',
|
||||
'backends/meta-screen-cast-area-stream-src.h',
|
||||
'backends/meta-screen-cast-monitor-stream.c',
|
||||
'backends/meta-screen-cast-monitor-stream.h',
|
||||
'backends/meta-screen-cast-monitor-stream-src.c',
|
||||
|
@@ -54,4 +54,10 @@ G_DECLARE_FINAL_TYPE (MetaRemoteAccessController,
|
||||
META, REMOTE_ACCESS_CONTROLLER,
|
||||
GObject)
|
||||
|
||||
META_EXPORT
|
||||
void meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController *controller);
|
||||
|
||||
META_EXPORT
|
||||
void meta_remote_access_controller_uninhibit_remote_access (MetaRemoteAccessController *controller);
|
||||
|
||||
#endif /* META_REMOTE_ACCESS_CONTROLLER_H */
|
||||
|
@@ -111,6 +111,39 @@
|
||||
<arg name="properties" type="a{sv}" direction="in" />
|
||||
<arg name="stream_path" type="o" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
RecordArea:
|
||||
@x: X position of the recorded area
|
||||
@y: Y position of the recorded area
|
||||
@width: width of the recorded area
|
||||
@height: height of the recorded area
|
||||
@properties: Properties
|
||||
@stream_path: Path to the new stream object
|
||||
|
||||
Record an area of the stage. The coordinates are in stage coordinates.
|
||||
The size of the stream does not necessarily match the size of the
|
||||
recorded area, and will depend on DPI scale of the affected monitors.
|
||||
|
||||
Available @properties include:
|
||||
|
||||
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
|
||||
Available since API version 2.
|
||||
|
||||
Available cursor mode values:
|
||||
|
||||
0: hidden - cursor is not included in the stream
|
||||
1: embedded - cursor is included in the framebuffer
|
||||
2: metadata - cursor is included as metadata in the PipeWire stream
|
||||
-->
|
||||
<method name="RecordArea">
|
||||
<arg name="x" type="i" direction="in" />
|
||||
<arg name="y" type="i" direction="in" />
|
||||
<arg name="width" type="i" direction="in" />
|
||||
<arg name="height" type="i" direction="in" />
|
||||
<arg name="properties" type="a{sv}" direction="in" />
|
||||
<arg name="stream_path" type="o" direction="out" />
|
||||
</method>
|
||||
</interface>
|
||||
|
||||
<!--
|
||||
|
@@ -59,3 +59,19 @@ executable('subsurface-remap-toplevel',
|
||||
install: have_installed_tests,
|
||||
install_dir: wayland_test_client_installed_tests_libexecdir,
|
||||
)
|
||||
|
||||
executable('meta-anonymous-file',
|
||||
sources: [
|
||||
'meta-anonymous-file.c',
|
||||
common_sources,
|
||||
],
|
||||
include_directories: tests_includepath,
|
||||
c_args: tests_c_args,
|
||||
dependencies: [
|
||||
glib_dep,
|
||||
wayland_client_dep,
|
||||
libmutter_dep,
|
||||
],
|
||||
install: have_installed_tests,
|
||||
install_dir: wayland_test_client_installed_tests_libexecdir,
|
||||
)
|
||||
|
278
src/tests/wayland-test-clients/meta-anonymous-file.c
Normal file
278
src/tests/wayland-test-clients/meta-anonymous-file.c
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Jonas Dreßler.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <core/meta-anonymous-file.h>
|
||||
|
||||
#include "wayland-test-client-utils.h"
|
||||
|
||||
#define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
|
||||
|
||||
static const char *teststring = "test string 1234567890";
|
||||
|
||||
static int
|
||||
test_read_fd_mmap (int fd,
|
||||
const char *expected_string)
|
||||
{
|
||||
void *mem;
|
||||
int string_size;
|
||||
|
||||
string_size = strlen (expected_string) + 1;
|
||||
|
||||
mem = mmap (NULL, string_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
g_assert (mem != MAP_FAILED);
|
||||
|
||||
if (strcmp (expected_string, mem) != 0)
|
||||
{
|
||||
munmap (mem, string_size);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
munmap (mem, string_size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
test_write_fd (int fd,
|
||||
const char *string)
|
||||
{
|
||||
int written_size, string_size;
|
||||
|
||||
string_size = strlen (string) + 1;
|
||||
written_size = write (fd, string, string_size);
|
||||
if (written_size != string_size)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
test_readonly_seals (int fd)
|
||||
{
|
||||
unsigned int seals;
|
||||
|
||||
seals = fcntl (fd, F_GET_SEALS);
|
||||
if (seals == -1)
|
||||
return FALSE;
|
||||
|
||||
if (seals != READONLY_SEALS)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
test_write_read (int fd)
|
||||
{
|
||||
g_autofree char *new_string = g_uuid_string_random ();
|
||||
|
||||
if (!test_write_fd (fd, new_string))
|
||||
return FALSE;
|
||||
|
||||
if (!test_read_fd_mmap (fd, new_string))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
static int
|
||||
test_open_write_read (const char *path)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open (path, O_RDWR);
|
||||
g_assert (fd != -1);
|
||||
|
||||
if (!test_write_read (fd))
|
||||
{
|
||||
close (fd);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
close (fd);
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
{
|
||||
MetaAnonymousFile *file;
|
||||
int fd = -1, other_fd = -1;
|
||||
g_autofree char *fd_path = NULL;
|
||||
|
||||
file = meta_anonymous_file_new (strlen (teststring) + 1,
|
||||
(const uint8_t *) teststring);
|
||||
if (!file)
|
||||
{
|
||||
g_critical ("%s: Creating file failed", __func__);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
||||
g_assert (fd != -1);
|
||||
other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
||||
g_assert (other_fd != -1);
|
||||
|
||||
/* When MAPMODE_PRIVATE was used, meta_anonymous_file_open_fd() should always
|
||||
* return the same fd. */
|
||||
if (other_fd != fd)
|
||||
goto fail;
|
||||
|
||||
/* If memfd_create was used and we request a MAPMODE_PRIVATE file, all the
|
||||
* readonly seals should be set. */
|
||||
if (!test_readonly_seals (fd))
|
||||
goto fail;
|
||||
|
||||
if (!test_read_fd_mmap (fd, teststring))
|
||||
goto fail;
|
||||
|
||||
/* Writing and reading the written data should fail */
|
||||
if (test_write_read (fd))
|
||||
goto fail;
|
||||
|
||||
/* Instead we should still be reading the teststring */
|
||||
if (!test_read_fd_mmap (fd, teststring))
|
||||
goto fail;
|
||||
|
||||
/* Opening the fd manually in RW mode and writing to it should fail */
|
||||
fd_path = g_strdup_printf ("/proc/%d/fd/%d", getpid (), fd);
|
||||
if (test_open_write_read (fd_path))
|
||||
goto fail;
|
||||
|
||||
/* Instead we should still be reading the teststring */
|
||||
if (!test_read_fd_mmap (fd, teststring))
|
||||
goto fail;
|
||||
|
||||
/* Just to be sure test the other fd, too */
|
||||
if (!test_read_fd_mmap (other_fd, teststring))
|
||||
goto fail;
|
||||
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
|
||||
|
||||
fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
|
||||
g_assert (fd != -1);
|
||||
other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
|
||||
g_assert (other_fd != -1);
|
||||
|
||||
/* The MAPMODE_SHARED fd should not have readonly seals applied */
|
||||
if (test_readonly_seals (fd))
|
||||
goto fail;
|
||||
|
||||
if (!test_read_fd_mmap (fd, teststring))
|
||||
goto fail;
|
||||
|
||||
if (!test_read_fd_mmap (other_fd, teststring))
|
||||
goto fail;
|
||||
|
||||
/* Writing and reading the written data should succeed */
|
||||
if (!test_write_read (fd))
|
||||
goto fail;
|
||||
|
||||
/* The other fd should still read the teststring though */
|
||||
if (!test_read_fd_mmap (other_fd, teststring))
|
||||
goto fail;
|
||||
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
meta_anonymous_file_close_fd (other_fd);
|
||||
|
||||
|
||||
/* Test an artificial out-of-space situation by setting the maximium file
|
||||
* size this process may create to 2 bytes, if memfd_create with
|
||||
* MAPMODE_PRIVATE is used, everything should still work (the existing FD
|
||||
* should be used). */
|
||||
struct rlimit limit = {2, 2};
|
||||
if (setrlimit (RLIMIT_FSIZE, &limit) == -1)
|
||||
goto fail;
|
||||
|
||||
fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
||||
g_assert (fd != -1);
|
||||
|
||||
if (!test_read_fd_mmap (fd, teststring))
|
||||
goto fail;
|
||||
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
#else
|
||||
fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
||||
g_assert (fd != -1);
|
||||
other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
|
||||
g_assert (other_fd != -1);
|
||||
|
||||
if (test_readonly_seals (fd))
|
||||
goto fail;
|
||||
|
||||
/* Writing and reading the written data should succeed */
|
||||
if (!test_write_read (fd))
|
||||
goto fail;
|
||||
|
||||
/* The other fd should still read the teststring though */
|
||||
if (!test_read_fd_mmap (other_fd, teststring))
|
||||
goto fail;
|
||||
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
meta_anonymous_file_close_fd (other_fd);
|
||||
|
||||
|
||||
fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
|
||||
g_assert (fd != -1);
|
||||
other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
|
||||
g_assert (other_fd != -1);
|
||||
|
||||
if (test_readonly_seals (fd))
|
||||
goto fail;
|
||||
|
||||
if (!test_read_fd_mmap (fd, teststring))
|
||||
goto fail;
|
||||
|
||||
if (!test_read_fd_mmap (other_fd, teststring))
|
||||
goto fail;
|
||||
|
||||
/* Writing and reading the written data should succeed */
|
||||
if (!test_write_read (fd))
|
||||
goto fail;
|
||||
|
||||
/* The other fd should still read the teststring though */
|
||||
if (!test_read_fd_mmap (other_fd, teststring))
|
||||
goto fail;
|
||||
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
meta_anonymous_file_close_fd (other_fd);
|
||||
#endif
|
||||
|
||||
meta_anonymous_file_free (file);
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
fail:
|
||||
if (fd > 0)
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
if (other_fd > 0)
|
||||
meta_anonymous_file_close_fd (other_fd);
|
||||
meta_anonymous_file_free (file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
@@ -48,15 +48,14 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "backends/meta-backend-private.h"
|
||||
#include "core/display-private.h"
|
||||
#include "core/meta-anonymous-file.h"
|
||||
#include "wayland/meta-wayland-private.h"
|
||||
|
||||
#ifdef HAVE_NATIVE_BACKEND
|
||||
@@ -79,86 +78,34 @@ unbind_resource (struct wl_resource *resource)
|
||||
wl_list_remove (wl_resource_get_link (resource));
|
||||
}
|
||||
|
||||
static int
|
||||
create_anonymous_file (off_t size,
|
||||
GError **error)
|
||||
{
|
||||
static const char template[] = "mutter-shared-XXXXXX";
|
||||
char *path;
|
||||
int fd, flags;
|
||||
|
||||
fd = g_file_open_tmp (template, &path, error);
|
||||
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
unlink (path);
|
||||
g_free (path);
|
||||
|
||||
flags = fcntl (fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
goto err;
|
||||
|
||||
if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
goto err;
|
||||
|
||||
if (ftruncate (fd, size) < 0)
|
||||
goto err;
|
||||
|
||||
return fd;
|
||||
|
||||
err:
|
||||
g_set_error_literal (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errno),
|
||||
strerror (errno));
|
||||
close (fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
send_keymap (MetaWaylandKeyboard *keyboard,
|
||||
struct wl_resource *resource)
|
||||
{
|
||||
MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info;
|
||||
GError *error = NULL;
|
||||
int fd;
|
||||
char *keymap_area;
|
||||
size_t size;
|
||||
MetaAnonymousFileMapmode mapmode;
|
||||
|
||||
if (!xkb_info->keymap_string)
|
||||
return;
|
||||
if (wl_resource_get_version (resource) < 7)
|
||||
mapmode = META_ANONYMOUS_FILE_MAPMODE_SHARED;
|
||||
else
|
||||
mapmode = META_ANONYMOUS_FILE_MAPMODE_PRIVATE;
|
||||
|
||||
fd = create_anonymous_file (xkb_info->keymap_size, &error);
|
||||
if (fd < 0)
|
||||
fd = meta_anonymous_file_open_fd (xkb_info->keymap_rofile, mapmode);
|
||||
size = meta_anonymous_file_size (xkb_info->keymap_rofile);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
g_warning ("Creating a keymap file for %lu bytes failed: %s",
|
||||
(unsigned long) xkb_info->keymap_size,
|
||||
error->message);
|
||||
g_clear_error (&error);
|
||||
g_warning ("Creating a keymap file failed: %s", strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
keymap_area = mmap (NULL, xkb_info->keymap_size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (keymap_area == MAP_FAILED)
|
||||
{
|
||||
g_warning ("Failed to mmap() %lu bytes\n",
|
||||
(unsigned long) xkb_info->keymap_size);
|
||||
close (fd);
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy (keymap_area, xkb_info->keymap_string);
|
||||
|
||||
munmap (keymap_area, xkb_info->keymap_size);
|
||||
|
||||
wl_keyboard_send_keymap (resource,
|
||||
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
||||
fd,
|
||||
keyboard->xkb_info.keymap_size);
|
||||
close (fd);
|
||||
fd, size);
|
||||
|
||||
meta_anonymous_file_close_fd (fd);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -177,6 +124,8 @@ meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard,
|
||||
struct xkb_keymap *keymap)
|
||||
{
|
||||
MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info;
|
||||
char *keymap_string;
|
||||
size_t keymap_size;
|
||||
|
||||
if (keymap == NULL)
|
||||
{
|
||||
@@ -184,20 +133,30 @@ meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard,
|
||||
return;
|
||||
}
|
||||
|
||||
g_clear_pointer (&xkb_info->keymap_string, g_free);
|
||||
xkb_keymap_unref (xkb_info->keymap);
|
||||
xkb_info->keymap = xkb_keymap_ref (keymap);
|
||||
|
||||
meta_wayland_keyboard_update_xkb_state (keyboard);
|
||||
|
||||
xkb_info->keymap_string =
|
||||
keymap_string =
|
||||
xkb_keymap_get_as_string (xkb_info->keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
|
||||
if (!xkb_info->keymap_string)
|
||||
if (!keymap_string)
|
||||
{
|
||||
g_warning ("Failed to get string version of keymap");
|
||||
return;
|
||||
}
|
||||
xkb_info->keymap_size = strlen (xkb_info->keymap_string) + 1;
|
||||
keymap_size = strlen (keymap_string) + 1;
|
||||
|
||||
xkb_info->keymap_rofile =
|
||||
meta_anonymous_file_new (keymap_size, (const uint8_t *) keymap_string);
|
||||
|
||||
free (keymap_string);
|
||||
|
||||
if (!xkb_info->keymap_rofile)
|
||||
{
|
||||
g_warning ("Failed to create anonymous file for keymap");
|
||||
return;
|
||||
}
|
||||
|
||||
inform_clients_of_new_keymap (keyboard);
|
||||
|
||||
@@ -590,7 +549,7 @@ meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
|
||||
{
|
||||
g_clear_pointer (&xkb_info->keymap, xkb_keymap_unref);
|
||||
g_clear_pointer (&xkb_info->state, xkb_state_unref);
|
||||
g_clear_pointer (&xkb_info->keymap_string, g_free);
|
||||
meta_anonymous_file_free (xkb_info->keymap_rofile);
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -49,6 +49,7 @@
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include "clutter/clutter.h"
|
||||
#include "core/meta-anonymous-file.h"
|
||||
#include "wayland/meta-wayland-types.h"
|
||||
|
||||
#define META_TYPE_WAYLAND_KEYBOARD (meta_wayland_keyboard_get_type ())
|
||||
@@ -74,8 +75,7 @@ typedef struct
|
||||
{
|
||||
struct xkb_keymap *keymap;
|
||||
struct xkb_state *state;
|
||||
size_t keymap_size;
|
||||
char *keymap_string;
|
||||
MetaAnonymousFile *keymap_rofile;
|
||||
} MetaWaylandXkbInfo;
|
||||
|
||||
struct _MetaWaylandKeyboard
|
||||
|
@@ -46,7 +46,9 @@ typedef struct
|
||||
{
|
||||
int display_index;
|
||||
char *lock_file;
|
||||
#ifdef __linux__
|
||||
int abstract_fd;
|
||||
#endif
|
||||
int unix_fd;
|
||||
char *name;
|
||||
} MetaXWaylandConnection;
|
||||
|
@@ -235,6 +235,7 @@ create_lock_file (int display, int *display_out)
|
||||
return filename;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static int
|
||||
bind_to_abstract_socket (int display,
|
||||
gboolean *fatal)
|
||||
@@ -274,6 +275,7 @@ bind_to_abstract_socket (int display,
|
||||
|
||||
return fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
bind_to_unix_socket (int display)
|
||||
@@ -381,26 +383,37 @@ meta_xwayland_override_display_number (int number)
|
||||
static gboolean
|
||||
open_display_sockets (MetaXWaylandManager *manager,
|
||||
int display_index,
|
||||
#ifdef __linux__
|
||||
int *abstract_fd_out,
|
||||
#endif
|
||||
int *unix_fd_out,
|
||||
gboolean *fatal)
|
||||
{
|
||||
int abstract_fd, unix_fd;
|
||||
#ifdef __linux__
|
||||
int abstract_fd;
|
||||
#endif
|
||||
int unix_fd;
|
||||
|
||||
#ifdef __linux__
|
||||
abstract_fd = bind_to_abstract_socket (display_index,
|
||||
fatal);
|
||||
if (abstract_fd < 0)
|
||||
return FALSE;
|
||||
#endif
|
||||
|
||||
unix_fd = bind_to_unix_socket (display_index);
|
||||
if (unix_fd < 0)
|
||||
{
|
||||
*fatal = FALSE;
|
||||
#ifdef __linux__
|
||||
close (abstract_fd);
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
*abstract_fd_out = abstract_fd;
|
||||
#endif
|
||||
*unix_fd_out = unix_fd;
|
||||
|
||||
return TRUE;
|
||||
@@ -429,7 +442,9 @@ choose_xdisplay (MetaXWaylandManager *manager,
|
||||
}
|
||||
|
||||
if (!open_display_sockets (manager, display,
|
||||
#ifdef __linux__
|
||||
&connection->abstract_fd,
|
||||
#endif
|
||||
&connection->unix_fd,
|
||||
&fatal))
|
||||
{
|
||||
@@ -617,10 +632,14 @@ meta_xwayland_start_xserver (MetaXWaylandManager *manager,
|
||||
launcher = g_subprocess_launcher_new (flags);
|
||||
|
||||
g_subprocess_launcher_take_fd (launcher, xwayland_client_fd[1], 3);
|
||||
g_subprocess_launcher_take_fd (launcher, manager->public_connection.abstract_fd, 4);
|
||||
g_subprocess_launcher_take_fd (launcher, displayfd[1], 4);
|
||||
g_subprocess_launcher_take_fd (launcher, manager->public_connection.unix_fd, 5);
|
||||
g_subprocess_launcher_take_fd (launcher, displayfd[1], 6);
|
||||
g_subprocess_launcher_take_fd (launcher, manager->private_connection.abstract_fd, 7);
|
||||
#ifdef __linux__
|
||||
g_subprocess_launcher_take_fd (launcher, manager->private_connection.abstract_fd, 6);
|
||||
g_subprocess_launcher_take_fd (launcher, manager->public_connection.abstract_fd, 7);
|
||||
#else
|
||||
g_subprocess_launcher_take_fd (launcher, manager->private_connection.unix_fd, 6);
|
||||
#endif
|
||||
|
||||
g_subprocess_launcher_setenv (launcher, "WAYLAND_SOCKET", "3", TRUE);
|
||||
|
||||
@@ -632,12 +651,14 @@ meta_xwayland_start_xserver (MetaXWaylandManager *manager,
|
||||
"-accessx",
|
||||
"-core",
|
||||
"-auth", manager->auth_file,
|
||||
"-listen", "4",
|
||||
"-displayfd", "4",
|
||||
"-listen", "5",
|
||||
"-displayfd", "6",
|
||||
#ifdef HAVE_XWAYLAND_INITFD
|
||||
"-initfd", "7",
|
||||
"-initfd", "6",
|
||||
#else
|
||||
"-listen", "6",
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
"-listen", "7",
|
||||
#endif
|
||||
NULL);
|
||||
@@ -756,14 +777,18 @@ meta_xwayland_init (MetaXWaylandManager *manager,
|
||||
{
|
||||
if (!open_display_sockets (manager,
|
||||
manager->public_connection.display_index,
|
||||
#ifdef __linux__
|
||||
&manager->public_connection.abstract_fd,
|
||||
#endif
|
||||
&manager->public_connection.unix_fd,
|
||||
&fatal))
|
||||
return FALSE;
|
||||
|
||||
if (!open_display_sockets (manager,
|
||||
manager->private_connection.display_index,
|
||||
#ifdef __linux__
|
||||
&manager->private_connection.abstract_fd,
|
||||
#endif
|
||||
&manager->private_connection.unix_fd,
|
||||
&fatal))
|
||||
return FALSE;
|
||||
@@ -774,8 +799,13 @@ meta_xwayland_init (MetaXWaylandManager *manager,
|
||||
|
||||
if (policy == META_DISPLAY_POLICY_ON_DEMAND)
|
||||
{
|
||||
#ifdef __linux__
|
||||
g_unix_fd_add (manager->public_connection.abstract_fd, G_IO_IN,
|
||||
xdisplay_connection_activity_cb, manager);
|
||||
#else
|
||||
g_unix_fd_add (manager->public_connection.unix_fd, G_IO_IN,
|
||||
xdisplay_connection_activity_cb, manager);
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
@@ -127,7 +127,6 @@ struct _MetaX11Display
|
||||
|
||||
struct {
|
||||
Window xwindow;
|
||||
uint32_t ownership_timestamp;
|
||||
MetaSelectionSource *owners[META_N_SELECTION_TYPES];
|
||||
GCancellable *cancellables[META_N_SELECTION_TYPES];
|
||||
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
#include "core/meta-selection-private.h"
|
||||
#include "meta/meta-selection-source-memory.h"
|
||||
#include "x11/meta-selection-source-x11-private.h"
|
||||
#include "x11/meta-x11-selection-output-stream-private.h"
|
||||
#include "x11/meta-x11-selection-private.h"
|
||||
@@ -31,6 +32,13 @@
|
||||
#define UTF8_STRING_MIMETYPE "text/plain;charset=utf-8"
|
||||
#define STRING_MIMETYPE "text/plain"
|
||||
|
||||
/* Set an arbitrary (although generous) threshold to determine whether a
|
||||
* XFixesSelectionNotify corresponds to a XSetSelectionOwner from another
|
||||
* client. The selection timestamp is not updated if the owner client is
|
||||
* closed.
|
||||
*/
|
||||
#define SELECTION_CLEARED_BY_CLIENT(e) (e->timestamp - e->selection_timestamp < 50)
|
||||
|
||||
static gboolean
|
||||
atom_to_selection_type (Display *xdisplay,
|
||||
Atom selection,
|
||||
@@ -297,8 +305,8 @@ source_new_cb (GObject *object,
|
||||
source = meta_selection_source_x11_new_finish (res, &error);
|
||||
if (source)
|
||||
{
|
||||
meta_selection_set_owner (selection, selection_type, source);
|
||||
g_set_object (&x11_display->selection.owners[selection_type], source);
|
||||
meta_selection_set_owner (selection, selection_type, source);
|
||||
g_object_unref (source);
|
||||
}
|
||||
else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
@@ -322,7 +330,6 @@ meta_x11_selection_handle_xfixes_selection_notify (MetaX11Display *x11_display,
|
||||
if (!atom_to_selection_type (xdisplay, event->selection, &selection_type))
|
||||
return FALSE;
|
||||
|
||||
x11_display->selection.ownership_timestamp = event->selection_timestamp;
|
||||
selection = meta_display_get_selection (meta_get_display ());
|
||||
|
||||
if (x11_display->selection.cancellables[selection_type])
|
||||
@@ -333,9 +340,19 @@ meta_x11_selection_handle_xfixes_selection_notify (MetaX11Display *x11_display,
|
||||
|
||||
x11_display->selection.cancellables[selection_type] = g_cancellable_new ();
|
||||
|
||||
if (event->owner == None)
|
||||
if (event->owner == None && x11_display->selection.owners[selection_type])
|
||||
{
|
||||
if (x11_display->selection.owners[selection_type])
|
||||
if (SELECTION_CLEARED_BY_CLIENT (event))
|
||||
{
|
||||
MetaSelectionSource *source;
|
||||
|
||||
/* Replace with an empty owner */
|
||||
source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL);
|
||||
g_set_object (&x11_display->selection.owners[selection_type], source);
|
||||
meta_selection_set_owner (selection, selection_type, source);
|
||||
g_object_unref (source);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* An X client went away, clear the selection */
|
||||
meta_selection_unset_owner (selection, selection_type,
|
||||
@@ -343,7 +360,7 @@ meta_x11_selection_handle_xfixes_selection_notify (MetaX11Display *x11_display,
|
||||
g_clear_object (&x11_display->selection.owners[selection_type]);
|
||||
}
|
||||
}
|
||||
else if (event->owner != x11_display->selection.xwindow)
|
||||
else if (event->owner != None && event->owner != x11_display->selection.xwindow)
|
||||
{
|
||||
SourceNewData *data;
|
||||
|
||||
@@ -361,8 +378,6 @@ meta_x11_selection_handle_xfixes_selection_notify (MetaX11Display *x11_display,
|
||||
data);
|
||||
}
|
||||
|
||||
x11_display->selection.ownership_timestamp = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -384,9 +399,8 @@ notify_selection_owner (MetaX11Display *x11_display,
|
||||
MetaSelectionSource *new_owner)
|
||||
{
|
||||
Display *xdisplay = x11_display->xdisplay;
|
||||
uint32_t timestamp;
|
||||
|
||||
if (new_owner && !META_IS_SELECTION_SOURCE_X11 (new_owner))
|
||||
if (new_owner && new_owner != x11_display->selection.owners[selection_type])
|
||||
{
|
||||
if (x11_display->selection.cancellables[selection_type])
|
||||
{
|
||||
@@ -394,17 +408,13 @@ notify_selection_owner (MetaX11Display *x11_display,
|
||||
g_clear_object (&x11_display->selection.cancellables[selection_type]);
|
||||
}
|
||||
|
||||
timestamp = x11_display->selection.ownership_timestamp ?
|
||||
x11_display->selection.ownership_timestamp :
|
||||
meta_display_get_current_time (x11_display->display);
|
||||
|
||||
/* If the owner is non-X11, claim the selection on our selection
|
||||
* window, so X11 apps can interface with it.
|
||||
*/
|
||||
XSetSelectionOwner (xdisplay,
|
||||
selection_to_atom (selection_type, xdisplay),
|
||||
x11_display->selection.xwindow,
|
||||
timestamp);
|
||||
META_CURRENT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user