Compare commits

...

47 Commits

Author SHA1 Message Date
Giovanni Campagna
c2bf44fa19 Bump version to 3.9.91
Update NEWS
2013-09-05 11:01:53 +02:00
Adel Gadllah
2d67b01c3a window-actor: Use more conservative paint volume
We know which part of the window is not obscured, so take advantaged of that
to limit the size of the paint volume that we report.
2013-09-04 20:08:24 +02:00
Neil Roberts
6014d31dea Don't create a dummy texture for the texture pipeline template
The meta_create_texture_pipeline function used to create a dummy 1x1
texture so that it could make sure that the all of the state that
affects the shader generation would be set on the template pipeline so
that Cogl could share the pipeline's shader with any other pipelines
that are just rendering a texture. This is no longer necessary because
the only thing that affects the shader generation is the texture type,
not the actual texture data and Cogl now has a function to explicitly
set the texture type which we can use instead. Additionally even if
the template mechanism is not used at all Cogl will still end up
reusing the same shader because it now has a shader cache which is
indexed by the pipeline state so pipeline's don't strictly need to
share ancestry in order to take advantage of it. However we still
might as well use the function because if there is a common ancestry
it is faster to look up the shader because Cogl doesn't need to hash
the pipeline state.

https://bugzilla.gnome.org/show_bug.cgi?id=707458
(cherry picked from commit c5bf60eab4)
2013-09-04 15:29:04 +01:00
Jasper St. Pierre
bf78f067a2 Make sure to always call va_end 2013-09-03 16:10:10 -04:00
Jasper St. Pierre
ddb9929dad compositor: Remove some uninitialized variables 2013-09-03 16:10:10 -04:00
Jasper St. Pierre
2098ec2d16 theme-parser: Remove a duplicate paste
How did this sneak in there?
2013-09-03 16:10:10 -04:00
Jasper St. Pierre
97eece6607 display: Add some missing breaks
If we somehow get an event with a wrong device ID, we should not
be comparing bad event IDs.
2013-09-03 16:10:10 -04:00
Ray Strode
7ac9a6e241 compositor: initial has_window to FALSE in sync_actor_stacking
Spotted by Jasper, discussed on IRC.
2013-09-03 16:09:05 -04:00
Giovanni Campagna
b53bf0e8c2 Fix distcheck
Missing translation files, missing dists and some -Werrors.
2013-09-03 17:00:18 +02:00
Giovanni Campagna
a26ded47d9 Add a private gtk-mutter protocol
Add a new interface, gtk_shell, than can be used by gtk to
retrieve a surface extension called gtk_surface, which will be
used to communicate with mutter all the GTK extensions to EWMH

https://bugzilla.gnome.org/show_bug.cgi?id=707128

Add support for GTK application menus

To do so, we need to be able to set surface state before creating
the MetaWindow, so we introduce MetaWaylandSurfaceInitialState as
a staging area.
The gtk-shell-surface implementation would either write to the
initial state, or directly to the window.

At the same, implement set_title and set_class too, because it's
easy enough.

https://bugzilla.gnome.org/show_bug.cgi?id=707128
2013-09-03 10:57:28 -04:00
Giovanni Campagna
cad9e14463 Move surface state tracking and surface interface to a separate file
Move everything surface related from meta-wayland.c to meta-wayland-surface.c

https://bugzilla.gnome.org/show_bug.cgi?id=707128
2013-09-03 10:57:28 -04:00
Giovanni Campagna
806d5939e3 wayland: split headers and distribute structure definitions
Instead of having all structures in one huge headers, move them
in the appropriate place, and create one header for surface state.

https://bugzilla.gnome.org/show_bug.cgi?id=707128
2013-09-03 10:57:28 -04:00
Giovanni Campagna
0f0c23fbab wayland: generalize ShellSurface to SurfaceExtension
We want to implement new shells and new extension interfaces
for wl_surface, so generalize the book-keeping structures.

https://bugzilla.gnome.org/show_bug.cgi?id=707128
2013-09-03 10:57:28 -04:00
Jasper St. Pierre
5d1fcc26c0 Update .gitignore 2013-09-03 10:57:28 -04:00
Giovanni Campagna
cbe5b6b3bc meta-wayland: intersect the damage region with the window size before applying
According to the wayland documentation, damage outside the
window size is ignored.
This happened with xwayland+wlshm (causing a GL error when calling
TexSubImage2D), probably due to not resizing the buffer
until we receive the corresponding X event.
Might also be an off-by-one in xwayland, as the window size did
not actually change.

Note: we might want to take the configure_notify path instead,
and keep the GL/clutter size consistent with wayland rather than
X, because in the end that's what matters for events and composition.

https://bugzilla.gnome.org/show_bug.cgi?id=706289
2013-09-03 10:16:47 -04:00
Giovanni Campagna
590cf4e832 MonitorXrandr: check the event timestamps before reconfiguring
If, checking the event timestamps, we see that a new configuration
was explicitly requested by an another XRandR client, don't proceed to
apply the intended configuration again, even if looking at the
EDIDs it appears that the outputs changed.
This works around some buggy Xorg drivers (qxl, vbox) that generate
a new serial number everytime the user resizes the host window.

https://bugzilla.gnome.org/show_bug.cgi?id=706735
2013-09-02 17:36:03 +02:00
Cosimo Cecchi
22ca820c44 idle-monitor: fix event propagation to devices
device_id_max is set to the device_id in ensure_device_monitor(), but we
will loop only to (device_id_max - 1) when propagating the sync XEvent
down, missing the device correspondng to device_id_max.

https://bugzilla.gnome.org/show_bug.cgi?id=707250
2013-09-01 17:51:10 -07:00
Jasper St. Pierre
b1206ceb66 wayland: Ensure that opaque / input regions are set at commit time
The protocol specification says that opaque / input regions should be
considered pending state and should only be actually swapped out when
the surface is committed, so it can be set atomically.
2013-08-31 13:29:02 -04:00
Giovanni Campagna
ad4053ab84 wayland: fix pointer focus for destroyed surfaces
We had an assertion in meta_wayland_surface_free() that after
a repick() we would not choose the freed surface, but that didn't
consider surfaces destroyed while holding the implicit pointer
grab (ie, because the user clicked on the X button). In that case,
we need to bypass the grab infrastructure and explicitly unfocus
the dead surface.

https://bugzilla.gnome.org/show_bug.cgi?id=706982
2013-08-30 16:07:00 +02:00
Colin Walters
7bd4e6ecb0 weston-launch: Don't segfault if passed no arguments
Probably not exploitable since I think Linux always puts NULL there,
but let's actually check.
2013-08-30 08:26:31 -04:00
Giovanni Campagna
deeb1db1ac wayland: don't free surfaces that have a window associated
After a MetaWaylandSurface is associated with a MetaWindow, it
should be freed only when the MetaWindow is unmanaged. For wayland
clients, the window is unmanaged when the resource is destroyed,
but for X11 clients we want to wait for the unmap event.

https://bugzilla.gnome.org/show_bug.cgi?id=705917
2013-08-30 10:37:25 +02:00
Giovanni Campagna
7eb4bfbea3 wayland: implement support for plugin modality
Calling XIGrabDevice has no effect under wayland, because the
xserver is getting events from us. Instead, we need to use our
own interfaces for grabs.
At the same time, we can simplify the public API, as plugins
should always listen for events using clutter.

https://bugzilla.gnome.org/show_bug.cgi?id=705917
2013-08-30 10:37:25 +02:00
Giovanni Campagna
59b274f12f MetaWaylandKeyboard: use the new clutter-evdev support for xkbcommon
We need to track the full xkb_state to have the necessary information
to send to the clients, otherwise they may get confused and lock
or invert the modifiers. In the evdev backend, we just retrieve the
same state object that clutter is using, while in the other backends
we fake the state using what clutter is providing (which is a subset
of what X11 provides, which would be necessary to have full state)

https://bugzilla.gnome.org/show_bug.cgi?id=705862
2013-08-30 10:37:25 +02:00
Giovanni Campagna
e311cef013 Add keybindings for switching VTs
Once mutter is started from weston-launch on its own VT, there is
no way to change VT again (for example to actually start an application),
because the keyboard is put in raw mode.
So introduce some keybindings mimicking the standard X ones (Ctrl+Alt+Fn)
that switch the VT manually when activated.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-30 10:32:55 +02:00
Giovanni Campagna
e72f81c24f wayland: add TTY and DRM master management
Now that we have a setuid launcher binary, we can make use of
using a private protocol through the socket we're passed at startup.

We also use the new hook in clutter-evdev to ask mutter-launch for
the FDs of the input devices we need, and we emulate the old X
DRM lock with a nested GMainContext without sources.

In the future, mutter-launch will be replaced with the new logind
API currently in development.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-30 10:32:55 +02:00
Giovanni Campagna
96fa518576 mutter-launch: augment with VT and TTY handling
Set the TTY mode appropriately at startup, and clean it up
when the compositor exits. Also, take control of VT switching,
including the calls to drmSetMaster and drmDropMaster as appropriate.
In the future, we the kernel implements the mute evdev ioctl,
we'll also make sure that input devices are appropriately released.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-30 10:32:55 +02:00
Giovanni Campagna
e263b3624c mutter-launch: use systemd to obtain the TTY
Using the command line or an environment variable is dangerous,
as those can be spoofed to gain access to other sessions.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-30 10:32:55 +02:00
Giovanni Campagna
1c34f0b342 mutter-launch: make sure that the spawned binaries sees the right libraries
Being a setuid binary, our LD_LIBRARY_PATH is cleared by glibc at
startup, but we need the spawned binary to see it, otherwise
jhbuild doesn't work, so hardcode it using the configured libdir.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-30 10:32:55 +02:00
Giovanni Campagna
2d27873f98 mutter-launch: simplify by removing features we don't need
Remove the ability to launch as a different user, which we don't
need because we're spawned by gdm or by the user manually on the
command line.
At the same time, require an active local session, and remove
the ability to run from anywhere by being in the right user group
(which automatically gives you root-like privileges)

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-30 10:32:55 +02:00
Giovanni Campagna
fd40a12213 wayland: import weston-launch setuid launcher
To run mutter as a display server, one needs to acquire and
release the DRM master, which is only possible for root, so
we take advantage of weston-launch, a small setuid helper binary
written for the weston project. We import our own slightly
modified copy of it, because weston-launch only launches weston,
for security reasons.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-30 10:32:55 +02:00
Jasper St. Pierre
57406e0a9e shaped-texture: Actually fetch rectangles from the blended_region
We checked for the blended region but actually fetched rectangles from
the clip region.
2013-08-29 18:35:18 -04:00
Jasper St. Pierre
1987cbb764 window-actor: Fix build 2013-08-29 17:16:15 -04:00
Jasper St. Pierre
705978405b shaped-texture: Fix build 2013-08-29 17:14:11 -04:00
Adel Gadllah
daba05f6a7 background: Rename visible_region to clip_region
This does better reflect what this region is used for.

https://bugzilla.gnome.org/show_bug.cgi?id=703332
2013-08-29 17:08:57 -04:00
Jasper St. Pierre
1a7984be43 window-actor: Use g_clear_pointer 2013-08-29 17:05:41 -04:00
Jasper St. Pierre
1b3c77d53a shaped-texture: Remove an unnecessary set to NULL 2013-08-29 17:05:36 -04:00
Jasper St. Pierre
5b0f0d9e5b shaped-texture: Remove bad comment
We do not assume ownership of the clip region anymore.
2013-08-29 17:04:55 -04:00
Adel Gadllah
9816659fa9 meta-window-actor: Fix spacing 2013-08-29 17:04:50 -04:00
Adel Gadllah
3b1b611634 meta-window-actor: Throttle obscured frame synced apps
We must send frame_drawn and frame_timing messages to even when
we don't actually queue a redraw on screen to comply with the
WM sync spec.

So throttle such apps to down to a ~100ms interval.

https://bugzilla.gnome.org/show_bug.cgi?id=703332
2013-08-29 17:04:36 -04:00
Adel Gadllah
f08921bd0c meta-shaped-texture: Don't queue redraws for obscured regions
When we get a damage event we update the window by calling
meta_shaped_texture_update_area which queues a redraw on the actor.
We can avoid that for obscured regions by comparing the damage area to
our visible area.

This patch causes _NET_WM_FRAME_DRAWN messages to be not sent in some cases
where they should be sent; they will be added back in a later commit.

https://bugzilla.gnome.org/show_bug.cgi?id=703332
2013-08-29 17:03:48 -04:00
Jasper St. Pierre
0089b5769c shaped-texture: Turn blending off when drawing entirely opaque regions
When drawing entirely opaque regions, we traditionally kept blending on
simply because it made the code more convenient and obvious to handle.
However, this can cause lots of performance issues on GPUs that aren't
too powerful, as they have to readback the buffer underneath.

Keep track of the opaque region set by windows (through _NET_WM_OPAQUE_REGION,
Wayland opaque_region hints, standard RGB32 frame masks or similar), and draw
those rectangles separately through a different path with blending turned off.

https://bugzilla.gnome.org/show_bug.cgi?id=707019
2013-08-29 15:31:53 -04:00
Jasper St. Pierre
452be05ea0 shaped-texture: Use non-deprecated cogl APIs
https://bugzilla.gnome.org/show_bug.cgi?id=707019
2013-08-29 15:31:53 -04:00
Jasper St. Pierre
69f038f7c7 shaped-texture: Simplify pipeline creation
Split out pipeline creation to a separate function so that we don't
have so much dense code in the paint function itself, and remove some
indentation levels.

Also, don't use our own template for the unmasked pipeline, since it
has nothing different from the default pipeline template.

We also don't store the pipelines anymore since their creation isn't
really helping us; we set the mask texture and paint texture on every
paint anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=707019
2013-08-29 15:31:52 -04:00
Jasper St. Pierre
2d35e07fae wayland: Add support for set_opaque_region / set_input_region
https://bugzilla.gnome.org/show_bug.cgi?id=707019
2013-08-29 15:31:52 -04:00
Jasper St. Pierre
95e2d26d03 window-actor: Mark all Wayland clients as argb32
https://bugzilla.gnome.org/show_bug.cgi?id=707019
2013-08-29 15:26:49 -04:00
Jasper St. Pierre
f506e090ea monitor: Use a more sane dummy configuration for nested mode 2013-08-29 15:26:49 -04:00
Jasper St. Pierre
0697b53826 wayland-private: Remove unused buffer fields 2013-08-28 15:28:34 -04:00
53 changed files with 4635 additions and 1954 deletions

11
.gitignore vendored
View File

@@ -48,10 +48,11 @@ po/*.pot
50-metacity-key.xml
libmutter-wayland.pc
mutter-wayland
mutter-theme-viewer
mutter-wayland.desktop
mutter-launch
org.gnome.mutter.gschema.valid
org.gnome.mutter.gschema.xml
org.gnome.mutter.wayland.gschema.valid
org.gnome.mutter.wayland.gschema.xml
testasyncgetprop
testboxes
testgradient
@@ -77,6 +78,12 @@ src/stamp-mutter-marshal.h
src/meta-dbus-xrandr.[ch]
src/meta-dbus-idle-monitor.[ch]
src/mutter-plugins.pc
src/wayland/gtk-shell-protocol.c
src/wayland/gtk-shell-client-protocol.h
src/wayland/gtk-shell-server-protocol.h
src/wayland/xserver-protocol.c
src/wayland/xserver-client-protocol.h
src/wayland/xserver-server-protocol.h
doc/reference/*.args
doc/reference/*.bak
doc/reference/*.hierarchy

33
NEWS
View File

@@ -1,3 +1,36 @@
3.9.91
======
* Drop man pages for removed utilities [Kalev; #706579]
* Add support for idle tracking [Giovanni, Cosimo; #706005, #707250]
* Skip CRTC reconfigurations that have no effect [Giovanni; #706672]
* Ignore skip-taskbar hints on parentless dialogs [Giovanni; #673399]
* Don't save pixbuf data in user data [Tim; #706777]
* Don't queue redraws for obscured regions [Adel; #703332]
* Suppor the opaque region hints for wayland clients [Jasper; #707019]
* Turn blending off when drawing entirely opaque regions [Jasper; #707019]
* Check event timestamps before reconfiguring [Giovanni; #706735]
* Merge the DBus API for display configuration in the wayland branch [Giovanni]
* Install an X IO error handler for XWayland [Giovanni; #706962]
* Use the clutter xkbcommon integration for the wayland keyboard [Giovanni; #705862]
* Add a setuid helper for running on KMS+evdev [Giovanni, Colin; #705861]
* Add keybindings for switching VT [Giovanni; #705861]
* Implement plugin modality when running as a wayland compositor [Giovanni; #705917]
* Add support for the application menu for wayland clients [Giovanni; #707128]
* Several Coverity spotted fixes [Jasper]
* Don't create a dummy texture for the texture template [Neil; #707458]
* Use a more conservative paint volume for obscured windows [Adel]
* Misc bug fixes [Giovanni, Colin, Seán, Jasper, Cosimo; #706582, #706598,
#706787, #706729, #706825, #707081, #707090, #707267, #706982, #706289]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Colin Guthrie, Kalev Lember,
Tim Lunn, Jasper St. Pierre, Neil Roberts, Rico Tzschichholz, Seán de Búrca
Translations:
Piotr Drąg [pl], Alexandre Franke [fr], Kjartan Maraas [nb],
Milo Casagrande [it], Balázs Úr [hu], Seán de Búrca [ga], Fran Diéguez [gl],
Daniel Mustieles [es], Aurimas Černius [lt], Gil Forcada [ca]
3.9.90
======
* First release from the wayland branch, includes basic support for running

View File

@@ -2,7 +2,7 @@ AC_PREREQ(2.50)
m4_define([mutter_major_version], [3])
m4_define([mutter_minor_version], [9])
m4_define([mutter_micro_version], [90])
m4_define([mutter_micro_version], [91])
m4_define([mutter_version],
[mutter_major_version.mutter_minor_version.mutter_micro_version])
@@ -139,6 +139,12 @@ AM_GLIB_GNU_GETTEXT
## here we get the flags we'll actually use
# GRegex requires Glib-2.14.0
PKG_CHECK_MODULES(ALL, glib-2.0 >= 2.14.0)
PKG_CHECK_MODULES(MUTTER_LAUNCH, libdrm libsystemd-login)
saved_LIBS="$LIBS"
LIBS="$LIBS $MUTTER_LAUNCH"
AC_CHECK_FUNCS([sd_session_get_vt])
LIBS="$saved_LIBS"
# Unconditionally use this dir to avoid a circular dep with gnomecc
GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings"
@@ -226,7 +232,7 @@ AS_IF([test "x$WAYLAND_SCANNER" = "xno"],
AC_SUBST([WAYLAND_SCANNER])
AC_SUBST(XWAYLAND_PATH)
MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server"
MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server libdrm"
PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
PKG_CHECK_EXISTS([xi >= 1.6.99.1],

View File

@@ -21,9 +21,9 @@ src/core/util.c
src/core/window.c
src/core/window-props.c
src/core/xprops.c
src/mutter.desktop.in
src/mutter-wm.desktop.in
src/mutter-wayland.desktop.in
src/org.gnome.mutter.gschema.xml.in
src/org.gnome.mutter.wayland.gschema.xml.in
src/ui/frames.c
src/ui/menu.c
src/ui/metaaccellabel.c

30
protocol/gtk-shell.xml Normal file
View File

@@ -0,0 +1,30 @@
<protocol name="gtk">
<interface name="gtk_shell" version="1">
<enum name="capability">
<entry name="global_app_menu" value="1"/>
<entry name="global_menu_bar" value="2"/>
</enum>
<event name="capabilities">
<arg name="capabilities" type="uint"/>
</event>
<request name="get_gtk_surface">
<arg name="gtk_surface" type="new_id" interface="gtk_surface"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
</interface>
<interface name="gtk_surface" version="1">
<request name="set_dbus_properties">
<arg name="application_id" type="string" allow-null="true"/>
<arg name="app_menu_path" type="string" allow-null="true"/>
<arg name="menubar_path" type="string" allow-null="true"/>
<arg name="window_object_path" type="string" allow-null="true"/>
<arg name="application_object_path" type="string" allow-null="true"/>
<arg name="unique_bus_name" type="string" allow-null="true"/>
</request>
</interface>
</protocol>

View File

@@ -40,6 +40,9 @@ mutter_built_sources = \
$(dbus_xrandr_built_sources) \
mutter-enum-types.h \
mutter-enum-types.c \
wayland/gtk-shell-protocol.c \
wayland/gtk-shell-server-protocol.h \
wayland/gtk-shell-client-protocol.h \
wayland/xserver-protocol.c \
wayland/xserver-server-protocol.h \
wayland/xserver-client-protocol.h
@@ -195,7 +198,12 @@ libmutter_wayland_la_SOURCES += \
wayland/meta-wayland-seat.c \
wayland/meta-wayland-seat.h \
wayland/meta-wayland-stage.h \
wayland/meta-wayland-stage.c
wayland/meta-wayland-stage.c \
wayland/meta-wayland-surface.c \
wayland/meta-wayland-surface.h \
wayland/meta-wayland-types.h \
wayland/meta-weston-launch.c \
wayland/meta-weston-launch.h
libmutter_wayland_la_LDFLAGS = -no-undefined
libmutter_wayland_la_LIBADD = $(MUTTER_LIBS)
@@ -247,6 +255,17 @@ bin_PROGRAMS=mutter-wayland
mutter_wayland_SOURCES = core/mutter.c
mutter_wayland_LDADD = $(MUTTER_LIBS) libmutter-wayland.la
bin_PROGRAMS+=mutter-launch
mutter_launch_SOURCES = wayland/weston-launch.c wayland/weston-launch.h
mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS) -DLIBDIR=\"$(libdir)\"
mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam
install-exec-hook:
-chown root $(DESTDIR)$(bindir)/mutter-launch
-chmod u+s $(DESTDIR)$(bindir)/mutter-launch
if HAVE_INTROSPECTION
include $(INTROSPECTION_MAKEFILE)
@@ -306,7 +325,7 @@ xml_DATA = $(xml_in_files:.xml.in=.xml)
dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h
gsettings_SCHEMAS = org.gnome.mutter.gschema.xml
gsettings_SCHEMAS = org.gnome.mutter.gschema.xml org.gnome.mutter.wayland.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
@GSETTINGS_RULES@
@@ -317,6 +336,7 @@ CLEANFILES = \
mutter-wayland.desktop \
mutter-wm.desktop \
org.gnome.mutter.gschema.xml \
org.gnome.mutter.wayland.gschema.xml \
$(xml_DATA) \
$(mutter_built_sources) \
$(typelib_DATA) \
@@ -333,10 +353,12 @@ EXTRA_DIST=$(desktopfiles_files) \
$(wmproperties_in_files) \
$(xml_in_files) \
org.gnome.mutter.gschema.xml.in \
org.gnome.mutter.wayland.gschema.xml.in \
mutter-schemas.convert \
libmutter-wayland.pc.in \
mutter-enum-types.h.in \
mutter-enum-types.c.in
mutter-enum-types.c.in \
xrandr.xml idle-monitor.xml
BUILT_SOURCES = $(mutter_built_sources)
MUTTER_STAMP_FILES = stamp-mutter-enum-types.h

View File

@@ -50,7 +50,7 @@ meta_create_color_texture_4ub (guint8 red,
CoglColor color;
guint8 pixel[4];
cogl_color_set_from_4ub (&color, red, green, blue, alpha);
cogl_color_init_from_4ub (&color, red, green, blue, alpha);
cogl_color_premultiply (&color);
pixel[0] = cogl_color_get_red_byte (&color);
@@ -73,10 +73,8 @@ meta_create_color_texture_4ub (guint8 red,
* @src_texture: (allow-none): texture to use initially for the layer
*
* Creates a pipeline with a single layer. Using a common template
* allows sharing a shader for different uses in Mutter. To share the same
* shader with all other pipelines that are just texture plus opacity
* would require Cogl fixes.
* (See http://bugzilla.clutter-project.org/show_bug.cgi?id=2425)
* makes it easier for Cogl to share a shader for different uses in
* Mutter.
*
* Return value: (transfer full): a newly created #CoglPipeline
*/
@@ -86,22 +84,21 @@ meta_create_texture_pipeline (CoglTexture *src_texture)
static CoglPipeline *texture_pipeline_template = NULL;
CoglPipeline *pipeline;
/* We use a pipeline that has a dummy texture as a base for all
texture pipelines. The idea is that only the Cogl texture object
would be different in the children so it is likely that Cogl will
be able to share GL programs between all the textures. */
/* The only state used in the pipeline that would affect the shader
generation is the texture type on the layer. Therefore we create
a template pipeline which sets this state and all texture
pipelines are created as a copy of this. That way Cogl can find
the shader state for the pipeline more quickly by looking at the
pipeline ancestry instead of resorting to the shader cache. */
if (G_UNLIKELY (texture_pipeline_template == NULL))
{
CoglTexture *dummy_texture;
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
dummy_texture = meta_create_color_texture_4ub (0xff, 0xff, 0xff, 0xff,
COGL_TEXTURE_NONE);
CoglContext *ctx =
clutter_backend_get_cogl_context (clutter_get_default_backend ());
texture_pipeline_template = cogl_pipeline_new (ctx);
cogl_pipeline_set_layer_texture (texture_pipeline_template, 0, dummy_texture);
cogl_object_unref (dummy_texture);
cogl_pipeline_set_layer_null_texture (texture_pipeline_template,
0, /* layer */
COGL_TEXTURE_TYPE_2D);
}
pipeline = cogl_pipeline_copy (texture_pipeline_template);

View File

@@ -85,6 +85,8 @@
#include "window-private.h" /* to check window->hidden */
#include "display-private.h" /* for meta_display_lookup_x_window() */
#include "meta-wayland-private.h"
#include "meta-wayland-pointer.h"
#include "meta-wayland-keyboard.h"
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
@@ -424,34 +426,20 @@ meta_stage_is_focused (MetaScreen *screen)
return (screen->display->focus_type == META_FOCUS_STAGE);
}
gboolean
meta_begin_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
MetaModalOptions options,
guint32 timestamp)
static gboolean
begin_modal_x11 (MetaScreen *screen,
MetaPlugin *plugin,
MetaModalOptions options,
guint32 timestamp)
{
/* To some extent this duplicates code in meta_display_begin_grab_op(), but there
* are significant differences in how we handle grabs that make it difficult to
* merge the two.
*/
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdpy = meta_display_get_xdisplay (display);
MetaCompositor *compositor = display->compositor;
ClutterStage *stage;
Window grab_window;
Cursor cursor = None;
gboolean pointer_grabbed = FALSE;
gboolean keyboard_grabbed = FALSE;
int result;
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
if (!stage)
return FALSE;
grab_window = clutter_x11_get_stage_window (stage);
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
return FALSE;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdpy = meta_display_get_xdisplay (display);
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
Window grab_window = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
Cursor cursor = None;
int result;
gboolean pointer_grabbed = FALSE;
gboolean keyboard_grabbed = FALSE;
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
{
@@ -501,14 +489,6 @@ meta_begin_modal_for_plugin (MetaScreen *screen,
keyboard_grabbed = TRUE;
}
display->grab_op = META_GRAB_OP_COMPOSITOR;
display->grab_window = NULL;
display->grab_screen = screen;
display->grab_have_pointer = TRUE;
display->grab_have_keyboard = TRUE;
compositor->modal_plugin = plugin;
return TRUE;
fail:
@@ -520,6 +500,80 @@ meta_begin_modal_for_plugin (MetaScreen *screen,
return FALSE;
}
static gboolean
begin_modal_wayland (MetaScreen *screen,
MetaPlugin *plugin,
MetaModalOptions options,
guint32 timestamp)
{
MetaWaylandCompositor *compositor;
gboolean pointer_grabbed = FALSE;
gboolean keyboard_grabbed = FALSE;
compositor = meta_wayland_compositor_get_default ();
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
{
if (!meta_wayland_pointer_begin_modal (&compositor->seat->pointer))
goto fail;
pointer_grabbed = TRUE;
}
if ((options & META_MODAL_KEYBOARD_ALREADY_GRABBED) == 0)
{
if (!meta_wayland_keyboard_begin_modal (&compositor->seat->keyboard,
timestamp))
goto fail;
keyboard_grabbed = TRUE;
}
return TRUE;
fail:
if (pointer_grabbed)
meta_wayland_pointer_end_modal (&compositor->seat->pointer);
if (keyboard_grabbed)
meta_wayland_keyboard_end_modal (&compositor->seat->keyboard, timestamp);
return FALSE;
}
gboolean
meta_begin_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
MetaModalOptions options,
guint32 timestamp)
{
/* To some extent this duplicates code in meta_display_begin_grab_op(), but there
* are significant differences in how we handle grabs that make it difficult to
* merge the two.
*/
MetaDisplay *display = meta_screen_get_display (screen);
MetaCompositor *compositor = display->compositor;
gboolean ok;
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
return FALSE;
if (meta_is_wayland_compositor ())
ok = begin_modal_wayland (screen, plugin, options, timestamp);
else
ok = begin_modal_x11 (screen, plugin, options, timestamp);
if (!ok)
return FALSE;
display->grab_op = META_GRAB_OP_COMPOSITOR;
display->grab_window = NULL;
display->grab_screen = screen;
display->grab_have_pointer = TRUE;
display->grab_have_keyboard = TRUE;
compositor->modal_plugin = plugin;
return TRUE;
}
void
meta_end_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
@@ -531,8 +585,19 @@ meta_end_modal_for_plugin (MetaScreen *screen,
g_return_if_fail (compositor->modal_plugin == plugin);
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_POINTER_ID, timestamp);
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp);
if (meta_is_wayland_compositor ())
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
meta_wayland_pointer_end_modal (&compositor->seat->pointer);
meta_wayland_keyboard_end_modal (&compositor->seat->keyboard,
timestamp);
}
else
{
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_POINTER_ID, timestamp);
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp);
}
display->grab_op = META_GRAB_OP_NONE;
display->grab_window = NULL;
@@ -629,7 +694,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
MetaCompScreen *info;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
Window xwin;
Window xwin = None;
gint width, height;
MetaWaylandCompositor *wayland_compositor;
@@ -930,8 +995,8 @@ is_grabbed_event (MetaDisplay *display,
}
void
meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
MetaWindow *window)
meta_compositor_window_shape_changed (MetaCompositor *compositor,
MetaWindow *window)
{
MetaWindowActor *window_actor;
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
@@ -1210,6 +1275,7 @@ sync_actor_stacking (MetaCompScreen *info)
* we go ahead and do it */
children = clutter_actor_get_children (info->window_group);
has_windows = FALSE;
reordered = FALSE;
/* We allow for actors in the window group other than the actors we

View File

@@ -6,9 +6,9 @@
#include <meta/screen.h>
#include <meta/meta-background-actor.h>
void meta_background_actor_set_visible_region (MetaBackgroundActor *self,
cairo_region_t *visible_region);
void meta_background_actor_set_clip_region (MetaBackgroundActor *self,
cairo_region_t *clip_region);
cairo_region_t *meta_background_actor_get_visible_region (MetaBackgroundActor *self);
cairo_region_t *meta_background_actor_get_clip_region (MetaBackgroundActor *self);
#endif /* META_BACKGROUND_ACTOR_PRIVATE_H */

View File

@@ -44,7 +44,7 @@
struct _MetaBackgroundActorPrivate
{
cairo_region_t *visible_region;
cairo_region_t *clip_region;
};
G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR);
@@ -54,7 +54,7 @@ meta_background_actor_dispose (GObject *object)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
meta_background_actor_set_visible_region (self, NULL);
meta_background_actor_set_clip_region (self, NULL);
G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object);
}
@@ -167,17 +167,17 @@ meta_background_actor_new (void)
}
/**
* meta_background_actor_set_visible_region:
* meta_background_actor_set_clip_region:
* @self: a #MetaBackgroundActor
* @visible_region: (allow-none): the area of the actor (in allocate-relative
* @clip_region: (allow-none): the area of the actor (in allocate-relative
* coordinates) that is visible.
*
* Sets the area of the background that is unobscured by overlapping windows.
* This is used to optimize and only paint the visible portions.
*/
void
meta_background_actor_set_visible_region (MetaBackgroundActor *self,
cairo_region_t *visible_region)
meta_background_actor_set_clip_region (MetaBackgroundActor *self,
cairo_region_t *clip_region)
{
MetaBackgroundActorPrivate *priv;
@@ -185,16 +185,16 @@ meta_background_actor_set_visible_region (MetaBackgroundActor *self,
priv = self->priv;
g_clear_pointer (&priv->visible_region,
g_clear_pointer (&priv->clip_region,
(GDestroyNotify)
cairo_region_destroy);
if (visible_region)
priv->visible_region = cairo_region_copy (visible_region);
if (clip_region)
priv->clip_region = cairo_region_copy (clip_region);
}
/**
* meta_background_actor_get_visible_region:
* meta_background_actor_get_clip_region:
* @self: a #MetaBackgroundActor
*
* Return value (transfer full): a #cairo_region_t that represents the part of
@@ -202,16 +202,16 @@ meta_background_actor_set_visible_region (MetaBackgroundActor *self,
* #MetaWindowActor objects.
*/
cairo_region_t *
meta_background_actor_get_visible_region (MetaBackgroundActor *self)
meta_background_actor_get_clip_region (MetaBackgroundActor *self)
{
MetaBackgroundActorPrivate *priv = self->priv;
ClutterActorBox content_box;
cairo_rectangle_int_t content_area = { 0 };
cairo_region_t *visible_region;
cairo_region_t *clip_region;
g_return_val_if_fail (META_IS_BACKGROUND_ACTOR (self), NULL);
if (!priv->visible_region)
if (!priv->clip_region)
return NULL;
clutter_actor_get_content_box (CLUTTER_ACTOR (self), &content_box);
@@ -221,8 +221,8 @@ meta_background_actor_get_visible_region (MetaBackgroundActor *self)
content_area.width = content_box.x2 - content_box.x1;
content_area.height = content_box.y2 - content_box.y1;
visible_region = cairo_region_create_rectangle (&content_area);
cairo_region_intersect (visible_region, priv->visible_region);
clip_region = cairo_region_create_rectangle (&content_area);
cairo_region_intersect (clip_region, priv->clip_region);
return visible_region;
return clip_region;
}

View File

@@ -6,6 +6,6 @@
#include <meta/screen.h>
#include <meta/meta-background-group.h>
void meta_background_group_set_visible_region (MetaBackgroundGroup *self,
cairo_region_t *visible_region);
void meta_background_group_set_clip_region (MetaBackgroundGroup *self,
cairo_region_t *visible_region);
#endif /* META_BACKGROUND_GROUP_PRIVATE_H */

View File

@@ -62,16 +62,16 @@ meta_background_group_init (MetaBackgroundGroup *self)
}
/**
* meta_background_group_set_visible_region:
* meta_background_group_set_clip_region:
* @self: a #MetaBackgroundGroup
* @visible_region: (allow-none): the parts of the background to paint
* @region: (allow-none): the parts of the background to paint
*
* Sets the area of the backgrounds that is unobscured by overlapping windows.
* This is used to optimize and only paint the visible portions.
*/
void
meta_background_group_set_visible_region (MetaBackgroundGroup *self,
cairo_region_t *region)
meta_background_group_set_clip_region (MetaBackgroundGroup *self,
cairo_region_t *region)
{
GList *children, *l;
@@ -82,7 +82,7 @@ meta_background_group_set_visible_region (MetaBackgroundGroup *self,
if (META_IS_BACKGROUND_ACTOR (actor))
{
meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (actor), region);
meta_background_actor_set_clip_region (META_BACKGROUND_ACTOR (actor), region);
}
else if (META_IS_BACKGROUND_GROUP (actor))
{
@@ -92,7 +92,7 @@ meta_background_group_set_visible_region (MetaBackgroundGroup *self,
continue;
cairo_region_translate (region, -x, -y);
meta_background_group_set_visible_region (META_BACKGROUND_GROUP (actor), region);
meta_background_group_set_clip_region (META_BACKGROUND_GROUP (actor), region);
cairo_region_translate (region, x, y);
}
}

View File

@@ -412,13 +412,13 @@ meta_background_paint_content (ClutterContent *content,
*/
if (META_IS_BACKGROUND_ACTOR (actor))
{
cairo_region_t *visible_region;
visible_region = meta_background_actor_get_visible_region (META_BACKGROUND_ACTOR (actor));
cairo_region_t *clip_region;
clip_region = meta_background_actor_get_clip_region (META_BACKGROUND_ACTOR (actor));
if (visible_region != NULL)
if (clip_region != NULL)
{
cairo_region_intersect (paintable_region, visible_region);
cairo_region_destroy (visible_region);
cairo_region_intersect (paintable_region, clip_region);
cairo_region_destroy (clip_region);
}
}

View File

@@ -192,10 +192,7 @@ meta_module_class_init (MetaModuleClass *klass)
static void
meta_module_init (MetaModule *self)
{
MetaModulePrivate *priv;
self->priv = priv = META_MODULE_GET_PRIVATE (self);
self->priv = META_MODULE_GET_PRIVATE (self);
}
GType

View File

@@ -138,9 +138,7 @@ meta_plugin_class_init (MetaPluginClass *klass)
static void
meta_plugin_init (MetaPlugin *self)
{
MetaPluginPrivate *priv;
self->priv = priv = META_PLUGIN_GET_PRIVATE (self);
self->priv = META_PLUGIN_GET_PRIVATE (self);
}
gboolean

View File

@@ -91,11 +91,10 @@ struct _MetaShapedTexturePrivate
CoglTexture *texture;
CoglTexture *mask_texture;
CoglPipeline *pipeline;
CoglPipeline *pipeline_unshaped;
cairo_region_t *clip_region;
cairo_region_t *input_shape_region;
cairo_region_t *opaque_region;
guint tex_width, tex_height;
@@ -145,8 +144,6 @@ meta_shaped_texture_dispose (GObject *object)
meta_texture_tower_free (priv->paint_tower);
priv->paint_tower = NULL;
g_clear_pointer (&priv->pipeline, cogl_object_unref);
g_clear_pointer (&priv->pipeline_unshaped, cogl_object_unref);
g_clear_pointer (&priv->texture, cogl_object_unref);
meta_shaped_texture_set_mask_texture (self, NULL);
@@ -155,6 +152,76 @@ meta_shaped_texture_dispose (GObject *object)
G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
}
static CoglPipeline *
get_unmasked_pipeline (CoglContext *ctx)
{
return cogl_pipeline_new (ctx);
}
static CoglPipeline *
get_masked_pipeline (CoglContext *ctx)
{
static CoglPipeline *template = NULL;
if (G_UNLIKELY (template == NULL))
{
template = cogl_pipeline_new (ctx);
cogl_pipeline_set_layer_combine (template, 1,
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
}
return cogl_pipeline_copy (template);
}
static CoglPipeline *
get_unblended_pipeline (CoglContext *ctx)
{
static CoglPipeline *template = NULL;
if (G_UNLIKELY (template == NULL))
{
CoglColor color;
template = cogl_pipeline_new (ctx);
cogl_color_init_from_4ub (&color, 255, 255, 255, 255);
cogl_pipeline_set_blend (template,
"RGBA = ADD (SRC_COLOR, 0)",
NULL);
cogl_pipeline_set_color (template, &color);
}
return cogl_pipeline_copy (template);
}
static void
paint_clipped_rectangle (CoglFramebuffer *fb,
CoglPipeline *pipeline,
cairo_rectangle_int_t *rect,
ClutterActorBox *alloc)
{
float coords[8];
float x1, y1, x2, y2;
x1 = rect->x;
y1 = rect->y;
x2 = rect->x + rect->width;
y2 = rect->y + rect->height;
coords[0] = rect->x / (alloc->x2 - alloc->x1);
coords[1] = rect->y / (alloc->y2 - alloc->y1);
coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1);
coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1);
coords[4] = coords[0];
coords[5] = coords[1];
coords[6] = coords[2];
coords[7] = coords[3];
cogl_framebuffer_draw_multitextured_rectangle (fb, pipeline,
x1, y1, x2, y2,
&coords[0], 8);
}
static void
set_cogl_texture (MetaShapedTexture *stex,
CoglTexture *cogl_tex)
@@ -171,12 +238,6 @@ set_cogl_texture (MetaShapedTexture *stex,
priv->texture = cogl_tex;
if (priv->pipeline != NULL)
cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex));
if (priv->pipeline_unshaped != NULL)
cogl_pipeline_set_layer_texture (priv->pipeline_unshaped, 0, COGL_TEXTURE (cogl_tex));
if (cogl_tex != NULL)
{
width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
@@ -210,14 +271,14 @@ meta_shaped_texture_paint (ClutterActor *actor)
{
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
MetaShapedTexturePrivate *priv = stex->priv;
CoglTexture *paint_tex;
guint tex_width, tex_height;
guchar opacity;
CoglContext *ctx;
CoglFramebuffer *fb;
CoglPipeline *pipeline = NULL;
CoglTexture *paint_tex;
ClutterActorBox alloc;
static CoglPipeline *pipeline_template = NULL;
static CoglPipeline *pipeline_unshaped_template = NULL;
CoglPipeline *pipeline;
cairo_region_t *blended_region = NULL;
if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
return;
@@ -254,38 +315,74 @@ meta_shaped_texture_paint (ClutterActor *actor)
if (tex_width == 0 || tex_height == 0) /* no contents yet */
return;
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
fb = cogl_get_draw_framebuffer ();
opacity = clutter_actor_get_paint_opacity (actor);
clutter_actor_get_allocation_box (actor, &alloc);
if (priv->opaque_region != NULL && opacity == 255)
{
CoglPipeline *opaque_pipeline;
cairo_region_t *region;
int n_rects;
int i;
if (priv->clip_region != NULL)
{
region = cairo_region_copy (priv->clip_region);
cairo_region_intersect (region, priv->opaque_region);
}
else
{
region = cairo_region_reference (priv->opaque_region);
}
if (cairo_region_is_empty (region))
goto paint_blended;
opaque_pipeline = get_unblended_pipeline (ctx);
cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
n_rects = cairo_region_num_rectangles (region);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc);
}
cogl_object_unref (opaque_pipeline);
if (priv->clip_region != NULL)
{
blended_region = cairo_region_copy (priv->clip_region);
}
else
{
cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height };
blended_region = cairo_region_create_rectangle (&rect);
}
cairo_region_subtract (blended_region, priv->opaque_region);
paint_blended:
cairo_region_destroy (region);
}
if (blended_region == NULL && priv->clip_region != NULL)
blended_region = cairo_region_reference (priv->clip_region);
if (blended_region != NULL && cairo_region_is_empty (blended_region))
goto out;
if (priv->mask_texture == NULL)
{
/* Use a single-layer texture if we don't have a mask. */
if (priv->pipeline_unshaped == NULL)
{
if (G_UNLIKELY (pipeline_unshaped_template == NULL))
{
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
pipeline_unshaped_template = cogl_pipeline_new (ctx);
}
priv->pipeline_unshaped = cogl_pipeline_copy (pipeline_unshaped_template);
}
pipeline = priv->pipeline_unshaped;
pipeline = get_unmasked_pipeline (ctx);
}
else
{
if (priv->pipeline == NULL)
{
if (G_UNLIKELY (pipeline_template == NULL))
{
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
pipeline_template = cogl_pipeline_new (ctx);
cogl_pipeline_set_layer_combine (pipeline_template, 1,
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
}
priv->pipeline = cogl_pipeline_copy (pipeline_template);
}
pipeline = priv->pipeline;
pipeline = get_masked_pipeline (ctx);
cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture);
}
@@ -293,66 +390,50 @@ meta_shaped_texture_paint (ClutterActor *actor)
{
CoglColor color;
guchar opacity = clutter_actor_get_paint_opacity (actor);
cogl_color_set_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_pipeline_set_color (pipeline, &color);
}
cogl_set_source (pipeline);
clutter_actor_get_allocation_box (actor, &alloc);
if (priv->clip_region)
if (blended_region != NULL)
{
int n_rects;
int i;
cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
/* Limit to how many separate rectangles we'll draw; beyond this just
* fall back and draw the whole thing */
# define MAX_RECTS 16
n_rects = cairo_region_num_rectangles (priv->clip_region);
n_rects = cairo_region_num_rectangles (blended_region);
if (n_rects <= MAX_RECTS)
{
float coords[8];
float x1, y1, x2, y2;
int i;
cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (priv->clip_region, i, &rect);
cairo_region_get_rectangle (blended_region, i, &rect);
if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
continue;
x1 = rect.x;
y1 = rect.y;
x2 = rect.x + rect.width;
y2 = rect.y + rect.height;
coords[0] = rect.x / (alloc.x2 - alloc.x1);
coords[1] = rect.y / (alloc.y2 - alloc.y1);
coords[2] = (rect.x + rect.width) / (alloc.x2 - alloc.x1);
coords[3] = (rect.y + rect.height) / (alloc.y2 - alloc.y1);
coords[4] = coords[0];
coords[5] = coords[1];
coords[6] = coords[2];
coords[7] = coords[3];
cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2,
&coords[0], 8);
paint_clipped_rectangle (fb, pipeline, &rect, &alloc);
}
return;
goto out;
}
}
cogl_rectangle (0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
cogl_framebuffer_draw_rectangle (fb, pipeline,
0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
out:
if (pipeline != NULL)
cogl_object_unref (pipeline);
if (blended_region != NULL)
cairo_region_destroy (blended_region);
}
static void
@@ -368,13 +449,16 @@ meta_shaped_texture_pick (ClutterActor *actor,
/* If there is no region then use the regular pick */
if (priv->input_shape_region == NULL)
CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)
->pick (actor, color);
CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)->pick (actor, color);
else
{
int n_rects;
float *rectangles;
int i;
CoglPipeline *pipeline;
CoglContext *ctx;
CoglFramebuffer *fb;
CoglColor cogl_color;
/* Note: We don't bother trying to intersect the pick and clip regions
* since needing to copy the region, do the intersection, and probably
@@ -403,12 +487,17 @@ meta_shaped_texture_pick (ClutterActor *actor,
rectangles[pos + 3] = rect.y + rect.height;
}
cogl_set_source_color4ub (color->red,
color->green,
color->blue,
color->alpha);
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
fb = cogl_get_draw_framebuffer ();
cogl_rectangles (rectangles, n_rects);
cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
pipeline = cogl_pipeline_new (ctx);
cogl_pipeline_set_color (pipeline, &cogl_color);
cogl_framebuffer_draw_rectangles (fb, pipeline,
rectangles, n_rects);
cogl_object_unref (pipeline);
}
}
@@ -602,18 +691,18 @@ wayland_surface_update_area (MetaShapedTexture *stex,
}
static void
queue_damage_redraw_with_clip (MetaShapedTexture *stex,
int x,
int y,
int width,
int height)
get_clip (MetaShapedTexture *stex,
int x,
int y,
int width,
int height,
cairo_rectangle_int_t *clip)
{
ClutterActor *self = CLUTTER_ACTOR (stex);
MetaShapedTexturePrivate *priv;
ClutterActorBox allocation;
float scale_x;
float scale_y;
cairo_rectangle_int_t clip;
/* NB: clutter_actor_queue_redraw_with_clip expects a box in the actor's
* coordinate space so we need to convert from surface coordinates to
@@ -642,26 +731,45 @@ queue_damage_redraw_with_clip (MetaShapedTexture *stex,
scale_x = (allocation.x2 - allocation.x1) / priv->tex_width;
scale_y = (allocation.y2 - allocation.y1) / priv->tex_height;
clip.x = x * scale_x;
clip.y = y * scale_y;
clip.width = width * scale_x;
clip.height = height * scale_y;
clutter_actor_queue_redraw_with_clip (self, &clip);
clip->x = x * scale_x;
clip->y = y * scale_y;
clip->width = width * scale_x;
clip->height = height * scale_y;
}
void
/**
* meta_shaped_texture_update_area:
* @stex: #MetaShapedTexture
* @x: the x coordinate of the damaged area
* @y: the y coordinate of the damaged area
* @width: the width of the damaged area
* @height: the height of the damaged area
* @unobscured_region: The unobscured region of the window or %NULL if
* there is no valid one (like when the actor is transformed or
* has a mapped clone)
*
* Repairs the damaged area indicated by @x, @y, @width and @height
* and queues a redraw for the intersection @visibible_region and
* the damage area. If @visibible_region is %NULL a redraw will always
* get queued.
*
* Return value: Whether a redraw have been queued or not
*/
gboolean
meta_shaped_texture_update_area (MetaShapedTexture *stex,
int x,
int y,
int width,
int height)
int x,
int y,
int width,
int height,
cairo_region_t *unobscured_region)
{
MetaShapedTexturePrivate *priv;
cairo_rectangle_int_t clip;
priv = stex->priv;
if (priv->texture == NULL)
return;
return FALSE;
switch (priv->type)
{
@@ -676,7 +784,36 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex,
meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
queue_damage_redraw_with_clip (stex, x, y, width, height);
get_clip (stex, x, y, width, height, &clip);
if (unobscured_region)
{
cairo_region_t *intersection;
if (cairo_region_is_empty (unobscured_region))
return FALSE;
intersection = cairo_region_copy (unobscured_region);
cairo_region_intersect_rectangle (intersection, &clip);
if (!cairo_region_is_empty (intersection))
{
cairo_rectangle_int_t damage_rect;
cairo_region_get_extents (intersection, &damage_rect);
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &damage_rect);
cairo_region_destroy (intersection);
return TRUE;
}
cairo_region_destroy (intersection);
return FALSE;
}
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
return TRUE;
}
/**
@@ -812,8 +949,8 @@ meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex,
/**
* meta_shaped_texture_set_clip_region:
* @stex: a #MetaShapedTexture
* @clip_region: (transfer full): the region of the texture that
* is visible and should be painted.
* @clip_region: the region of the texture that is visible and
* should be painted.
*
* Provides a hint to the texture about what areas of the texture
* are not completely obscured and thus need to be painted. This
@@ -834,10 +971,7 @@ meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
priv = stex->priv;
if (priv->clip_region)
{
cairo_region_destroy (priv->clip_region);
priv->clip_region = NULL;
}
cairo_region_destroy (priv->clip_region);
if (clip_region)
priv->clip_region = cairo_region_copy (clip_region);
@@ -845,6 +979,36 @@ meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
priv->clip_region = NULL;
}
/**
* meta_shaped_texture_set_opaque_region:
* @stex: a #MetaShapedTexture
* @opaque_region: (transfer full): the region of the texture that
* can have blending turned off.
*
* As most windows have a large portion that does not require blending,
* we can easily turn off blending if we know the areas that do not
* require blending. This sets the region where we will not blend for
* optimization purposes.
*/
void
meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
cairo_region_t *opaque_region)
{
MetaShapedTexturePrivate *priv;
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
priv = stex->priv;
if (priv->opaque_region)
cairo_region_destroy (priv->opaque_region);
if (opaque_region)
priv->opaque_region = cairo_region_reference (opaque_region);
else
priv->opaque_region = NULL;
}
/**
* meta_shaped_texture_get_image:
* @stex: A #MetaShapedTexture

View File

@@ -70,11 +70,14 @@ void meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
cairo_region_t *meta_window_actor_get_obscured_region (MetaWindowActor *self);
void meta_window_actor_set_visible_region (MetaWindowActor *self,
cairo_region_t *visible_region);
void meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
cairo_region_t *beneath_region);
void meta_window_actor_reset_visible_regions (MetaWindowActor *self);
void meta_window_actor_set_clip_region (MetaWindowActor *self,
cairo_region_t *clip_region);
void meta_window_actor_set_clip_region_beneath (MetaWindowActor *self,
cairo_region_t *beneath_region);
void meta_window_actor_reset_clip_regions (MetaWindowActor *self);
void meta_window_actor_set_unobscured_region (MetaWindowActor *self,
cairo_region_t *unobscured_region);
void meta_window_actor_effect_completed (MetaWindowActor *actor,
gulong event);

View File

@@ -10,7 +10,6 @@
#include <math.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xrender.h>
@@ -34,6 +33,7 @@
#include "meta-texture-rectangle.h"
#include "region-utils.h"
#include "meta-wayland-private.h"
#include "monitor-private.h"
enum {
POSITION_CHANGED,
@@ -71,13 +71,19 @@ struct _MetaWindowActorPrivate
/* A region that matches the shape of the window, including frame bounds */
cairo_region_t *shape_region;
/* If the window has an input shape, a region that matches the shape */
cairo_region_t *input_shape_region;
cairo_region_t *input_region;
/* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with
* the shape region. */
cairo_region_t *opaque_region;
/* The region we should clip to when painting the shadow */
cairo_region_t *shadow_clip;
/* The region that is visible, used to optimize out redraws */
cairo_region_t *unobscured_region;
guint send_frame_messages_timer;
gint64 frame_drawn_time;
/* Extracted size-invariant shape used for shadows */
MetaWindowShape *shadow_shape;
@@ -187,6 +193,12 @@ static void meta_window_actor_handle_updates (MetaWindowActor *self);
static void check_needs_reshape (MetaWindowActor *self);
static void do_send_frame_drawn (MetaWindowActor *self, FrameData *frame);
static void do_send_frame_timings (MetaWindowActor *self,
FrameData *frame,
gint refresh_interval,
gint64 presentation_time);
G_DEFINE_TYPE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR);
static void
@@ -357,16 +369,25 @@ meta_window_actor_constructed (GObject *object)
Window xwindow = priv->xwindow;
MetaWindow *window = priv->window;
Display *xdisplay = meta_display_get_xdisplay (display);
XRenderPictFormat *format;
if (!meta_is_wayland_compositor ())
priv->damage = XDamageCreate (xdisplay, xwindow,
XDamageReportBoundingBox);
format = XRenderFindVisualFormat (xdisplay, window->xvisual);
if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
{
XRenderPictFormat *format;
if (format && format->type == PictTypeDirect && format->direct.alphaMask)
priv->argb32 = TRUE;
format = XRenderFindVisualFormat (xdisplay, window->xvisual);
if (format && format->type == PictTypeDirect && format->direct.alphaMask)
priv->argb32 = TRUE;
}
else
{
/* XXX: parse shm formats to determine argb32 */
priv->argb32 = TRUE;
}
if (!priv->actor)
{
@@ -407,8 +428,8 @@ meta_window_actor_constructed (GObject *object)
/* Start off with empty regions to maintain the invariant that
these regions are always set */
priv->shape_region = cairo_region_create();
priv->input_shape_region = cairo_region_create();
priv->shape_region = cairo_region_create ();
priv->input_region = cairo_region_create ();
}
static void
@@ -430,15 +451,17 @@ meta_window_actor_dispose (GObject *object)
info = meta_screen_get_compositor_data (screen);
if (!meta_is_wayland_compositor ())
{
display = meta_screen_get_display (screen);
xdisplay = meta_display_get_xdisplay (display);
meta_window_actor_detach_x11_pixmap (self);
meta_window_actor_detach_x11_pixmap (self);
if (priv->send_frame_messages_timer != 0)
{
g_source_remove (priv->send_frame_messages_timer);
priv->send_frame_messages_timer = 0;
}
g_clear_pointer (&priv->unobscured_region, cairo_region_destroy);
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->input_shape_region, cairo_region_destroy);
g_clear_pointer (&priv->input_region, cairo_region_destroy);
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
@@ -449,6 +472,9 @@ meta_window_actor_dispose (GObject *object)
if (!meta_is_wayland_compositor () && priv->damage != None)
{
display = meta_screen_get_display (screen);
xdisplay = meta_display_get_xdisplay (display);
meta_error_trap_push (display);
XDamageDestroy (xdisplay, priv->damage);
meta_error_trap_pop (display);
@@ -661,6 +687,16 @@ meta_window_actor_paint (ClutterActor *actor)
gboolean appears_focused = meta_window_appears_focused (priv->window);
MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
/* This window got damage when obscured; we set up a timer
* to send frame completion events, but since we're drawing
* the window now (for some other reason) cancel the timer
* and send the completion events normally */
if (priv->send_frame_messages_timer != 0)
{
g_source_remove (priv->send_frame_messages_timer);
priv->send_frame_messages_timer = 0;
}
if (shadow != NULL)
{
MetaShadowParams params;
@@ -731,6 +767,9 @@ meta_window_actor_get_paint_volume (ClutterActor *actor,
gdk_rectangle_union (&bounds, &shadow_bounds, &bounds);
}
if (priv->unobscured_region)
cairo_region_intersect_rectangle (priv->unobscured_region, &bounds);
origin.x = bounds.x;
origin.y = bounds.y;
origin.z = 0.0f;
@@ -923,11 +962,66 @@ meta_window_actor_freeze (MetaWindowActor *self)
self->priv->freeze_count++;
}
static
gboolean send_frame_messages_timeout (gpointer data)
{
MetaWindowActor *self = (MetaWindowActor *) data;
MetaWindowActorPrivate *priv = self->priv;
FrameData *frame = g_slice_new0 (FrameData);
frame->sync_request_serial = priv->window->sync_request_serial;
do_send_frame_drawn (self, frame);
do_send_frame_timings (self, frame, 0, 0);
priv->needs_frame_drawn = FALSE;
priv->send_frame_messages_timer = 0;
frame_data_free (frame);
return FALSE;
}
static void
queue_send_frame_messages_timeout (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
gint64 current_time = meta_compositor_monotonic_time_to_server_time (display, g_get_monotonic_time ());
MetaMonitorManager *monitor_manager = meta_monitor_manager_get ();
MetaWindow *window = priv->window;
MetaOutput *outputs;
guint n_outputs, i;
float refresh_rate = 60.0f;
gint interval, offset;
outputs = meta_monitor_manager_get_outputs (monitor_manager, &n_outputs);
for (i = 0; i < n_outputs; i++)
{
if (outputs[i].output_id == window->monitor->output_id && outputs[i].crtc)
{
refresh_rate = outputs[i].crtc->current_mode->refresh_rate;
break;
}
}
interval = (int)(1000000 / refresh_rate) * 6;
offset = MAX (0, current_time - priv->frame_drawn_time + interval) / 1000;
/* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
* so the timer will run *after* the clutter frame handling, if a frame is ready
* to be drawn when the timer expires.
*/
priv->send_frame_messages_timer = g_timeout_add_full (META_PRIORITY_REDRAW, offset, send_frame_messages_timeout, self, NULL);
}
static void
meta_window_actor_damage_all (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
CoglTexture *texture;
gboolean redraw_queued;
if (!priv->needs_damage_all)
return;
@@ -937,13 +1031,16 @@ meta_window_actor_damage_all (MetaWindowActor *self)
if (!priv->mapped || priv->needs_pixmap)
return;
meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
0, 0,
cogl_texture_get_width (texture),
cogl_texture_get_height (texture));
redraw_queued = meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
0, 0,
cogl_texture_get_width (texture),
cogl_texture_get_height (texture),
clutter_actor_has_mapped_clones (priv->actor) ?
NULL : priv->unobscured_region);
priv->repaint_scheduled = priv->repaint_scheduled || redraw_queued;
priv->needs_damage_all = FALSE;
priv->repaint_scheduled = TRUE;
}
static void
@@ -999,14 +1096,31 @@ meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
if (!priv->repaint_scheduled)
{
gboolean is_obscured = FALSE;
/* Find out whether the window is completly obscured */
if (priv->unobscured_region)
{
cairo_region_t *unobscured_window_region;
unobscured_window_region = cairo_region_copy (priv->shape_region);
cairo_region_intersect (unobscured_window_region, priv->unobscured_region);
is_obscured = cairo_region_is_empty (unobscured_window_region);
cairo_region_destroy (unobscured_window_region);
}
/* A frame was marked by the client without actually doing any
* damage, or while we had the window frozen (e.g. during an
* interactive resize.) We need to make sure that the
* damage or any unobscured, or while we had the window frozen
* (e.g. during an interactive resize.) We need to make sure that the
* pre_paint/post_paint functions get called, enabling us to
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
* consistent timing with non-empty frames.
* consistent timing with non-empty frames. If the window
* is completely obscured we fire off the send_frame_messages timeout.
*/
if (priv->mapped && (!meta_is_wayland_compositor () || !priv->needs_pixmap))
if (is_obscured)
{
queue_send_frame_messages_timeout (self);
}
else if (priv->mapped && (!meta_is_wayland_compositor () || !priv->needs_pixmap))
{
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
@@ -1260,7 +1374,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self)
if (priv->opacity != 0xff)
return FALSE;
if (metaWindow->has_shape)
if (metaWindow->shape_region != NULL)
return FALSE;
if (priv->argb32 && !meta_window_requested_bypass_compositor (metaWindow))
@@ -1726,40 +1840,67 @@ see_region (cairo_region_t *region,
#endif
/**
* meta_window_actor_set_visible_region:
* meta_window_actor_set_unobscured_region:
* @self: a #MetaWindowActor
* @visible_region: the region of the screen that isn't completely
* @unobscured_region: the region of the screen that isn't completely
* obscured.
*
* Provides a hint as to what areas of the window need to queue
* redraws when damaged. Regions not in @unobscured_region are completely obscured.
* Unlike meta_window_actor_set_clip_region(), the region here
* doesn't take into account any clipping that is in effect while drawing.
*/
void
meta_window_actor_set_unobscured_region (MetaWindowActor *self,
cairo_region_t *unobscured_region)
{
MetaWindowActorPrivate *priv = self->priv;
if (priv->unobscured_region)
cairo_region_destroy (priv->unobscured_region);
if (unobscured_region)
priv->unobscured_region = cairo_region_copy (unobscured_region);
else
priv->unobscured_region = NULL;
}
/**
* meta_window_actor_set_clip_region:
* @self: a #MetaWindowActor
* @clip_region: the region of the screen that isn't completely
* obscured.
*
* Provides a hint as to what areas of the window need to be
* drawn. Regions not in @visible_region are completely obscured.
* drawn. Regions not in @clip_region are completely obscured or
* not drawn in this frame.
* This will be set before painting then unset afterwards.
*/
void
meta_window_actor_set_visible_region (MetaWindowActor *self,
cairo_region_t *visible_region)
meta_window_actor_set_clip_region (MetaWindowActor *self,
cairo_region_t *clip_region)
{
MetaWindowActorPrivate *priv = self->priv;
meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor),
visible_region);
clip_region);
}
/**
* meta_window_actor_set_visible_region_beneath:
* meta_window_actor_set_clip_region_beneath:
* @self: a #MetaWindowActor
* @visible_region: the region of the screen that isn't completely
* @clip_region: the region of the screen that isn't completely
* obscured beneath the main window texture.
*
* Provides a hint as to what areas need to be drawn *beneath*
* the main window texture. This is the relevant visible region
* the main window texture. This is the relevant clip region
* when drawing the shadow, properly accounting for areas of the
* shadow hid by the window itself. This will be set before painting
* then unset afterwards.
*/
void
meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
cairo_region_t *beneath_region)
meta_window_actor_set_clip_region_beneath (MetaWindowActor *self,
cairo_region_t *beneath_region)
{
MetaWindowActorPrivate *priv = self->priv;
gboolean appears_focused = meta_window_appears_focused (priv->window);
@@ -1778,14 +1919,14 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
}
/**
* meta_window_actor_reset_visible_regions:
* meta_window_actor_reset_clip_regions:
* @self: a #MetaWindowActor
*
* Unsets the regions set by meta_window_actor_set_visible_region() and
* meta_window_actor_set_visible_region_beneath()
* Unsets the regions set by meta_window_actor_set_clip_region() and
* meta_window_actor_set_clip_region_beneath()
*/
void
meta_window_actor_reset_visible_regions (MetaWindowActor *self)
meta_window_actor_reset_clip_regions (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
@@ -1960,6 +2101,7 @@ meta_window_actor_process_x11_damage (MetaWindowActor *self,
{
MetaWindowActorPrivate *priv = self->priv;
MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen);
gboolean redraw_queued;
priv->received_x11_damage = TRUE;
@@ -2007,12 +2149,16 @@ meta_window_actor_process_x11_damage (MetaWindowActor *self,
if (!priv->mapped || priv->needs_pixmap)
return;
meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
event->area.x,
event->area.y,
event->area.width,
event->area.height);
priv->repaint_scheduled = TRUE;
redraw_queued = meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
event->area.x,
event->area.y,
event->area.width,
event->area.height,
clutter_actor_has_mapped_clones (priv->actor) ?
NULL : priv->unobscured_region);
priv->repaint_scheduled = priv->repaint_scheduled || redraw_queued;
}
void
@@ -2023,13 +2169,17 @@ meta_window_actor_process_wayland_damage (MetaWindowActor *self,
int height)
{
MetaWindowActorPrivate *priv = self->priv;
gboolean redraw_queued;
if (!priv->mapped)
return;
meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
x, y, width, height);
priv->repaint_scheduled = TRUE;
redraw_queued = meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
x, y, width, height,
clutter_actor_has_mapped_clones (priv->actor) ?
NULL : priv->unobscured_region);
priv->repaint_scheduled = priv->repaint_scheduled || redraw_queued;
}
void
@@ -2171,85 +2321,21 @@ build_and_scan_frame_mask (MetaWindowActor *self,
g_free (mask_data);
}
static cairo_region_t *
region_create_from_x_rectangles (const XRectangle *rects,
int n_rects,
int dx,
int dy)
{
int i;
cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i ++)
{
cairo_rects[i].x = rects[i].x + dx;
cairo_rects[i].y = rects[i].y + dy;
cairo_rects[i].width = rects[i].width;
cairo_rects[i].height = rects[i].height;
}
return cairo_region_create_rectangles (cairo_rects, n_rects);
}
static void
meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
meta_window_actor_update_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
cairo_region_t *region = NULL;
gboolean needs_mask;
if (priv->shadow_shape != NULL)
if (priv->window->frame != NULL && priv->window->shape_region != NULL)
{
meta_window_shape_unref (priv->shadow_shape);
priv->shadow_shape = NULL;
region = cairo_region_copy (priv->window->shape_region);
cairo_region_translate (region, client_area->x, client_area->y);
}
meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
#ifdef HAVE_SHAPE
if (priv->window->has_shape)
else if (priv->window->shape_region != NULL)
{
/* Translate the set of XShape rectangles that we
* get from the X server to a cairo_region. */
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
XRectangle *rects;
int n_rects, ordering;
meta_error_trap_push (display);
rects = XShapeGetRectangles (xdisplay,
priv->window->xwindow,
ShapeBounding,
&n_rects,
&ordering);
meta_error_trap_pop (display);
if (rects)
{
region = region_create_from_x_rectangles (rects, n_rects,
client_area->x,
client_area->y);
XFree (rects);
}
}
#endif
needs_mask = (region != NULL) || (priv->window->frame != NULL);
if (region != NULL)
{
/* The shape we get back from the client may have coordinates
* outside of the frame. The X SHAPE Extension requires that
* the overall shape the client provides never exceeds the
* "bounding rectangle" of the window -- the shape that the
* window would have gotten if it was unshaped. In our case,
* this is simply the client area.
*/
cairo_region_intersect_rectangle (region, client_area);
region = cairo_region_reference (priv->window->shape_region);
}
else
{
@@ -2259,11 +2345,67 @@ meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
region = cairo_region_create_rectangle (client_area);
}
/* The region at this point should be constrained to the
* bounds of the client rectangle. */
meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
if ((priv->window->shape_region != NULL) || (priv->window->frame != NULL))
build_and_scan_frame_mask (self, client_area, region);
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
priv->shape_region = region;
g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
meta_window_actor_invalidate_shadow (self);
}
static void
meta_window_actor_update_input_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
MetaShapedTexture *stex = META_SHAPED_TEXTURE (priv->actor);
cairo_region_t *region = NULL;
if (priv->window->frame != NULL && priv->window->input_region != NULL)
{
region = meta_frame_get_frame_bounds (priv->window->frame);
cairo_region_subtract_rectangle (region, client_area);
/* input_region is in client window coordinates, so translate the
* input region into that coordinate system and back */
cairo_region_translate (region, -client_area->x, -client_area->y);
cairo_region_union (region, priv->window->input_region);
cairo_region_translate (region, client_area->x, client_area->y);
}
else if (priv->window->shape_region != NULL)
{
region = cairo_region_reference (priv->window->input_region);
}
else
{
/* If we don't have a shape on the server, that means that
* we have an implicit shape of one rectangle covering the
* entire window. */
region = cairo_region_create_rectangle (client_area);
}
meta_shaped_texture_set_input_shape_region (stex, region);
cairo_region_destroy (region);
}
static void
meta_window_actor_update_opaque_region (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
if (priv->argb32 && priv->window->opaque_region != NULL)
{
MetaFrameBorders borders;
meta_frame_calc_borders (priv->window->frame, &borders);
/* The opaque region is defined to be a part of the
* window which ARGB32 will always paint with opaque
* pixels. For these regions, we want to avoid painting
@@ -2275,91 +2417,16 @@ meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
* case, graphical glitches will occur.
*/
priv->opaque_region = cairo_region_copy (priv->window->opaque_region);
cairo_region_translate (priv->opaque_region, client_area->x, client_area->y);
cairo_region_intersect (priv->opaque_region, region);
cairo_region_translate (priv->opaque_region, borders.total.left, borders.total.top);
cairo_region_intersect (priv->opaque_region, priv->shape_region);
}
else if (priv->argb32)
priv->opaque_region = NULL;
else
priv->opaque_region = cairo_region_reference (region);
priv->opaque_region = cairo_region_reference (priv->shape_region);
if (needs_mask)
{
/* This takes the region, generates a mask using GTK+
* and scans the mask looking for all opaque pixels,
* adding it to region.
*/
build_and_scan_frame_mask (self, client_area, region);
}
priv->shape_region = region;
meta_window_actor_invalidate_shadow (self);
}
static void
meta_window_actor_update_x11_input_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
cairo_region_t *region = NULL;
g_clear_pointer (&priv->input_shape_region, cairo_region_destroy);
#ifdef HAVE_SHAPE
/* Note: we currently assume that mutter never sets an input region
* when there is a frame. */
if (priv->window->frame == NULL && priv->window->has_input_shape)
{
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
XRectangle *rects;
int n_rects, ordering;
/* Note we only actually query the ShapeInput shape of a window
* when we don't have a frame because we assume currently that
* mutter never sets an ShapeInput shape on a frame. */
meta_error_trap_push (display);
rects = XShapeGetRectangles (xdisplay,
priv->window->xwindow,
ShapeInput,
&n_rects,
&ordering);
meta_error_trap_pop (display);
if (rects)
{
region = region_create_from_x_rectangles (rects, n_rects,
client_area->x,
client_area->y);
XFree (rects);
}
}
#endif /* HAVE_SHAPE */
if (region != NULL)
{
/* The X shape extension requires us to intersect the input
* region with the effective bounding shape to determine the
* effective input region.
*/
if (priv->shape_region)
cairo_region_intersect (region, priv->shape_region);
else
cairo_region_intersect_rectangle (region, client_area);
}
else
{
/* If we don't have a shape on the server, that means that we
* have an implicit shape of one rectangle covering the entire
* window. */
region = cairo_region_create_rectangle (client_area);
}
priv->input_shape_region = region;
meta_shaped_texture_set_input_shape_region (META_SHAPED_TEXTURE (priv->actor),
priv->input_shape_region);
meta_shaped_texture_set_opaque_region (META_SHAPED_TEXTURE (priv->actor),
priv->opaque_region);
}
static void
@@ -2385,24 +2452,9 @@ check_needs_reshape (MetaWindowActor *self)
else
client_area.height = priv->window->rect.height;
if (priv->window->client_type == META_WINDOW_CLIENT_TYPE_X11)
{
meta_window_actor_update_x11_shape_region (self, &client_area);
meta_window_actor_update_x11_input_shape_region (self, &client_area);
}
else
{
/* TODO: properly support setting an input region as specified
* via the wayland protocol */
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
g_clear_pointer (&priv->input_shape_region, cairo_region_destroy);
priv->shape_region = cairo_region_create_rectangle (&client_area);
priv->opaque_region = cairo_region_reference (priv->shape_region);
priv->input_shape_region = cairo_region_reference (priv->shape_region);
}
meta_window_actor_update_shape_region (self, &client_area);
meta_window_actor_update_input_region (self, &client_area);
meta_window_actor_update_opaque_region (self);
priv->needs_reshape = FALSE;
}
@@ -2560,6 +2612,35 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
}
}
static void
do_send_frame_drawn (MetaWindowActor *self, FrameData *frame)
{
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
XClientMessageEvent ev = { 0, };
frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
g_get_monotonic_time ());
priv->frame_drawn_time = frame->frame_drawn_time;
ev.type = ClientMessage;
ev.window = meta_window_get_xwindow (priv->window);
ev.message_type = display->atom__NET_WM_FRAME_DRAWN;
ev.format = 32;
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
ev.data.l[1] = frame->sync_request_serial >> 32;
ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT(0xffffffff);
ev.data.l[3] = frame->frame_drawn_time >> 32;
meta_error_trap_push (display);
XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
XFlush (xdisplay);
meta_error_trap_pop (display);
}
void
meta_window_actor_post_paint (MetaWindowActor *self)
{
@@ -2567,47 +2648,29 @@ meta_window_actor_post_paint (MetaWindowActor *self)
priv->repaint_scheduled = FALSE;
/* This window had damage, but wasn't actually redrawn because
* it is obscured. So we should wait until timer expiration
* before sending _NET_WM_FRAME_* messages.
*/
if (priv->send_frame_messages_timer != 0)
return;
if (priv->needs_frame_drawn)
{
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
XClientMessageEvent ev = { 0, };
FrameData *frame = priv->frames->data;
frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
g_get_monotonic_time ());
ev.type = ClientMessage;
ev.window = meta_window_get_xwindow (priv->window);
ev.message_type = display->atom__NET_WM_FRAME_DRAWN;
ev.format = 32;
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
ev.data.l[1] = frame->sync_request_serial >> 32;
ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT(0xffffffff);
ev.data.l[3] = frame->frame_drawn_time >> 32;
meta_error_trap_push (display);
XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
XFlush (xdisplay);
meta_error_trap_pop (display);
do_send_frame_drawn (self, priv->frames->data);
priv->needs_frame_drawn = FALSE;
}
}
static void
send_frame_timings (MetaWindowActor *self,
FrameData *frame,
CoglFrameInfo *frame_info,
gint64 presentation_time)
do_send_frame_timings (MetaWindowActor *self,
FrameData *frame,
gint refresh_interval,
gint64 presentation_time)
{
MetaWindowActorPrivate *priv = self->priv;
MetaDisplay *display = meta_screen_get_display (priv->screen);
Display *xdisplay = meta_display_get_xdisplay (display);
float refresh_rate;
int refresh_interval;
XClientMessageEvent ev = { 0, };
@@ -2618,13 +2681,6 @@ send_frame_timings (MetaWindowActor *self,
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
ev.data.l[1] = frame->sync_request_serial >> 32;
refresh_rate = cogl_frame_info_get_refresh_rate (frame_info);
/* 0.0 is a flag for not known, but sanity-check against other odd numbers */
if (refresh_rate >= 1.0)
refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
else
refresh_interval = 0;
if (presentation_time != 0)
{
gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display,
@@ -2646,6 +2702,25 @@ send_frame_timings (MetaWindowActor *self,
meta_error_trap_pop (display);
}
static void
send_frame_timings (MetaWindowActor *self,
FrameData *frame,
CoglFrameInfo *frame_info,
gint64 presentation_time)
{
float refresh_rate;
int refresh_interval;
refresh_rate = cogl_frame_info_get_refresh_rate (frame_info);
/* 0.0 is a flag for not known, but sanity-check against other odd numbers */
if (refresh_rate >= 1.0)
refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
else
refresh_interval = 0;
do_send_frame_timings (self, frame, refresh_interval, presentation_time);
}
void
meta_window_actor_frame_complete (MetaWindowActor *self,
CoglFrameInfo *frame_info,

View File

@@ -90,17 +90,30 @@ painting_untransformed (MetaWindowGroup *window_group,
static void
meta_window_group_paint (ClutterActor *actor)
{
cairo_region_t *visible_region;
ClutterActor *stage;
cairo_region_t *clip_region;
cairo_region_t *unobscured_region;
ClutterActorIter iter;
ClutterActor *child;
cairo_rectangle_int_t visible_rect;
cairo_rectangle_int_t visible_rect, clip_rect;
int paint_x_origin, paint_y_origin;
int actor_x_origin, actor_y_origin;
int paint_x_offset, paint_y_offset;
MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
MetaCompScreen *info;
MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen);
ClutterActor *stage = clutter_actor_get_stage (actor);
/* Start off by treating all windows as completely unobscured, so damage anywhere
* in a window queues redraws, but confine it more below. */
clutter_actor_iter_init (&iter, actor);
while (clutter_actor_iter_next (&iter, &child))
{
if (META_IS_WINDOW_ACTOR (child))
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (child);
meta_window_actor_set_unobscured_region (window_actor, NULL);
}
}
/* Normally we expect an actor to be drawn at it's position on the screen.
* However, if we're inside the paint of a ClutterClone, that won't be the
@@ -125,17 +138,22 @@ meta_window_group_paint (ClutterActor *actor)
paint_x_offset = paint_x_origin - actor_x_origin;
paint_y_offset = paint_y_origin - actor_y_origin;
visible_rect.x = visible_rect.y = 0;
visible_rect.width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
visible_rect.height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
unobscured_region = cairo_region_create_rectangle (&visible_rect);
/* Get the clipped redraw bounds from Clutter so that we can avoid
* painting shadows on windows that don't need to be painted in this
* frame. In the case of a multihead setup with mismatched monitor
* sizes, we could intersect this with an accurate union of the
* monitors to avoid painting shadows that are visible only in the
* holes. */
stage = clutter_actor_get_stage (actor);
clutter_stage_get_redraw_clip_bounds (CLUTTER_STAGE (stage),
&visible_rect);
&clip_rect);
visible_region = cairo_region_create_rectangle (&visible_rect);
clip_region = cairo_region_create_rectangle (&clip_rect);
if (!meta_is_wayland_compositor ())
{
@@ -146,7 +164,8 @@ meta_window_group_paint (ClutterActor *actor)
MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window);
meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect);
cairo_region_subtract_rectangle (visible_region, &unredirected_rect);
cairo_region_subtract_rectangle (unobscured_region, &unredirected_rect);
cairo_region_subtract_rectangle (clip_region, &unredirected_rect);
}
}
@@ -186,7 +205,6 @@ meta_window_group_paint (ClutterActor *actor)
if (META_IS_WINDOW_ACTOR (child))
{
MetaWindow *meta_window;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (child);
int x, y;
@@ -196,27 +214,28 @@ meta_window_group_paint (ClutterActor *actor)
x += paint_x_offset;
y += paint_y_offset;
/* Temporarily move to the coordinate system of the actor */
cairo_region_translate (visible_region, - x, - y);
cairo_region_translate (unobscured_region, - x, - y);
cairo_region_translate (clip_region, - x, - y);
meta_window_actor_set_visible_region (window_actor, visible_region);
meta_window_actor_set_unobscured_region (window_actor, unobscured_region);
meta_window_actor_set_clip_region (window_actor, clip_region);
/* TODO: Track the opaque regions of wayland clients.
* Although wayland clients can report opaque window
* regions, for now we assume that all wayland clients are
* transparent... */
meta_window = meta_window_actor_get_meta_window (window_actor);
if (meta_window->client_type != META_WINDOW_CLIENT_TYPE_WAYLAND &&
clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
{
cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
if (obscured_region)
cairo_region_subtract (visible_region, obscured_region);
{
cairo_region_subtract (unobscured_region, obscured_region);
cairo_region_subtract (clip_region, obscured_region);
}
}
meta_window_actor_set_visible_region_beneath (window_actor, visible_region);
cairo_region_translate (visible_region, x, y);
meta_window_actor_set_clip_region_beneath (window_actor, clip_region);
cairo_region_translate (unobscured_region, x, y);
cairo_region_translate (clip_region, x, y);
}
else if (META_IS_BACKGROUND_ACTOR (child) ||
META_IS_BACKGROUND_GROUP (child))
@@ -229,17 +248,18 @@ meta_window_group_paint (ClutterActor *actor)
x += paint_x_offset;
y += paint_y_offset;
cairo_region_translate (visible_region, - x, - y);
cairo_region_translate (clip_region, - x, - y);
if (META_IS_BACKGROUND_GROUP (child))
meta_background_group_set_visible_region (META_BACKGROUND_GROUP (child), visible_region);
meta_background_group_set_clip_region (META_BACKGROUND_GROUP (child), clip_region);
else
meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (child), visible_region);
cairo_region_translate (visible_region, x, y);
meta_background_actor_set_clip_region (META_BACKGROUND_ACTOR (child), clip_region);
cairo_region_translate (clip_region, x, y);
}
}
cairo_region_destroy (visible_region);
cairo_region_destroy (unobscured_region);
cairo_region_destroy (clip_region);
CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor);
@@ -252,12 +272,12 @@ meta_window_group_paint (ClutterActor *actor)
if (META_IS_WINDOW_ACTOR (child))
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (child);
meta_window_actor_reset_visible_regions (window_actor);
meta_window_actor_reset_clip_regions (window_actor);
}
else if (META_IS_BACKGROUND_ACTOR (child))
{
MetaBackgroundActor *background_actor = META_BACKGROUND_ACTOR (child);
meta_background_actor_set_visible_region (background_actor, NULL);
meta_background_actor_set_clip_region (background_actor, NULL);
}
}
}

View File

@@ -89,7 +89,7 @@ meta_core_get (Display *xdisplay,
if (request != META_CORE_WINDOW_HAS_FRAME &&
(window == NULL || window->frame == NULL)) {
meta_bug ("No such frame window 0x%lx!\n", xwindow);
return;
goto out;
}
while (request != META_CORE_GET_END) {
@@ -99,7 +99,7 @@ meta_core_get (Display *xdisplay,
switch (request) {
case META_CORE_WINDOW_HAS_FRAME:
*((gboolean*)answer) = window != NULL && window->frame != NULL;
if (!*((gboolean*)answer)) return; /* see above */
if (!*((gboolean*)answer)) goto out; /* see above */
break;
case META_CORE_GET_CLIENT_WIDTH:
*((gint*)answer) = window->rect.width;
@@ -160,6 +160,7 @@ meta_core_get (Display *xdisplay,
request = va_arg (args, MetaCoreGetType);
}
out:
va_end (args);
}

View File

@@ -1876,14 +1876,17 @@ get_input_event (MetaDisplay *display,
case XI_ButtonRelease:
if (((XIDeviceEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID)
return input_event;
break;
case XI_KeyPress:
case XI_KeyRelease:
if (((XIDeviceEvent *) input_event)->deviceid == META_VIRTUAL_CORE_KEYBOARD_ID)
return input_event;
break;
case XI_FocusIn:
case XI_FocusOut:
if (((XIEnterEvent *) input_event)->deviceid == META_VIRTUAL_CORE_KEYBOARD_ID)
return input_event;
break;
case XI_Enter:
case XI_Leave:
if (((XIEnterEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID)
@@ -2336,59 +2339,9 @@ meta_display_handle_event (MetaDisplay *display,
XShapeEvent *sev = (XShapeEvent*) event;
if (sev->kind == ShapeBounding)
{
if (sev->shaped && !window->has_shape)
{
window->has_shape = TRUE;
meta_topic (META_DEBUG_SHAPES,
"Window %s now has a shape\n",
window->desc);
}
else if (!sev->shaped && window->has_shape)
{
window->has_shape = FALSE;
meta_topic (META_DEBUG_SHAPES,
"Window %s no longer has a shape\n",
window->desc);
}
else
{
meta_topic (META_DEBUG_SHAPES,
"Window %s shape changed\n",
window->desc);
}
if (display->compositor)
meta_compositor_window_x11_shape_changed (display->compositor,
window);
}
meta_window_update_shape_region_x11 (window);
else if (sev->kind == ShapeInput)
{
if (sev->shaped && !window->has_input_shape)
{
window->has_input_shape = TRUE;
meta_topic (META_DEBUG_SHAPES,
"Window %s now has an input shape\n",
window->desc);
}
else if (!sev->shaped && window->has_input_shape)
{
window->has_input_shape = FALSE;
meta_topic (META_DEBUG_SHAPES,
"Window %s no longer has an input shape\n",
window->desc);
}
else
{
meta_topic (META_DEBUG_SHAPES,
"Window %s input shape changed\n",
window->desc);
}
if (display->compositor)
meta_compositor_window_x11_shape_changed (display->compositor,
window);
}
meta_window_update_input_region_x11 (window);
}
else
{

View File

@@ -53,8 +53,13 @@
#include <X11/XKBlib.h>
#endif
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
#define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings"
#define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings"
#define SCHEMA_MUTTER_WAYLAND_KEYBINDINGS "org.gnome.mutter.wayland.keybindings"
static gboolean add_builtin_keybinding (MetaDisplay *display,
const char *name,
@@ -4098,6 +4103,40 @@ handle_set_spew_mark (MetaDisplay *display,
meta_verbose ("-- MARK MARK MARK MARK --\n");
}
#ifdef HAVE_WAYLAND
static void
handle_switch_vt (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XIDeviceEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
gint vt = binding->handler->data;
MetaWaylandCompositor *compositor;
MetaLauncher *launcher;
compositor = meta_wayland_compositor_get_default ();
launcher = meta_wayland_compositor_get_launcher (compositor);
if (launcher)
{
GError *error;
error = NULL;
if (!meta_launcher_activate_vt (launcher, vt, &error))
{
g_warning ("Failed to switch VT: %s", error->message);
g_error_free (error);
}
}
else
{
g_debug ("Ignoring VT switch keybinding, not running as VT manager");
}
}
#endif
/**
* meta_keybindings_set_custom_handler:
* @name: The name of the keybinding to set
@@ -4161,6 +4200,7 @@ init_builtin_key_bindings (MetaDisplay *display)
META_KEY_BINDING_IS_REVERSED)
GSettings *common_keybindings = g_settings_new (SCHEMA_COMMON_KEYBINDINGS);
GSettings *mutter_keybindings = g_settings_new (SCHEMA_MUTTER_KEYBINDINGS);
GSettings *mutter_wayland_keybindings = g_settings_new (SCHEMA_MUTTER_WAYLAND_KEYBINDINGS);
add_builtin_keybinding (display,
"switch-to-workspace-1",
@@ -4422,6 +4462,60 @@ init_builtin_key_bindings (MetaDisplay *display)
META_KEYBINDING_ACTION_SET_SPEW_MARK,
handle_set_spew_mark, 0);
#ifdef HAVE_WAYLAND
if (meta_is_wayland_compositor ())
{
add_builtin_keybinding (display,
"switch-to-session-1",
mutter_wayland_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 1);
add_builtin_keybinding (display,
"switch-to-session-2",
mutter_wayland_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 2);
add_builtin_keybinding (display,
"switch-to-session-3",
mutter_wayland_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 3);
add_builtin_keybinding (display,
"switch-to-session-4",
mutter_wayland_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 4);
add_builtin_keybinding (display,
"switch-to-session-5",
mutter_wayland_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 5);
add_builtin_keybinding (display,
"switch-to-session-6",
mutter_wayland_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 6);
add_builtin_keybinding (display,
"switch-to-session-7",
mutter_wayland_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 7);
}
#endif
#undef REVERSES_AND_REVERSED
/************************ PER WINDOW BINDINGS ************************/

View File

@@ -58,6 +58,7 @@
#include "meta-wayland-private.h"
#include <glib-object.h>
#include <glib-unix.h>
#include <gdk/gdkx.h>
#include <stdlib.h>
@@ -352,59 +353,12 @@ meta_finalize (void)
meta_wayland_finalize ();
}
static int signal_pipe_fds[2] = { -1, -1 };
static void
signal_handler (int signum)
{
if (signal_pipe_fds[1] >= 0)
{
switch (signum)
{
case SIGTERM:
write (signal_pipe_fds[1], "T", 1);
break;
default:
break;
}
}
}
static gboolean
on_signal (GIOChannel *source,
GIOCondition condition,
void *data)
on_sigterm (gpointer user_data)
{
char signal;
int count;
meta_quit (EXIT_SUCCESS);
for (;;)
{
count = read (signal_pipe_fds[0], &signal, 1);
if (count == EINTR)
continue;
if (count < 0)
{
const char *msg = strerror (errno);
g_warning ("Error handling signal: %s", msg);
}
if (count != 1)
{
g_warning ("Unexpectedly failed to read byte from signal pipe\n");
return TRUE;
}
break;
}
switch (signal)
{
case 'T': /* SIGTERM */
meta_quit (META_EXIT_SUCCESS);
break;
default:
g_warning ("Spurious character '%c' read from signal pipe", signal);
}
return TRUE;
return G_SOURCE_REMOVE;
}
/**
@@ -418,7 +372,6 @@ meta_init (void)
{
struct sigaction act;
sigset_t empty_mask;
GIOChannel *channel;
sigemptyset (&empty_mask);
act.sa_handler = SIG_IGN;
@@ -433,20 +386,7 @@ meta_init (void)
g_strerror (errno));
#endif
if (pipe (signal_pipe_fds) != 0)
g_printerr ("Failed to create signal pipe: %s\n",
g_strerror (errno));
channel = g_io_channel_unix_new (signal_pipe_fds[0]);
g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_signal, NULL);
g_io_channel_set_close_on_unref (channel, TRUE);
g_io_channel_unref (channel);
act.sa_handler = &signal_handler;
if (sigaction (SIGTERM, &act, NULL) < 0)
g_printerr ("Failed to register SIGTERM handler: %s\n",
g_strerror (errno));
g_unix_signal_add (SIGTERM, on_sigterm, NULL);
if (g_getenv ("MUTTER_VERBOSE"))
meta_set_verbose (TRUE);

View File

@@ -230,7 +230,7 @@ meta_idle_monitor_handle_xevent_all (XEvent *xevent)
{
int i;
for (i = 0; i < device_id_max; i++)
for (i = 0; i <= device_id_max; i++)
if (device_monitors[i])
meta_idle_monitor_handle_xevent (device_monitors[i], (XSyncAlarmNotifyEvent*)xevent);
}

View File

@@ -55,6 +55,8 @@ typedef struct {
char *serial;
} MetaOutputKey;
/* Keep this structure packed, so that we
can use memcmp */
typedef struct {
gboolean enabled;
MetaRectangle rect;
@@ -147,6 +149,13 @@ output_key_equal (const MetaOutputKey *one,
strcmp (one->serial, two->serial) == 0;
}
static gboolean
output_config_equal (const MetaOutputConfig *one,
const MetaOutputConfig *two)
{
return memcmp (one, two, sizeof (MetaOutputConfig)) == 0;
}
static unsigned int
config_hash (gconstpointer data)
{
@@ -180,6 +189,30 @@ config_equal (gconstpointer one,
return ok;
}
static gboolean
config_equal_full (gconstpointer one,
gconstpointer two)
{
const MetaConfiguration *c_one = one;
const MetaConfiguration *c_two = two;
unsigned int i;
gboolean ok;
if (c_one->n_outputs != c_two->n_outputs)
return FALSE;
ok = TRUE;
for (i = 0; i < c_one->n_outputs && ok; i++)
{
ok = output_key_equal (&c_one->keys[i],
&c_two->keys[i]);
ok = ok && output_config_equal (&c_one->outputs[i],
&c_two->outputs[i]);
}
return ok;
}
static void
meta_monitor_config_init (MetaMonitorConfig *self)
{
@@ -1243,6 +1276,12 @@ meta_monitor_config_update_current (MetaMonitorConfig *self,
init_config_from_output (&current->outputs[i], &outputs[i]);
}
if (self->current && config_equal_full (current, self->current))
{
config_free (current);
return;
}
if (self->current && !self->current_is_stored)
config_free (self->current);

View File

@@ -301,6 +301,8 @@ GType meta_monitor_manager_get_type (void);
void meta_monitor_manager_initialize (void);
MetaMonitorManager *meta_monitor_manager_get (void);
void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager);
MetaMonitorInfo *meta_monitor_manager_get_monitor_infos (MetaMonitorManager *manager,
unsigned int *n_infos);
@@ -379,6 +381,9 @@ void meta_monitor_config_restore_previous (MetaMonitorConfig *con
void meta_crtc_info_free (MetaCRTCInfo *info);
void meta_output_info_free (MetaOutputInfo *info);
void meta_monitor_manager_free_output_array (MetaOutput *old_outputs,
int n_old_outputs);
/* Returns true if transform causes width and height to be inverted
This is true for the odd transforms in the enum */
static inline gboolean

View File

@@ -974,11 +974,57 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManager *manager,
XEvent *event)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
MetaOutput *old_outputs;
MetaCRTC *old_crtcs;
MetaMonitorMode *old_modes;
int n_old_outputs;
if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
return FALSE;
XRRUpdateConfiguration (event);
/* Save the old structures, so they stay valid during the update */
old_outputs = manager->outputs;
n_old_outputs = manager->n_outputs;
old_modes = manager->modes;
old_crtcs = manager->crtcs;
manager->serial++;
meta_monitor_manager_xrandr_read_current (manager);
/* Check if the current intended configuration has the same outputs
as the new real one, or if the event is a result of an XRandR call.
If so, we can go straight to rebuild the logical config and tell
the outside world.
Otherwise, this event was caused by hotplug, so give a chance to
MetaMonitorConfig.
Note that we need to check both the timestamps and the list of
outputs, because the X server might emit spurious events with
new configTimestamps (bug 702804), and the driver may have
changed the EDID for some other reason (old broken qxl and vbox
drivers...).
*/
if (manager_xrandr->resources->timestamp >= manager_xrandr->resources->configTimestamp ||
meta_monitor_config_match_current (manager->config, manager))
{
/* This will be a no-op if the change was from our side, as
we already called it in the DBus method handler */
meta_monitor_config_update_current (manager->config, manager);
meta_monitor_manager_rebuild_derived (manager);
}
else
{
if (!meta_monitor_config_apply_stored (manager->config, manager))
meta_monitor_config_make_default (manager->config, manager);
}
meta_monitor_manager_free_output_array (old_outputs, n_old_outputs);
g_free (old_modes);
g_free (old_crtcs);
return TRUE;
}

View File

@@ -59,67 +59,28 @@ static void meta_monitor_manager_display_config_init (MetaDBusDisplayConfigIface
G_DEFINE_TYPE_WITH_CODE (MetaMonitorManager, meta_monitor_manager, META_DBUS_TYPE_DISPLAY_CONFIG_SKELETON,
G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_DISPLAY_CONFIG, meta_monitor_manager_display_config_init));
static void free_output_array (MetaOutput *old_outputs,
int n_old_outputs);
static void invalidate_logical_config (MetaMonitorManager *manager);
static void initialize_dbus_interface (MetaMonitorManager *manager);
static void
read_current_dummy (MetaMonitorManager *manager)
{
/* The dummy monitor config has:
- one enabled output, LVDS, primary, at 0x0 and 1024x768
- one free CRTC
- two disabled outputs
- three modes, 1024x768, 800x600 and 640x480
- no clones are possible (use different CRTCs)
Low-level IDs should be assigned sequentially, to
mimick what XRandR and KMS do
*/
manager->max_screen_width = 65535;
manager->max_screen_height = 65535;
manager->screen_width = 1024;
manager->screen_height = 768;
manager->modes = g_new0 (MetaMonitorMode, 6);
manager->n_modes = 6;
manager->modes = g_new0 (MetaMonitorMode, 1);
manager->n_modes = 1;
manager->modes[0].mode_id = 1;
manager->modes[0].mode_id = 0;
manager->modes[0].width = 1024;
manager->modes[0].height = 768;
manager->modes[0].refresh_rate = 60.0;
manager->modes[1].mode_id = 2;
manager->modes[1].width = 800;
manager->modes[1].height = 600;
manager->modes[1].refresh_rate = 60.0;
manager->crtcs = g_new0 (MetaCRTC, 1);
manager->n_crtcs = 1;
manager->modes[2].mode_id = 3;
manager->modes[2].width = 640;
manager->modes[2].height = 480;
manager->modes[2].refresh_rate = 60.0;
manager->modes[3].mode_id = 4;
manager->modes[3].width = 1920;
manager->modes[3].height = 1080;
manager->modes[3].refresh_rate = 60.0;
manager->modes[4].mode_id = 5;
manager->modes[4].width = 1920;
manager->modes[4].height = 1080;
manager->modes[4].refresh_rate = 55.0;
manager->modes[5].mode_id = 6;
manager->modes[5].width = 1600;
manager->modes[5].height = 900;
manager->modes[5].refresh_rate = 60.0;
manager->crtcs = g_new0 (MetaCRTC, 3);
manager->n_crtcs = 3;
manager->crtcs[0].crtc_id = 4;
manager->crtcs[0].crtc_id = 1;
manager->crtcs[0].rect.x = 0;
manager->crtcs[0].rect.y = 0;
manager->crtcs[0].rect.width = manager->modes[0].width;
@@ -130,111 +91,30 @@ read_current_dummy (MetaMonitorManager *manager)
manager->crtcs[0].is_dirty = FALSE;
manager->crtcs[0].logical_monitor = NULL;
manager->crtcs[1].crtc_id = 5;
manager->crtcs[1].rect.x = 0;
manager->crtcs[1].rect.y = 0;
manager->crtcs[1].rect.width = 0;
manager->crtcs[1].rect.height = 0;
manager->crtcs[1].current_mode = NULL;
manager->crtcs[1].transform = WL_OUTPUT_TRANSFORM_NORMAL;
manager->crtcs[1].all_transforms = ALL_WL_TRANSFORMS;
manager->crtcs[1].is_dirty = FALSE;
manager->crtcs[1].logical_monitor = NULL;
manager->outputs = g_new0 (MetaOutput, 1);
manager->n_outputs = 1;
manager->crtcs[2].crtc_id = 5;
manager->crtcs[2].rect.x = 0;
manager->crtcs[2].rect.y = 0;
manager->crtcs[2].rect.width = 0;
manager->crtcs[2].rect.height = 0;
manager->crtcs[2].current_mode = NULL;
manager->crtcs[2].transform = WL_OUTPUT_TRANSFORM_NORMAL;
manager->crtcs[2].all_transforms = ALL_WL_TRANSFORMS;
manager->crtcs[2].is_dirty = FALSE;
manager->crtcs[2].logical_monitor = NULL;
manager->outputs = g_new0 (MetaOutput, 3);
manager->n_outputs = 3;
manager->outputs[0].crtc = NULL;
manager->outputs[0].output_id = 6;
manager->outputs[0].name = g_strdup ("HDMI");
manager->outputs[0].crtc = &manager->crtcs[0];
manager->outputs[0].output_id = 1;
manager->outputs[0].name = g_strdup ("LVDS");
manager->outputs[0].vendor = g_strdup ("MetaProducts Inc.");
manager->outputs[0].product = g_strdup ("unknown");
manager->outputs[0].serial = g_strdup ("0xC0F01A");
manager->outputs[0].width_mm = 510;
manager->outputs[0].height_mm = 287;
manager->outputs[0].serial = g_strdup ("0xC0FFEE");
manager->outputs[0].width_mm = 222;
manager->outputs[0].height_mm = 125;
manager->outputs[0].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
manager->outputs[0].preferred_mode = &manager->modes[3];
manager->outputs[0].n_modes = 5;
manager->outputs[0].modes = g_new0 (MetaMonitorMode *, 5);
manager->outputs[0].preferred_mode = &manager->modes[0];
manager->outputs[0].n_modes = 1;
manager->outputs[0].modes = g_new0 (MetaMonitorMode *, 1);
manager->outputs[0].modes[0] = &manager->modes[0];
manager->outputs[0].modes[1] = &manager->modes[1];
manager->outputs[0].modes[2] = &manager->modes[2];
manager->outputs[0].modes[3] = &manager->modes[3];
manager->outputs[0].modes[4] = &manager->modes[4];
manager->outputs[0].n_possible_crtcs = 3;
manager->outputs[0].possible_crtcs = g_new0 (MetaCRTC *, 3);
manager->outputs[0].n_possible_crtcs = 1;
manager->outputs[0].possible_crtcs = g_new0 (MetaCRTC *, 1);
manager->outputs[0].possible_crtcs[0] = &manager->crtcs[0];
manager->outputs[0].possible_crtcs[1] = &manager->crtcs[1];
manager->outputs[0].possible_crtcs[2] = &manager->crtcs[2];
manager->outputs[0].n_possible_clones = 0;
manager->outputs[0].possible_clones = g_new0 (MetaOutput *, 0);
manager->outputs[0].backlight = -1;
manager->outputs[0].backlight_min = 0;
manager->outputs[0].backlight_max = 0;
manager->outputs[1].crtc = &manager->crtcs[0];
manager->outputs[1].output_id = 7;
manager->outputs[1].name = g_strdup ("LVDS");
manager->outputs[1].vendor = g_strdup ("MetaProducts Inc.");
manager->outputs[1].product = g_strdup ("unknown");
manager->outputs[1].serial = g_strdup ("0xC0FFEE");
manager->outputs[1].width_mm = 222;
manager->outputs[1].height_mm = 125;
manager->outputs[1].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
manager->outputs[1].preferred_mode = &manager->modes[5];
manager->outputs[1].n_modes = 4;
manager->outputs[1].modes = g_new0 (MetaMonitorMode *, 4);
manager->outputs[1].modes[0] = &manager->modes[0];
manager->outputs[1].modes[1] = &manager->modes[1];
manager->outputs[1].modes[2] = &manager->modes[2];
manager->outputs[1].modes[3] = &manager->modes[5];
manager->outputs[1].n_possible_crtcs = 3;
manager->outputs[1].possible_crtcs = g_new0 (MetaCRTC *, 3);
manager->outputs[1].possible_crtcs[0] = &manager->crtcs[0];
manager->outputs[1].possible_crtcs[1] = &manager->crtcs[1];
manager->outputs[1].possible_crtcs[2] = &manager->crtcs[2];
manager->outputs[1].n_possible_clones = 0;
manager->outputs[1].possible_clones = g_new0 (MetaOutput *, 0);
manager->outputs[1].backlight = -1;
manager->outputs[1].backlight_min = 0;
manager->outputs[1].backlight_max = 0;
manager->outputs[2].crtc = NULL;
manager->outputs[2].output_id = 8;
manager->outputs[2].name = g_strdup ("VGA");
manager->outputs[2].vendor = g_strdup ("MetaProducts Inc.");
manager->outputs[2].product = g_strdup ("unknown");
manager->outputs[2].serial = g_strdup ("0xC4FE");
manager->outputs[2].width_mm = 309;
manager->outputs[2].height_mm = 174;
manager->outputs[2].subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
manager->outputs[2].preferred_mode = &manager->modes[0];
manager->outputs[2].n_modes = 3;
manager->outputs[2].modes = g_new0 (MetaMonitorMode *, 3);
manager->outputs[2].modes[0] = &manager->modes[0];
manager->outputs[2].modes[1] = &manager->modes[1];
manager->outputs[2].modes[2] = &manager->modes[2];
manager->outputs[2].n_possible_crtcs = 3;
manager->outputs[2].possible_crtcs = g_new0 (MetaCRTC *, 3);
manager->outputs[2].possible_crtcs[0] = &manager->crtcs[0];
manager->outputs[2].possible_crtcs[1] = &manager->crtcs[1];
manager->outputs[2].possible_crtcs[2] = &manager->crtcs[2];
manager->outputs[2].n_possible_clones = 0;
manager->outputs[2].possible_clones = g_new0 (MetaOutput *, 0);
manager->outputs[2].backlight = -1;
manager->outputs[2].backlight_min = 0;
manager->outputs[2].backlight_max = 0;
}
static void
@@ -349,7 +229,7 @@ apply_config_dummy (MetaMonitorManager *manager,
manager->screen_width = screen_width;
manager->screen_height = screen_height;
invalidate_logical_config (manager);
meta_monitor_manager_rebuild_derived (manager);
}
static GBytes *
@@ -536,7 +416,7 @@ meta_monitor_manager_constructed (GObject *object)
read_current_config (manager);
free_output_array (old_outputs, n_old_outputs);
meta_monitor_manager_free_output_array (old_outputs, n_old_outputs);
g_free (old_modes);
g_free (old_crtcs);
}
@@ -567,9 +447,9 @@ meta_monitor_manager_set_power_save_mode (MetaMonitorManager *manager,
manager->power_save_mode = mode;
}
static void
free_output_array (MetaOutput *old_outputs,
int n_old_outputs)
void
meta_monitor_manager_free_output_array (MetaOutput *old_outputs,
int n_old_outputs)
{
int i;
@@ -592,7 +472,7 @@ meta_monitor_manager_finalize (GObject *object)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (object);
free_output_array (manager->outputs, manager->n_outputs);
meta_monitor_manager_free_output_array (manager->outputs, manager->n_outputs);
g_free (manager->monitor_infos);
g_free (manager->modes);
g_free (manager->crtcs);
@@ -1485,8 +1365,8 @@ meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager,
*height = manager->max_screen_height;
}
static void
invalidate_logical_config (MetaMonitorManager *manager)
void
meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager)
{
MetaMonitorInfo *old_monitor_infos;
@@ -1507,51 +1387,11 @@ meta_monitor_manager_handle_xevent (MetaMonitorManager *manager,
XEvent *event)
{
MetaMonitorManagerClass *klass;
MetaOutput *old_outputs;
MetaCRTC *old_crtcs;
MetaMonitorMode *old_modes;
int n_old_outputs;
gboolean changed;
klass = META_MONITOR_MANAGER_GET_CLASS (manager);
if (klass->handle_xevent)
changed = klass->handle_xevent (manager, event);
return klass->handle_xevent (manager, event);
else
changed = FALSE;
if (!changed)
return FALSE;
/* Save the old structures, so they stay valid during the update */
old_outputs = manager->outputs;
n_old_outputs = manager->n_outputs;
old_modes = manager->modes;
old_crtcs = manager->crtcs;
read_current_config (manager);
/* Check if the current intended configuration has the same outputs
as the new real one. If so, this was a result of an ApplyConfiguration
call (or a change from ourselves), and we can go straight to rebuild
the logical config and tell the outside world.
Otherwise, this event was caused by hotplug, so give a chance to
MetaMonitorConfig.
*/
if (meta_monitor_config_match_current (manager->config, manager))
{
invalidate_logical_config (manager);
}
else
{
if (!meta_monitor_config_apply_stored (manager->config, manager))
meta_monitor_config_make_default (manager->config, manager);
}
free_output_array (old_outputs, n_old_outputs);
g_free (old_modes);
g_free (old_crtcs);
return TRUE;
}

View File

@@ -1051,7 +1051,6 @@ stack_tracker_event_received (MetaStackTracker *tracker,
tracker->xserver_serial = op->any.serial;
#warning "TODO: remove unused tracker->xserver_stack"
/* XXX: With the design we have ended up with it looks like we've
* ended up making it unnecessary to maintain tracker->xserver_stack
* since we only need an xserver_stack during the

View File

@@ -44,15 +44,7 @@
#include <X11/Xutil.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "meta-wayland-private.h"
/* XXX: We should find a nicer approach to deal with the
* circular dependency we have with the current headers
* (meta-wayland-private.h which typedefs MetaWaylandSurface
* also includes window-private.h) */
#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
typedef struct _MetaWaylandSurface MetaWaylandSurface;
#endif
#include "meta-wayland-types.h"
typedef struct _MetaWindowQueue MetaWindowQueue;
@@ -339,11 +331,6 @@ struct _MetaWindow
guint using_net_wm_icon_name : 1; /* vs. plain wm_icon_name */
guint using_net_wm_visible_icon_name : 1; /* tracked so we can clear it */
/* has a bounding shape mask */
guint has_shape : 1;
/* has an input shape mask */
guint has_input_shape : 1;
/* icon props have changed */
guint need_reread_icon : 1;
@@ -365,9 +352,15 @@ struct _MetaWindow
/* if non-NULL, the bounds of the window frame */
cairo_region_t *frame_bounds;
/* if non-NULL, the bounding shape region of the window */
cairo_region_t *shape_region;
/* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */
cairo_region_t *opaque_region;
/* the input shape region for picking */
cairo_region_t *input_region;
/* if TRUE, the we have the new form of sync request counter which
* also handles application frames */
guint extended_sync_request_counter : 1;
@@ -685,7 +678,6 @@ void meta_window_update_icon_now (MetaWindow *window);
void meta_window_update_role (MetaWindow *window);
void meta_window_update_net_wm_type (MetaWindow *window);
void meta_window_update_opaque_region (MetaWindow *window);
void meta_window_update_for_monitors_changed (MetaWindow *window);
void meta_window_update_on_all_workspaces (MetaWindow *window);
@@ -699,4 +691,29 @@ void meta_window_compute_tile_match (MetaWindow *window);
gboolean meta_window_updates_are_frozen (MetaWindow *window);
void meta_window_set_opaque_region (MetaWindow *window,
cairo_region_t *region);
void meta_window_update_opaque_region_x11 (MetaWindow *window);
void meta_window_set_input_region (MetaWindow *window,
cairo_region_t *region);
void meta_window_update_input_region_x11 (MetaWindow *window);
void meta_window_set_shape_region (MetaWindow *window,
cairo_region_t *region);
void meta_window_update_shape_region_x11 (MetaWindow *window);
void meta_window_set_title (MetaWindow *window,
const char *title);
void meta_window_set_wm_class (MetaWindow *window,
const char *wm_class,
const char *wm_instance);
void meta_window_set_gtk_dbus_properties (MetaWindow *window,
const char *application_id,
const char *unique_bus_name,
const char *appmenu_path,
const char *menubar_path,
const char *application_object_path,
const char *window_object_path);
#endif

View File

@@ -489,28 +489,19 @@ static void
set_window_title (MetaWindow *window,
const char *title)
{
char *str;
char *new_title = NULL;
gboolean modified =
set_title_text (window,
window->using_net_wm_visible_name,
title,
window->display->atom__NET_WM_VISIBLE_NAME,
&window->title);
&new_title);
window->using_net_wm_visible_name = modified;
/* strndup is a hack since GNU libc has broken %.10s */
str = g_strndup (window->title, 10);
g_free (window->desc);
window->desc = g_strdup_printf ("0x%lx (%s)", window->xwindow, str);
g_free (str);
meta_window_set_title (window, new_title);
if (window->frame)
meta_ui_set_frame_title (window->screen->ui,
window->frame->xwindow,
window->title);
g_object_notify (G_OBJECT (window), "title");
g_free (new_title);
}
static void
@@ -565,7 +556,7 @@ reload_opaque_region (MetaWindow *window,
MetaPropValue *value,
gboolean initial)
{
meta_window_update_opaque_region (window);
meta_window_update_opaque_region_x11 (window);
}
static void
@@ -875,23 +866,15 @@ reload_wm_class (MetaWindow *window,
MetaPropValue *value,
gboolean initial)
{
if (window->res_class)
g_free (window->res_class);
if (window->res_name)
g_free (window->res_name);
window->res_class = NULL;
window->res_name = NULL;
if (value->type != META_PROP_VALUE_INVALID)
{
if (value->v.class_hint.res_name)
window->res_name = g_strdup (value->v.class_hint.res_name);
if (value->v.class_hint.res_class)
window->res_class = g_strdup (value->v.class_hint.res_class);
g_object_notify (G_OBJECT (window), "wm-class");
{
meta_window_set_wm_class (window,
value->v.class_hint.res_class,
value->v.class_hint.res_name);
}
else
{
meta_window_set_wm_class (window, NULL, NULL);
}
meta_verbose ("Window %s class: '%s' name: '%s'\n",

View File

@@ -822,8 +822,6 @@ meta_window_new_shared (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
gulong existing_wm_state,
gboolean has_shape,
gboolean has_input_shape,
MetaCompEffect effect,
XWindowAttributes *attrs)
{
@@ -876,9 +874,6 @@ meta_window_new_shared (MetaDisplay *display,
/* avoid tons of stack updates */
meta_stack_freeze (window->screen->stack);
window->has_shape = has_shape;
window->has_input_shape = has_input_shape;
window->rect.x = attrs->x;
window->rect.y = attrs->y;
window->rect.width = attrs->width;
@@ -1059,6 +1054,8 @@ meta_window_new_shared (MetaDisplay *display,
}
meta_display_register_x_window (display, &window->xwindow, window);
meta_window_update_shape_region_x11 (window);
meta_window_update_input_region_x11 (window);
}
/* assign the window to its group, or create a new group if needed
@@ -1067,7 +1064,10 @@ meta_window_new_shared (MetaDisplay *display,
window->xgroup_leader = None;
meta_window_compute_group (window);
meta_window_load_initial_properties (window);
if (client_type == META_WINDOW_CLIENT_TYPE_X11)
meta_window_load_initial_properties (window);
else
meta_wayland_surface_set_initial_state (window->surface, window);
if (!window->override_redirect &&
client_type == META_WINDOW_CLIENT_TYPE_X11)
@@ -1425,8 +1425,6 @@ meta_window_new_for_wayland (MetaDisplay *display,
None,
TRUE,
WithdrawnState,
FALSE, /* has shape */
FALSE, /* has input shape */
META_COMP_EFFECT_NONE,
&attrs);
@@ -1455,8 +1453,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
gulong existing_wm_state;
MetaWindow *window;
gulong event_mask;
gboolean has_shape = FALSE;
gboolean has_input_shape = FALSE;
meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
@@ -1576,53 +1572,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
XISelectEvents (display->xdisplay, xwindow, &mask, 1);
}
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (display))
{
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
XRectangle *input_rectangles;
int n_rects, ordering;
XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
XShapeQueryExtents (display->xdisplay, xwindow,
&bounding_shaped, &x_bounding, &y_bounding,
&w_bounding, &h_bounding,
&clip_shaped, &x_clip, &y_clip,
&w_clip, &h_clip);
has_shape = bounding_shaped != FALSE;
/* XXX: The x shape extension doesn't provide a way to only test if an
* input shape has been specified, so we have to query and throw away the
* rectangles. */
meta_error_trap_push (display);
input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow,
ShapeInput, &n_rects, &ordering);
meta_error_trap_pop (display);
if (input_rectangles)
{
if (n_rects > 1 ||
(n_rects == 1 &&
(input_rectangles[0].x != x_bounding ||
input_rectangles[1].y != y_bounding ||
input_rectangles[2].width != w_bounding ||
input_rectangles[3].height != h_bounding)))
{
has_input_shape = TRUE;
}
XFree (input_rectangles);
}
meta_topic (META_DEBUG_SHAPES,
"Window has_shape = %d extents %d,%d %u x %u\n",
has_shape, x_bounding, y_bounding,
w_bounding, h_bounding);
}
#endif
/* Get rid of any borders */
if (attrs->border_width != 0)
XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
@@ -1656,8 +1605,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
xwindow,
must_be_viewable,
existing_wm_state,
has_shape,
has_input_shape,
effect,
attrs);
@@ -7810,14 +7757,25 @@ meta_window_update_net_wm_type (MetaWindow *window)
}
void
meta_window_update_opaque_region (MetaWindow *window)
meta_window_set_opaque_region (MetaWindow *window,
cairo_region_t *region)
{
g_clear_pointer (&window->opaque_region, cairo_region_destroy);
if (region != NULL)
window->opaque_region = cairo_region_reference (region);
if (window->display->compositor)
meta_compositor_window_shape_changed (window->display->compositor, window);
}
void
meta_window_update_opaque_region_x11 (MetaWindow *window)
{
cairo_region_t *opaque_region = NULL;
gulong *region = NULL;
int nitems;
g_clear_pointer (&window->opaque_region, cairo_region_destroy);
if (meta_prop_get_cardinal_list (window->display,
window->xwindow,
window->display->atom__NET_WM_OPAQUE_REGION,
@@ -7860,11 +7818,191 @@ meta_window_update_opaque_region (MetaWindow *window)
}
out:
window->opaque_region = opaque_region;
meta_XFree (region);
meta_window_set_opaque_region (window, opaque_region);
cairo_region_destroy (opaque_region);
}
static cairo_region_t *
region_create_from_x_rectangles (const XRectangle *rects,
int n_rects)
{
int i;
cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i ++)
{
cairo_rects[i].x = rects[i].x;
cairo_rects[i].y = rects[i].y;
cairo_rects[i].width = rects[i].width;
cairo_rects[i].height = rects[i].height;
}
return cairo_region_create_rectangles (cairo_rects, n_rects);
}
void
meta_window_set_input_region (MetaWindow *window,
cairo_region_t *region)
{
g_clear_pointer (&window->input_region, cairo_region_destroy);
if (region != NULL)
window->input_region = cairo_region_reference (region);
if (window->display->compositor)
meta_compositor_window_x11_shape_changed (window->display->compositor, window);
meta_compositor_window_shape_changed (window->display->compositor, window);
}
void
meta_window_update_input_region_x11 (MetaWindow *window)
{
cairo_region_t *region = NULL;
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (window->display))
{
/* Translate the set of XShape rectangles that we
* get from the X server to a cairo_region. */
XRectangle *rects = NULL;
int n_rects, ordering;
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
meta_error_trap_push (window->display);
XShapeQueryExtents (window->display->xdisplay, window->xwindow,
&bounding_shaped, &x_bounding, &y_bounding,
&w_bounding, &h_bounding,
&clip_shaped, &x_clip, &y_clip,
&w_clip, &h_clip);
rects = XShapeGetRectangles (window->display->xdisplay,
window->xwindow,
ShapeInput,
&n_rects,
&ordering);
meta_error_trap_pop (window->display);
/* XXX: The x shape extension doesn't provide a way to only test if an
* input shape has been specified, so we have to query and throw away the
* rectangles. */
if (rects)
{
if (n_rects > 1 ||
(n_rects == 1 &&
(rects[0].x != x_bounding ||
rects[1].y != y_bounding ||
rects[2].width != w_bounding ||
rects[3].height != h_bounding)))
region = region_create_from_x_rectangles (rects, n_rects);
XFree (rects);
}
}
#endif /* HAVE_SHAPE */
if (region != NULL)
{
cairo_rectangle_int_t client_area;
client_area.x = 0;
client_area.y = 0;
client_area.width = window->rect.width;
client_area.height = window->rect.height;
/* The shape we get back from the client may have coordinates
* outside of the frame. The X SHAPE Extension requires that
* the overall shape the client provides never exceeds the
* "bounding rectangle" of the window -- the shape that the
* window would have gotten if it was unshaped. In our case,
* this is simply the client area.
*/
cairo_region_intersect_rectangle (region, &client_area);
}
meta_window_set_input_region (window, region);
cairo_region_destroy (region);
}
void
meta_window_set_shape_region (MetaWindow *window,
cairo_region_t *region)
{
g_clear_pointer (&window->shape_region, cairo_region_destroy);
if (region != NULL)
window->shape_region = cairo_region_reference (region);
if (window->display->compositor)
meta_compositor_window_shape_changed (window->display->compositor, window);
}
void
meta_window_update_shape_region_x11 (MetaWindow *window)
{
cairo_region_t *region = NULL;
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (window->display))
{
/* Translate the set of XShape rectangles that we
* get from the X server to a cairo_region. */
XRectangle *rects = NULL;
int n_rects, ordering;
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
meta_error_trap_push (window->display);
XShapeQueryExtents (window->display->xdisplay, window->xwindow,
&bounding_shaped, &x_bounding, &y_bounding,
&w_bounding, &h_bounding,
&clip_shaped, &x_clip, &y_clip,
&w_clip, &h_clip);
if (bounding_shaped)
{
rects = XShapeGetRectangles (window->display->xdisplay,
window->xwindow,
ShapeBounding,
&n_rects,
&ordering);
}
meta_error_trap_pop (window->display);
if (rects)
{
region = region_create_from_x_rectangles (rects, n_rects);
XFree (rects);
}
}
#endif /* HAVE_SHAPE */
if (region != NULL)
{
cairo_rectangle_int_t client_area;
client_area.x = 0;
client_area.y = 0;
client_area.width = window->rect.width;
client_area.height = window->rect.height;
/* The shape we get back from the client may have coordinates
* outside of the frame. The X SHAPE Extension requires that
* the overall shape the client provides never exceeds the
* "bounding rectangle" of the window -- the shape that the
* window would have gotten if it was unshaped. In our case,
* this is simply the client area.
*/
cairo_region_intersect_rectangle (region, &client_area);
}
meta_window_set_shape_region (window, region);
cairo_region_destroy (region);
}
static void
@@ -11332,3 +11470,78 @@ meta_window_can_close (MetaWindow *window)
{
return window->has_close_func;
}
void
meta_window_set_title (MetaWindow *window,
const char *title)
{
char *str;
g_free (window->title);
window->title = g_strdup (title);
/* strndup is a hack since GNU libc has broken %.10s */
str = g_strndup (window->title, 10);
g_free (window->desc);
window->desc = g_strdup_printf ("0x%lx (%s)", window->xwindow, str);
g_free (str);
if (window->frame)
meta_ui_set_frame_title (window->screen->ui,
window->frame->xwindow,
window->title);
g_object_notify (G_OBJECT (window), "title");
}
void
meta_window_set_wm_class (MetaWindow *window,
const char *wm_class,
const char *wm_instance)
{
g_free (window->res_class);
g_free (window->res_name);
window->res_name = g_strdup (wm_instance);
window->res_class = g_strdup (wm_class);
g_object_notify (G_OBJECT (window), "wm-class");
}
void
meta_window_set_gtk_dbus_properties (MetaWindow *window,
const char *application_id,
const char *unique_bus_name,
const char *appmenu_path,
const char *menubar_path,
const char *application_object_path,
const char *window_object_path)
{
g_object_freeze_notify (G_OBJECT (window));
g_free (window->gtk_application_id);
window->gtk_application_id = g_strdup (application_id);
g_object_notify (G_OBJECT (window), "gtk-application-id");
g_free (window->gtk_unique_bus_name);
window->gtk_unique_bus_name = g_strdup (unique_bus_name);
g_object_notify (G_OBJECT (window), "gtk-unique-bus-name");
g_free (window->gtk_app_menu_object_path);
window->gtk_app_menu_object_path = g_strdup (appmenu_path);
g_object_notify (G_OBJECT (window), "gtk-app-menu-object-path");
g_free (window->gtk_menubar_object_path);
window->gtk_menubar_object_path = g_strdup (menubar_path);
g_object_notify (G_OBJECT (window), "gtk-menubar-object-path");
g_free (window->gtk_application_object_path);
window->gtk_application_object_path = g_strdup (application_object_path);
g_object_notify (G_OBJECT (window), "gtk-application-object-path");
g_free (window->gtk_window_object_path);
window->gtk_window_object_path = g_strdup (window_object_path);
g_object_notify (G_OBJECT (window), "gtk-window-object-path");
g_object_thaw_notify (G_OBJECT (window));
}

View File

@@ -64,8 +64,8 @@ void meta_compositor_manage_screen (MetaCompositor *compositor,
void meta_compositor_unmanage_screen (MetaCompositor *compositor,
MetaScreen *screen);
void meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
MetaWindow *window);
void meta_compositor_window_shape_changed (MetaCompositor *compositor,
MetaWindow *window);
gboolean meta_compositor_process_event (MetaCompositor *compositor,
XEvent *event,

View File

@@ -67,11 +67,12 @@ GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
gboolean create_mipmaps);
void meta_shaped_texture_update_area (MetaShapedTexture *stex,
int x,
int y,
int width,
int height);
gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex,
int x,
int y,
int width,
int height,
cairo_region_t *unobscured_region);
CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
@@ -80,10 +81,12 @@ void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
void meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex,
cairo_region_t *shape_region);
/* Assumes ownership of clip_region */
void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
cairo_region_t *clip_region);
void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
cairo_region_t *opaque_region);
cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture *stex,
cairo_rectangle_int_t *clip);

View File

@@ -0,0 +1,33 @@
<schemalist>
<schema id="org.gnome.mutter.wayland.keybindings" path="/org/gnome/mutter/wayland/keybindings/"
gettext-domain="@GETTEXT_DOMAIN@">
<key name="switch-to-session-1" type="as">
<default><![CDATA[['<Primary><Alt>F1']]]></default>
<_summary>Switch to VT 1</_summary>
</key>
<key name="switch-to-session-2" type="as">
<default><![CDATA[['<Primary><Alt>F2']]]></default>
<_summary>Switch to VT 2</_summary>
</key>
<key name="switch-to-session-3" type="as">
<default><![CDATA[['<Primary><Alt>F3']]]></default>
<_summary>Switch to VT 3</_summary>
</key>
<key name="switch-to-session-4" type="as">
<default><![CDATA[['<Primary><Alt>F4']]]></default>
<_summary>Switch to VT 4</_summary>
</key>
<key name="switch-to-session-5" type="as">
<default><![CDATA[['<Primary><Alt>F5']]]></default>
<_summary>Switch to VT 5</_summary>
</key>
<key name="switch-to-session-6" type="as">
<default><![CDATA[['<Primary><Alt>F6']]]></default>
<_summary>Switch to VT 6</_summary>
</key>
<key name="switch-to-session-7" type="as">
<default><![CDATA[['<Primary><Alt>F7']]]></default>
<_summary>Switch to VT 7</_summary>
</key>
</schema>
</schemalist>

View File

@@ -430,7 +430,11 @@ locate_attributes (GMarkupParseContext *context,
while (name != NULL)
{
g_return_val_if_fail (retloc != NULL, FALSE);
if (retloc == NULL)
{
retval = FALSE;
goto out;
}
g_assert (n_attrs < MAX_ATTRS);
@@ -3212,9 +3216,6 @@ parse_style_set_element (GMarkupParseContext *context,
meta_frame_style_ref (frame_style);
info->style_set->tiled_right_styles[frame_focus] = frame_style;
break;
meta_frame_style_ref (frame_style);
info->style_set->tiled_right_styles[frame_focus] = frame_style;
break;
case META_FRAME_STATE_SHADED:
if (info->style_set->shaded_styles[frame_resize][frame_focus])
{

View File

@@ -33,6 +33,7 @@
#include "meta-wayland-data-device.h"
#include "meta-wayland-seat.h"
#include "meta-wayland-pointer.h"
#include "meta-wayland-private.h"
static void
data_offer_accept (struct wl_client *client,

View File

@@ -56,8 +56,9 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <clutter/evdev/clutter-evdev.h>
#include "meta-wayland-keyboard.h"
#include "meta-wayland-private.h"
static MetaWaylandSeat *
meta_wayland_keyboard_get_seat (MetaWaylandKeyboard *keyboard)
@@ -258,12 +259,11 @@ default_grab_modifiers (MetaWaylandKeyboardGrab *grab, uint32_t serial,
pointer->focus);
if (pr)
{
wl_keyboard_send_modifiers (pr,
serial,
keyboard->modifiers.mods_depressed,
keyboard->modifiers.mods_latched,
keyboard->modifiers.mods_locked,
keyboard->modifiers.group);
wl_keyboard_send_modifiers (pr, serial,
mods_depressed,
mods_latched,
mods_locked,
group);
}
}
}
@@ -274,10 +274,36 @@ static const MetaWaylandKeyboardGrabInterface
default_grab_modifiers,
};
static void
modal_key (MetaWaylandKeyboardGrab *grab,
uint32_t time,
uint32_t key,
uint32_t state)
{
}
static void
modal_modifiers (MetaWaylandKeyboardGrab *grab,
uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group)
{
}
static MetaWaylandKeyboardGrabInterface modal_grab = {
modal_key,
modal_modifiers,
};
gboolean
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
struct wl_display *display)
struct wl_display *display,
gboolean is_evdev)
{
ClutterDeviceManager *manager;
memset (keyboard, 0, sizeof *keyboard);
wl_list_init (&keyboard->resource_list);
@@ -293,8 +319,22 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
keyboard->xkb_context = xkb_context_new (0 /* flags */);
meta_wayland_keyboard_build_global_keymap (keyboard->xkb_context,
&keyboard->xkb_names,
&keyboard->xkb_info);
&keyboard->xkb_names,
&keyboard->xkb_info);
keyboard->is_evdev = is_evdev;
if (is_evdev)
{
manager = clutter_device_manager_get_default ();
clutter_evdev_set_keyboard_map (manager, keyboard->xkb_info.keymap);
keyboard->xkb_state = clutter_evdev_get_keyboard_state (manager);
xkb_state_ref (keyboard->xkb_state);
}
else
{
keyboard->xkb_state = xkb_state_new (keyboard->xkb_info.keymap);
}
return TRUE;
}
@@ -312,17 +352,12 @@ meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
}
static void
set_modifiers (MetaWaylandKeyboard *keyboard,
guint32 serial,
ClutterModifierType modifier_state)
update_state_from_clutter (MetaWaylandKeyboard *keyboard,
ClutterModifierType modifier_state)
{
MetaWaylandKeyboardGrab *grab = keyboard->grab;
uint32_t depressed_mods = 0;
uint32_t locked_mods = 0;
if (keyboard->last_modifier_state == modifier_state)
return;
if ((modifier_state & CLUTTER_SHIFT_MASK) &&
keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.shift_mod);
@@ -355,14 +390,56 @@ set_modifiers (MetaWaylandKeyboard *keyboard,
keyboard->xkb_info.mod5_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.mod5_mod);
keyboard->last_modifier_state = modifier_state;
xkb_state_update_mask (keyboard->xkb_state,
depressed_mods,
0,
locked_mods,
0, 0, 0);
}
static gboolean
state_equal (MetaWaylandXkbState *one,
MetaWaylandXkbState *two)
{
return one->mods_depressed == two->mods_depressed &&
one->mods_latched == two->mods_latched &&
one->mods_locked == two->mods_locked &&
one->group == two->group;
}
static void
set_modifiers (MetaWaylandKeyboard *keyboard,
guint32 serial,
ClutterModifierType modifier_state)
{
MetaWaylandKeyboardGrab *grab = keyboard->grab;
MetaWaylandXkbState new_state;
/* In the evdev case, the state is shared with the clutter backend, so
we don't need to update it */
if (!keyboard->is_evdev)
update_state_from_clutter (keyboard, modifier_state);
new_state.mods_depressed = xkb_state_serialize_mods (keyboard->xkb_state,
XKB_STATE_MODS_DEPRESSED);
new_state.mods_latched = xkb_state_serialize_mods (keyboard->xkb_state,
XKB_STATE_MODS_LATCHED);
new_state.mods_locked = xkb_state_serialize_mods (keyboard->xkb_state,
XKB_STATE_MODS_LOCKED);
new_state.group = xkb_state_serialize_layout (keyboard->xkb_state,
XKB_STATE_LAYOUT_EFFECTIVE);
if (state_equal (&keyboard->modifier_state, &new_state))
return;
keyboard->modifier_state = new_state;
grab->interface->modifiers (grab,
serial,
depressed_mods,
0, /* latched_modes */
locked_mods,
0 /* group */);
new_state.mods_depressed,
new_state.mods_latched,
new_state.mods_locked,
new_state.group);
}
void
@@ -461,13 +538,29 @@ meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
display = wl_client_get_display (client);
serial = wl_display_next_serial (display);
wl_keyboard_send_modifiers (resource, serial,
keyboard->modifiers.mods_depressed,
keyboard->modifiers.mods_latched,
keyboard->modifiers.mods_locked,
keyboard->modifiers.group);
wl_keyboard_send_enter (resource, serial, surface->resource,
&keyboard->keys);
/* If we're in a modal grab, the client is focused but doesn't see
modifiers or pressed keys (and fix that up when we exit the modal) */
if (keyboard->grab->interface == &modal_grab)
{
struct wl_array empty;
wl_array_init (&empty);
wl_keyboard_send_modifiers (resource, serial,
0, 0, 0, 0);
wl_keyboard_send_enter (resource, serial, surface->resource,
&empty);
}
else
{
wl_keyboard_send_modifiers (resource, serial,
keyboard->modifier_state.mods_depressed,
keyboard->modifier_state.mods_latched,
keyboard->modifier_state.mods_locked,
keyboard->modifier_state.group);
wl_keyboard_send_enter (resource, serial, surface->resource,
&keyboard->keys);
}
wl_resource_add_destroy_listener (resource, &keyboard->focus_listener);
keyboard->focus_serial = serial;
}
@@ -504,9 +597,79 @@ meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard)
meta_wayland_xkb_info_destroy (&keyboard->xkb_info);
xkb_context_unref (keyboard->xkb_context);
xkb_state_unref (keyboard->xkb_state);
/* XXX: What about keyboard->resource_list? */
if (keyboard->focus_resource)
wl_list_remove (&keyboard->focus_listener.link);
wl_array_release (&keyboard->keys);
}
gboolean
meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard,
guint32 timestamp)
{
MetaWaylandKeyboardGrab *grab;
uint32_t *end = (void *) ((char *) keyboard->keys.data +
keyboard->keys.size);
uint32_t *k;
uint32_t serial;
if (keyboard->grab != &keyboard->default_grab)
return FALSE;
if (keyboard->focus)
{
/* Fake key release events for the focused app */
serial = wl_display_next_serial (keyboard->display);
keyboard->grab->interface->modifiers (keyboard->grab,
serial,
0, 0, 0, 0);
for (k = keyboard->keys.data; k < end; k++)
keyboard->grab->interface->key (keyboard->grab,
timestamp,
*k, 0);
}
grab = g_slice_new0 (MetaWaylandKeyboardGrab);
grab->interface = &modal_grab;
meta_wayland_keyboard_start_grab (keyboard, grab);
return TRUE;
}
void
meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard,
guint32 timestamp)
{
MetaWaylandKeyboardGrab *grab;
uint32_t *end = (void *) ((char *) keyboard->keys.data +
keyboard->keys.size);
uint32_t *k;
uint32_t serial;
grab = keyboard->grab;
g_assert (grab->interface == &modal_grab);
meta_wayland_keyboard_end_grab (keyboard);
g_slice_free (MetaWaylandKeyboardGrab, grab);
if (keyboard->focus)
{
/* Fake key press events for the focused app */
serial = wl_display_next_serial (keyboard->display);
keyboard->grab->interface->modifiers (keyboard->grab,
serial,
keyboard->modifier_state.mods_depressed,
keyboard->modifier_state.mods_latched,
keyboard->modifier_state.mods_locked,
keyboard->modifier_state.group);
for (k = keyboard->keys.data; k < end; k++)
keyboard->grab->interface->key (keyboard->grab,
timestamp,
*k, 1);
}
}

View File

@@ -48,11 +48,83 @@
#include <clutter/clutter.h>
#include <wayland-server.h>
#include "meta-wayland-seat.h"
struct _MetaWaylandKeyboardGrabInterface
{
void (*key) (MetaWaylandKeyboardGrab * grab, uint32_t time,
uint32_t key, uint32_t state);
void (*modifiers) (MetaWaylandKeyboardGrab * grab, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group);
};
struct _MetaWaylandKeyboardGrab
{
const MetaWaylandKeyboardGrabInterface *interface;
MetaWaylandKeyboard *keyboard;
MetaWaylandSurface *focus;
uint32_t key;
};
typedef struct
{
struct xkb_keymap *keymap;
int keymap_fd;
size_t keymap_size;
char *keymap_area;
xkb_mod_index_t shift_mod;
xkb_mod_index_t caps_mod;
xkb_mod_index_t ctrl_mod;
xkb_mod_index_t alt_mod;
xkb_mod_index_t mod2_mod;
xkb_mod_index_t mod3_mod;
xkb_mod_index_t super_mod;
xkb_mod_index_t mod5_mod;
} MetaWaylandXkbInfo;
typedef struct
{
uint32_t mods_depressed;
uint32_t mods_latched;
uint32_t mods_locked;
uint32_t group;
} MetaWaylandXkbState;
struct _MetaWaylandKeyboard
{
struct wl_list resource_list;
MetaWaylandSurface *focus;
struct wl_resource *focus_resource;
struct wl_listener focus_listener;
uint32_t focus_serial;
struct wl_signal focus_signal;
MetaWaylandKeyboardGrab *grab;
MetaWaylandKeyboardGrab default_grab;
uint32_t grab_key;
uint32_t grab_serial;
uint32_t grab_time;
struct wl_array keys;
MetaWaylandXkbState modifier_state;
struct wl_display *display;
struct xkb_context *xkb_context;
struct xkb_state *xkb_state;
gboolean is_evdev;
MetaWaylandXkbInfo xkb_info;
struct xkb_rule_names xkb_names;
MetaWaylandKeyboardGrab input_method_grab;
struct wl_resource *input_method_resource;
};
gboolean
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
struct wl_display *display);
struct wl_display *display,
gboolean is_evdev);
void
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
@@ -69,6 +141,13 @@ meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *device,
void
meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard);
gboolean
meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard,
guint32 timestamp);
void
meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard,
guint32 timestamp);
void
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard);

View File

@@ -44,6 +44,7 @@
#include "config.h"
#include "meta-wayland-pointer.h"
#include "meta-wayland-private.h"
#include <string.h>
@@ -194,10 +195,10 @@ meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer,
{
wl_keyboard_send_modifiers (kr,
serial,
kbd->modifiers.mods_depressed,
kbd->modifiers.mods_latched,
kbd->modifiers.mods_locked,
kbd->modifiers.group);
kbd->modifier_state.mods_depressed,
kbd->modifier_state.mods_latched,
kbd->modifier_state.mods_locked,
kbd->modifier_state.group);
}
}
wl_pointer_send_enter (resource, serial, surface->resource, sx, sy);
@@ -262,3 +263,83 @@ meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
&pointer->current_listener);
pointer->current_listener.notify = current_surface_destroy;
}
static void
modal_focus (MetaWaylandPointerGrab *grab,
MetaWaylandSurface *surface,
wl_fixed_t x,
wl_fixed_t y)
{
}
static void
modal_motion (MetaWaylandPointerGrab *grab,
uint32_t time,
wl_fixed_t x,
wl_fixed_t y)
{
}
static void
modal_button (MetaWaylandPointerGrab *grab,
uint32_t time,
uint32_t button,
uint32_t state)
{
}
static MetaWaylandPointerGrabInterface modal_grab = {
modal_focus,
modal_motion,
modal_button
};
gboolean
meta_wayland_pointer_begin_modal (MetaWaylandPointer *pointer)
{
MetaWaylandPointerGrab *grab;
if (pointer->grab != &pointer->default_grab)
return FALSE;
meta_wayland_pointer_set_focus (pointer, NULL,
wl_fixed_from_int (0),
wl_fixed_from_int (0));
grab = g_slice_new0 (MetaWaylandPointerGrab);
grab->interface = &modal_grab;
meta_wayland_pointer_start_grab (pointer, grab);
return TRUE;
}
void
meta_wayland_pointer_end_modal (MetaWaylandPointer *pointer)
{
MetaWaylandPointerGrab *grab;
grab = pointer->grab;
g_assert (grab->interface == &modal_grab);
meta_wayland_pointer_end_grab (pointer);
g_slice_free (MetaWaylandPointerGrab, grab);
}
/* Called when the focused resource is destroyed */
void
meta_wayland_pointer_destroy_focus (MetaWaylandPointer *pointer)
{
if (pointer->grab == &pointer->default_grab)
{
/* The surface was destroyed, but had the implicit pointer grab.
Bypass the grab interface. */
g_assert (pointer->button_count > 0);
/* Note: we focus the NULL interface, not the current one, because
we have button down, and the clients would be confused if the
pointer enters the surface.
*/
meta_wayland_pointer_set_focus (pointer, NULL, 0, 0);
}
}

View File

@@ -22,7 +22,51 @@
#include <wayland-server.h>
#include "meta-wayland-seat.h"
#include <glib.h>
#include "meta-wayland-types.h"
struct _MetaWaylandPointerGrabInterface
{
void (*focus) (MetaWaylandPointerGrab * grab,
MetaWaylandSurface * surface, wl_fixed_t x, wl_fixed_t y);
void (*motion) (MetaWaylandPointerGrab * grab,
uint32_t time, wl_fixed_t x, wl_fixed_t y);
void (*button) (MetaWaylandPointerGrab * grab,
uint32_t time, uint32_t button, uint32_t state);
};
struct _MetaWaylandPointerGrab
{
const MetaWaylandPointerGrabInterface *interface;
MetaWaylandPointer *pointer;
MetaWaylandSurface *focus;
wl_fixed_t x, y;
};
struct _MetaWaylandPointer
{
struct wl_list resource_list;
MetaWaylandSurface *focus;
struct wl_resource *focus_resource;
struct wl_listener focus_listener;
guint32 focus_serial;
struct wl_signal focus_signal;
MetaWaylandPointerGrab *grab;
MetaWaylandPointerGrab default_grab;
wl_fixed_t grab_x, grab_y;
guint32 grab_button;
guint32 grab_serial;
guint32 grab_time;
wl_fixed_t x, y;
MetaWaylandSurface *current;
struct wl_listener current_listener;
wl_fixed_t current_x, current_y;
guint32 button_count;
};
void
meta_wayland_pointer_init (MetaWaylandPointer *pointer);
@@ -35,6 +79,10 @@ meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer,
MetaWaylandSurface *surface,
wl_fixed_t sx,
wl_fixed_t sy);
void
meta_wayland_pointer_destroy_focus (MetaWaylandPointer *pointer);
void
meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer,
MetaWaylandPointerGrab *grab);
@@ -42,6 +90,11 @@ meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer,
void
meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer);
gboolean
meta_wayland_pointer_begin_modal (MetaWaylandPointer *pointer);
void
meta_wayland_pointer_end_modal (MetaWaylandPointer *pointer);
void
meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
MetaWaylandSurface *surface);

View File

@@ -28,43 +28,12 @@
#include <cairo.h>
#include "window-private.h"
#include "meta-weston-launch.h"
#include <meta/meta-cursor-tracker.h>
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
typedef struct _MetaWaylandSeat MetaWaylandSeat;
typedef struct _MetaWaylandPointer MetaWaylandPointer;
typedef struct _MetaWaylandPointerGrab MetaWaylandPointerGrab;
typedef struct _MetaWaylandPointerGrabInterface MetaWaylandPointerGrabInterface;
typedef struct _MetaWaylandKeyboard MetaWaylandKeyboard;
typedef struct _MetaWaylandKeyboardGrab MetaWaylandKeyboardGrab;
typedef struct _MetaWaylandKeyboardGrabInterface MetaWaylandKeyboardGrabInterface;
typedef struct _MetaWaylandDataOffer MetaWaylandDataOffer;
typedef struct _MetaWaylandDataSource MetaWaylandDataSource;
typedef struct
{
struct wl_resource *resource;
struct wl_signal destroy_signal;
struct wl_listener destroy_listener;
union
{
struct wl_shm_buffer *shm_buffer;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
struct wl_buffer *legacy_buffer;
G_GNUC_END_IGNORE_DEPRECATIONS;
};
int32_t width, height;
uint32_t busy_count;
} MetaWaylandBuffer;
typedef struct
{
MetaWaylandBuffer *buffer;
struct wl_listener destroy_listener;
} MetaWaylandBufferReference;
#include "meta-wayland-types.h"
#include "meta-wayland-surface.h"
#include "meta-wayland-seat.h"
typedef struct
{
@@ -72,46 +41,6 @@ typedef struct
cairo_region_t *region;
} MetaWaylandRegion;
struct _MetaWaylandSurface
{
struct wl_resource *resource;
MetaWaylandCompositor *compositor;
guint32 xid;
int x;
int y;
MetaWaylandBufferReference buffer_ref;
MetaWindow *window;
gboolean has_shell_surface;
/* All the pending state, that wl_surface.commit will apply. */
struct
{
/* wl_surface.attach */
gboolean newly_attached;
MetaWaylandBuffer *buffer;
struct wl_listener buffer_destroy_listener;
int32_t sx;
int32_t sy;
/* wl_surface.damage */
cairo_region_t *damage;
/* wl_surface.frame */
struct wl_list frame_callback_list;
} pending;
};
#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
typedef struct _MetaWaylandSurface MetaWaylandSurface;
#endif
typedef struct
{
MetaWaylandSurface *surface;
struct wl_resource *resource;
struct wl_listener surface_destroy_listener;
} MetaWaylandShellSurface;
typedef struct
{
GSource source;
@@ -148,6 +77,9 @@ struct _MetaWaylandCompositor
struct wl_client *xwayland_client;
struct wl_resource *xserver_resource;
MetaLauncher *launcher;
int drm_fd;
MetaWaylandSeat *seat;
/* This surface is only used to keep drag of the implicit grab when
@@ -159,173 +91,6 @@ struct _MetaWaylandCompositor
guint32 implicit_grab_button;
};
struct _MetaWaylandPointerGrabInterface
{
void (*focus) (MetaWaylandPointerGrab * grab,
MetaWaylandSurface * surface, wl_fixed_t x, wl_fixed_t y);
void (*motion) (MetaWaylandPointerGrab * grab,
uint32_t time, wl_fixed_t x, wl_fixed_t y);
void (*button) (MetaWaylandPointerGrab * grab,
uint32_t time, uint32_t button, uint32_t state);
};
struct _MetaWaylandPointerGrab
{
const MetaWaylandPointerGrabInterface *interface;
MetaWaylandPointer *pointer;
MetaWaylandSurface *focus;
wl_fixed_t x, y;
};
struct _MetaWaylandPointer
{
struct wl_list resource_list;
MetaWaylandSurface *focus;
struct wl_resource *focus_resource;
struct wl_listener focus_listener;
guint32 focus_serial;
struct wl_signal focus_signal;
MetaWaylandPointerGrab *grab;
MetaWaylandPointerGrab default_grab;
wl_fixed_t grab_x, grab_y;
guint32 grab_button;
guint32 grab_serial;
guint32 grab_time;
wl_fixed_t x, y;
MetaWaylandSurface *current;
struct wl_listener current_listener;
wl_fixed_t current_x, current_y;
guint32 button_count;
};
struct _MetaWaylandKeyboardGrabInterface
{
void (*key) (MetaWaylandKeyboardGrab * grab, uint32_t time,
uint32_t key, uint32_t state);
void (*modifiers) (MetaWaylandKeyboardGrab * grab, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group);
};
struct _MetaWaylandKeyboardGrab
{
const MetaWaylandKeyboardGrabInterface *interface;
MetaWaylandKeyboard *keyboard;
MetaWaylandSurface *focus;
uint32_t key;
};
typedef struct
{
struct xkb_keymap *keymap;
int keymap_fd;
size_t keymap_size;
char *keymap_area;
xkb_mod_index_t shift_mod;
xkb_mod_index_t caps_mod;
xkb_mod_index_t ctrl_mod;
xkb_mod_index_t alt_mod;
xkb_mod_index_t mod2_mod;
xkb_mod_index_t mod3_mod;
xkb_mod_index_t super_mod;
xkb_mod_index_t mod5_mod;
} MetaWaylandXkbInfo;
struct _MetaWaylandKeyboard
{
struct wl_list resource_list;
MetaWaylandSurface *focus;
struct wl_resource *focus_resource;
struct wl_listener focus_listener;
uint32_t focus_serial;
struct wl_signal focus_signal;
MetaWaylandKeyboardGrab *grab;
MetaWaylandKeyboardGrab default_grab;
uint32_t grab_key;
uint32_t grab_serial;
uint32_t grab_time;
struct wl_array keys;
struct
{
uint32_t mods_depressed;
uint32_t mods_latched;
uint32_t mods_locked;
uint32_t group;
} modifiers;
struct wl_display *display;
struct xkb_context *xkb_context;
MetaWaylandXkbInfo xkb_info;
struct xkb_rule_names xkb_names;
MetaWaylandKeyboardGrab input_method_grab;
struct wl_resource *input_method_resource;
ClutterModifierType last_modifier_state;
};
struct _MetaWaylandDataOffer
{
struct wl_resource *resource;
MetaWaylandDataSource *source;
struct wl_listener source_destroy_listener;
};
struct _MetaWaylandDataSource
{
struct wl_resource *resource;
struct wl_array mime_types;
void (*accept) (MetaWaylandDataSource * source,
uint32_t serial, const char *mime_type);
void (*send) (MetaWaylandDataSource * source,
const char *mime_type, int32_t fd);
void (*cancel) (MetaWaylandDataSource * source);
};
struct _MetaWaylandSeat
{
struct wl_list base_resource_list;
struct wl_signal destroy_signal;
uint32_t selection_serial;
MetaWaylandDataSource *selection_data_source;
struct wl_listener selection_data_source_listener;
struct wl_signal selection_signal;
struct wl_list drag_resource_list;
struct wl_client *drag_client;
MetaWaylandDataSource *drag_data_source;
struct wl_listener drag_data_source_listener;
MetaWaylandSurface *drag_focus;
struct wl_resource *drag_focus_resource;
struct wl_listener drag_focus_listener;
MetaWaylandPointerGrab drag_grab;
MetaWaylandSurface *drag_surface;
struct wl_listener drag_icon_listener;
struct wl_signal drag_icon_signal;
MetaWaylandPointer pointer;
MetaWaylandKeyboard keyboard;
struct wl_display *display;
MetaCursorTracker *cursor_tracker;
MetaWaylandSurface *sprite;
int hotspot_x, hotspot_y;
struct wl_listener sprite_destroy_listener;
ClutterActor *current_stage;
};
void meta_wayland_init (void);
void meta_wayland_finalize (void);
@@ -338,6 +103,12 @@ void meta_wayland_compositor_repick (MetaWaylandComp
void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
MetaWindow *window);
void meta_wayland_surface_free (MetaWaylandSurface *surface);
MetaLauncher *meta_wayland_compositor_get_launcher (MetaWaylandCompositor *compositor);
MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resource *resource);
void meta_wayland_buffer_reference (MetaWaylandBufferReference *ref,
MetaWaylandBuffer *buffer);
#endif /* META_WAYLAND_PRIVATE_H */

View File

@@ -271,7 +271,8 @@ pointer_handle_sprite_destroy (struct wl_listener *listener, void *data)
}
MetaWaylandSeat *
meta_wayland_seat_new (struct wl_display *display)
meta_wayland_seat_new (struct wl_display *display,
gboolean is_native)
{
MetaWaylandSeat *seat = g_new0 (MetaWaylandSeat, 1);
@@ -285,7 +286,7 @@ meta_wayland_seat_new (struct wl_display *display)
meta_wayland_pointer_init (&seat->pointer);
meta_wayland_keyboard_init (&seat->keyboard, display);
meta_wayland_keyboard_init (&seat->keyboard, display, is_native);
seat->display = display;

View File

@@ -27,10 +27,68 @@
#include <clutter/clutter.h>
#include <glib.h>
#include "meta-wayland-private.h"
#include <meta/meta-cursor-tracker.h>
#include "meta-wayland-types.h"
#include "meta-wayland-keyboard.h"
#include "meta-wayland-pointer.h"
struct _MetaWaylandDataOffer
{
struct wl_resource *resource;
MetaWaylandDataSource *source;
struct wl_listener source_destroy_listener;
};
struct _MetaWaylandDataSource
{
struct wl_resource *resource;
struct wl_array mime_types;
void (*accept) (MetaWaylandDataSource * source,
uint32_t serial, const char *mime_type);
void (*send) (MetaWaylandDataSource * source,
const char *mime_type, int32_t fd);
void (*cancel) (MetaWaylandDataSource * source);
};
struct _MetaWaylandSeat
{
struct wl_list base_resource_list;
struct wl_signal destroy_signal;
uint32_t selection_serial;
MetaWaylandDataSource *selection_data_source;
struct wl_listener selection_data_source_listener;
struct wl_signal selection_signal;
struct wl_list drag_resource_list;
struct wl_client *drag_client;
MetaWaylandDataSource *drag_data_source;
struct wl_listener drag_data_source_listener;
MetaWaylandSurface *drag_focus;
struct wl_resource *drag_focus_resource;
struct wl_listener drag_focus_listener;
MetaWaylandPointerGrab drag_grab;
MetaWaylandSurface *drag_surface;
struct wl_listener drag_icon_listener;
struct wl_signal drag_icon_signal;
MetaWaylandPointer pointer;
MetaWaylandKeyboard keyboard;
struct wl_display *display;
MetaCursorTracker *cursor_tracker;
MetaWaylandSurface *sprite;
int hotspot_x, hotspot_y;
struct wl_listener sprite_destroy_listener;
ClutterActor *current_stage;
};
MetaWaylandSeat *
meta_wayland_seat_new (struct wl_display *display);
meta_wayland_seat_new (struct wl_display *display,
gboolean is_native);
void
meta_wayland_seat_handle_event (MetaWaylandSeat *seat,

View File

@@ -26,6 +26,7 @@
#include <cogl/cogl-wayland-server.h>
#include "meta-wayland-stage.h"
#include "meta-wayland-private.h"
#include "meta/meta-window-actor.h"
#include "meta/meta-shaped-texture.h"
#include "meta-cursor-tracker-private.h"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef META_WAYLAND_SURFACE_H
#define META_WAYLAND_SURFACE_H
#include <wayland-server.h>
#include <xkbcommon/xkbcommon.h>
#include <clutter/clutter.h>
#include <glib.h>
#include <cairo.h>
#include <meta/meta-cursor-tracker.h>
#include "meta-wayland-types.h"
struct _MetaWaylandBuffer
{
struct wl_resource *resource;
struct wl_signal destroy_signal;
struct wl_listener destroy_listener;
int32_t width, height;
uint32_t busy_count;
};
struct _MetaWaylandBufferReference
{
MetaWaylandBuffer *buffer;
struct wl_listener destroy_listener;
};
typedef struct
{
/* wl_surface.attach */
gboolean newly_attached;
MetaWaylandBuffer *buffer;
struct wl_listener buffer_destroy_listener;
int32_t sx;
int32_t sy;
/* wl_surface.damage */
cairo_region_t *damage;
cairo_region_t *input_region;
cairo_region_t *opaque_region;
/* wl_surface.frame */
struct wl_list frame_callback_list;
} MetaWaylandDoubleBufferedState;
typedef struct
{
char *title;
char *wm_class;
char *gtk_application_id;
char *gtk_unique_bus_name;
char *gtk_app_menu_path;
char *gtk_menubar_path;
char *gtk_application_object_path;
char *gtk_window_object_path;
} MetaWaylandSurfaceInitialState;
struct _MetaWaylandSurface
{
struct wl_resource *resource;
MetaWaylandCompositor *compositor;
guint32 xid;
int x;
int y;
MetaWaylandBufferReference buffer_ref;
MetaWindow *window;
gboolean has_shell_surface;
gboolean has_gtk_surface;
/* All the pending state, that wl_surface.commit will apply. */
MetaWaylandDoubleBufferedState pending;
/* All the initial state, that wl_shell_surface.set_* will apply
(through meta_window_new_for_wayland) */
MetaWaylandSurfaceInitialState *initial_state;
};
typedef struct
{
MetaWaylandSurface *surface;
struct wl_resource *resource;
struct wl_listener surface_destroy_listener;
} MetaWaylandSurfaceExtension;
void meta_wayland_init_shell (MetaWaylandCompositor *compositor);
MetaWaylandSurface *meta_wayland_surface_create (MetaWaylandCompositor *compositor,
struct wl_client *client,
guint32 id,
guint32 version);
void meta_wayland_surface_free (MetaWaylandSurface *surface);
void meta_wayland_surface_set_initial_state (MetaWaylandSurface *surface,
MetaWindow *window);
#endif

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef META_WAYLAND_TYPES_H
#define META_WAYLAND_TYPES_H
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
typedef struct _MetaWaylandSeat MetaWaylandSeat;
typedef struct _MetaWaylandPointer MetaWaylandPointer;
typedef struct _MetaWaylandPointerGrab MetaWaylandPointerGrab;
typedef struct _MetaWaylandPointerGrabInterface MetaWaylandPointerGrabInterface;
typedef struct _MetaWaylandKeyboard MetaWaylandKeyboard;
typedef struct _MetaWaylandKeyboardGrab MetaWaylandKeyboardGrab;
typedef struct _MetaWaylandKeyboardGrabInterface MetaWaylandKeyboardGrabInterface;
typedef struct _MetaWaylandDataOffer MetaWaylandDataOffer;
typedef struct _MetaWaylandDataSource MetaWaylandDataSource;
typedef struct _MetaWaylandBuffer MetaWaylandBuffer;
typedef struct _MetaWaylandBufferReference MetaWaylandBufferReference;
typedef struct _MetaWaylandSurface MetaWaylandSurface;
#endif

View File

@@ -50,6 +50,7 @@
#include <meta/main.h>
#include "frame.h"
#include "meta-idle-monitor-private.h"
#include "meta-weston-launch.h"
#include "monitor-private.h"
static MetaWaylandCompositor _meta_wayland_compositor;
@@ -135,7 +136,7 @@ meta_wayland_buffer_destroy_handler (struct wl_listener *listener,
g_slice_free (MetaWaylandBuffer, buffer);
}
static MetaWaylandBuffer *
MetaWaylandBuffer *
meta_wayland_buffer_from_resource (struct wl_resource *resource)
{
MetaWaylandBuffer *buffer;
@@ -164,7 +165,7 @@ meta_wayland_buffer_from_resource (struct wl_resource *resource)
static void
meta_wayland_buffer_reference_handle_destroy (struct wl_listener *listener,
void *data)
void *data)
{
MetaWaylandBufferReference *ref =
wl_container_of (listener, ref, destroy_listener);
@@ -174,7 +175,7 @@ meta_wayland_buffer_reference_handle_destroy (struct wl_listener *listener,
ref->buffer = NULL;
}
static void
void
meta_wayland_buffer_reference (MetaWaylandBufferReference *ref,
MetaWaylandBuffer *buffer)
{
@@ -201,245 +202,6 @@ meta_wayland_buffer_reference (MetaWaylandBufferReference *ref,
ref->destroy_listener.notify = meta_wayland_buffer_reference_handle_destroy;
}
static void
surface_process_damage (MetaWaylandSurface *surface,
cairo_region_t *region)
{
if (surface->window &&
surface->buffer_ref.buffer)
{
MetaWindowActor *window_actor =
META_WINDOW_ACTOR (meta_window_get_compositor_private (surface->window));
if (window_actor)
{
int i, n_rectangles = cairo_region_num_rectangles (region);
for (i = 0; i < n_rectangles; i++)
{
cairo_rectangle_int_t rectangle;
cairo_region_get_rectangle (region, i, &rectangle);
meta_window_actor_process_wayland_damage (window_actor,
rectangle.x,
rectangle.y,
rectangle.width,
rectangle.height);
}
}
}
}
static void
meta_wayland_surface_destroy (struct wl_client *wayland_client,
struct wl_resource *wayland_resource)
{
wl_resource_destroy (wayland_resource);
}
static void
meta_wayland_surface_attach (struct wl_client *wayland_client,
struct wl_resource *wayland_surface_resource,
struct wl_resource *wayland_buffer_resource,
gint32 sx, gint32 sy)
{
MetaWaylandSurface *surface =
wl_resource_get_user_data (wayland_surface_resource);
MetaWaylandBuffer *buffer;
/* X11 unmanaged window */
if (!surface)
return;
if (wayland_buffer_resource)
buffer = meta_wayland_buffer_from_resource (wayland_buffer_resource);
else
buffer = NULL;
/* Attach without commit in between does not send wl_buffer.release */
if (surface->pending.buffer)
wl_list_remove (&surface->pending.buffer_destroy_listener.link);
surface->pending.sx = sx;
surface->pending.sy = sy;
surface->pending.buffer = buffer;
surface->pending.newly_attached = TRUE;
if (buffer)
wl_signal_add (&buffer->destroy_signal,
&surface->pending.buffer_destroy_listener);
}
static void
meta_wayland_surface_damage (struct wl_client *client,
struct wl_resource *surface_resource,
gint32 x,
gint32 y,
gint32 width,
gint32 height)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
cairo_rectangle_int_t rectangle = { x, y, width, height };
/* X11 unmanaged window */
if (!surface)
return;
cairo_region_union_rectangle (surface->pending.damage, &rectangle);
}
static void
destroy_frame_callback (struct wl_resource *callback_resource)
{
MetaWaylandFrameCallback *callback =
wl_resource_get_user_data (callback_resource);
wl_list_remove (&callback->link);
g_slice_free (MetaWaylandFrameCallback, callback);
}
static void
meta_wayland_surface_frame (struct wl_client *client,
struct wl_resource *surface_resource,
guint32 callback_id)
{
MetaWaylandFrameCallback *callback;
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
/* X11 unmanaged window */
if (!surface)
return;
callback = g_slice_new0 (MetaWaylandFrameCallback);
callback->compositor = surface->compositor;
callback->resource = wl_resource_create (client,
&wl_callback_interface, 1,
callback_id);
wl_resource_set_user_data (callback->resource, callback);
wl_resource_set_destructor (callback->resource, destroy_frame_callback);
wl_list_insert (surface->pending.frame_callback_list.prev, &callback->link);
}
static void
meta_wayland_surface_set_opaque_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region)
{
g_warning ("TODO: support set_opaque_region request");
}
static void
meta_wayland_surface_set_input_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region)
{
g_warning ("TODO: support set_input_region request");
}
static void
empty_region (cairo_region_t *region)
{
cairo_rectangle_int_t rectangle = { 0, 0, 0, 0 };
cairo_region_intersect_rectangle (region, &rectangle);
}
static void
meta_wayland_surface_commit (struct wl_client *client,
struct wl_resource *resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
MetaWaylandCompositor *compositor;
/* X11 unmanaged window */
if (!surface)
return;
compositor = surface->compositor;
/* wl_surface.attach */
if (surface->pending.newly_attached &&
surface->buffer_ref.buffer != surface->pending.buffer)
{
/* Note: we set this before informing any window-actor since the
* window actor will expect to find the new buffer within the
* surface. */
meta_wayland_buffer_reference (&surface->buffer_ref,
surface->pending.buffer);
if (surface->pending.buffer)
{
MetaWaylandBuffer *buffer = surface->pending.buffer;
if (surface->window)
{
MetaWindow *window = surface->window;
MetaWindowActor *window_actor =
META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
MetaRectangle rect;
meta_window_get_input_rect (surface->window, &rect);
if (window_actor)
meta_window_actor_attach_wayland_buffer (window_actor, buffer);
/* XXX: we resize X based surfaces according to X events */
if (surface->xid == 0 &&
(buffer->width != rect.width || buffer->height != rect.height))
meta_window_resize (surface->window, FALSE, buffer->width, buffer->height);
}
else if (surface == compositor->seat->sprite)
meta_wayland_seat_update_sprite (compositor->seat);
}
}
if (surface->pending.buffer)
{
wl_list_remove (&surface->pending.buffer_destroy_listener.link);
surface->pending.buffer = NULL;
}
surface->pending.sx = 0;
surface->pending.sy = 0;
surface->pending.newly_attached = FALSE;
surface_process_damage (surface, surface->pending.damage);
empty_region (surface->pending.damage);
/* wl_surface.frame */
wl_list_insert_list (&compositor->frame_callbacks,
&surface->pending.frame_callback_list);
wl_list_init (&surface->pending.frame_callback_list);
}
static void
meta_wayland_surface_set_buffer_transform (struct wl_client *client,
struct wl_resource *resource,
int32_t transform)
{
g_warning ("TODO: support set_buffer_transform request");
}
static void
meta_wayland_surface_set_buffer_scale (struct wl_client *client,
struct wl_resource *resource,
int scale)
{
g_warning ("TODO: support set_buffer_scale request");
}
const struct wl_surface_interface meta_wayland_surface_interface = {
meta_wayland_surface_destroy,
meta_wayland_surface_attach,
meta_wayland_surface_damage,
meta_wayland_surface_frame,
meta_wayland_surface_set_opaque_region,
meta_wayland_surface_set_input_region,
meta_wayland_surface_commit,
meta_wayland_surface_set_buffer_transform,
meta_wayland_surface_set_buffer_scale
};
void
meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
MetaWindow *window)
@@ -459,82 +221,6 @@ meta_wayland_compositor_repick (MetaWaylandCompositor *compositor)
NULL);
}
void
meta_wayland_surface_free (MetaWaylandSurface *surface)
{
MetaWaylandCompositor *compositor = surface->compositor;
MetaWaylandFrameCallback *cb, *next;
compositor->surfaces = g_list_remove (compositor->surfaces, surface);
meta_wayland_buffer_reference (&surface->buffer_ref, NULL);
if (surface->pending.buffer)
wl_list_remove (&surface->pending.buffer_destroy_listener.link);
cairo_region_destroy (surface->pending.damage);
wl_list_for_each_safe (cb, next,
&surface->pending.frame_callback_list, link)
wl_resource_destroy (cb->resource);
meta_wayland_compositor_repick (compositor);
/* Check that repick didn't pick the freed surface */
g_assert (surface != compositor->seat->pointer.focus);
g_assert (surface != compositor->seat->keyboard.focus);
if (compositor->implicit_grab_surface == surface)
compositor->implicit_grab_surface = compositor->seat->pointer.current;
if (surface->resource)
wl_resource_set_user_data (surface->resource, NULL);
g_slice_free (MetaWaylandSurface, surface);
}
static void
meta_wayland_surface_resource_destroy_cb (struct wl_resource *resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
/* There are four cases here:
- An X11 unmanaged window -> surface is NULL, nothing to do
- An X11 unmanaged window, but we got the wayland event first ->
just clear the resource pointer
- A wayland surface without window (destroyed before set_toplevel) ->
need to free the surface itself
- A wayland window -> need to unmanage
*/
if (surface)
{
surface->resource = NULL;
/* NB: If the surface corresponds to an X window then we will be
* sure to free the MetaWindow according to some X event. */
if (surface->window &&
surface->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
{
MetaDisplay *display = meta_get_display ();
guint32 timestamp = meta_display_get_current_time_roundtrip (display);
meta_window_unmanage (surface->window, timestamp);
}
else
meta_wayland_surface_free (surface);
}
}
static void
surface_handle_pending_buffer_destroy (struct wl_listener *listener,
void *data)
{
MetaWaylandSurface *surface =
wl_container_of (listener, surface, pending.buffer_destroy_listener);
surface->pending.buffer = NULL;
}
static void
meta_wayland_compositor_create_surface (struct wl_client *wayland_client,
struct wl_resource *wayland_compositor_resource,
@@ -542,24 +228,14 @@ meta_wayland_compositor_create_surface (struct wl_client *wayland_client,
{
MetaWaylandCompositor *compositor =
wl_resource_get_user_data (wayland_compositor_resource);
MetaWaylandSurface *surface = g_slice_new0 (MetaWaylandSurface);
surface->compositor = compositor;
MetaWaylandSurface *surface;
/* a surface inherits the version from the compositor */
surface->resource = wl_resource_create (wayland_client,
&wl_surface_interface,
wl_resource_get_version (wayland_compositor_resource),
id);
wl_resource_set_implementation (surface->resource, &meta_wayland_surface_interface, surface,
meta_wayland_surface_resource_destroy_cb);
surface->pending.damage = cairo_region_create ();
surface->pending.buffer_destroy_listener.notify =
surface_handle_pending_buffer_destroy;
wl_list_init (&surface->pending.frame_callback_list);
surface = meta_wayland_surface_create (compositor,
wayland_client,
id,
wl_resource_get_version (wayland_compositor_resource));
compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
}
@@ -846,405 +522,6 @@ compositor_bind (struct wl_client *client,
wl_resource_set_implementation (resource, &meta_wayland_compositor_interface, compositor, NULL);
}
static void
shell_surface_pong (struct wl_client *client,
struct wl_resource *resource,
guint32 serial)
{
}
typedef struct _MetaWaylandGrab
{
MetaWaylandPointerGrab grab;
MetaWaylandShellSurface *shell_surface;
struct wl_listener shell_surface_destroy_listener;
MetaWaylandPointer *pointer;
} MetaWaylandGrab;
typedef struct _MetaWaylandMoveGrab
{
MetaWaylandGrab base;
wl_fixed_t dx, dy;
} MetaWaylandMoveGrab;
static void
destroy_shell_surface_grab_listener (struct wl_listener *listener,
void *data)
{
MetaWaylandGrab *grab = wl_container_of (listener, grab,
shell_surface_destroy_listener);
grab->shell_surface = NULL;
/* XXX: Could we perhaps just stop the grab here so we don't have
* to consider grab->shell_surface becoming NULL in grab interface
* callbacks? */
}
typedef enum _GrabCursor
{
GRAB_CURSOR_MOVE,
} GrabCursor;
static void
grab_pointer (MetaWaylandGrab *grab,
const MetaWaylandPointerGrabInterface *interface,
MetaWaylandShellSurface *shell_surface,
MetaWaylandPointer *pointer,
GrabCursor cursor)
{
/* TODO: popup_grab_end (pointer); */
grab->grab.interface = interface;
grab->shell_surface = shell_surface;
grab->shell_surface_destroy_listener.notify =
destroy_shell_surface_grab_listener;
wl_resource_add_destroy_listener (shell_surface->resource,
&grab->shell_surface_destroy_listener);
grab->pointer = pointer;
grab->grab.focus = shell_surface->surface;
meta_wayland_pointer_start_grab (pointer, &grab->grab);
/* TODO: send_grab_cursor (cursor); */
/* XXX: In Weston there is a desktop shell protocol which has
* a set_grab_surface request that's used to specify the surface
* that's focused here.
*
* TODO: understand why.
*
* XXX: For now we just focus the surface directly associated with
* the grab.
*/
meta_wayland_pointer_set_focus (pointer,
grab->shell_surface->surface,
wl_fixed_from_int (0),
wl_fixed_from_int (0));
}
static void
release_pointer (MetaWaylandGrab *grab)
{
if (grab->shell_surface)
wl_list_remove (&grab->shell_surface_destroy_listener.link);
meta_wayland_pointer_end_grab (grab->pointer);
}
static void
noop_grab_focus (MetaWaylandPointerGrab *grab,
MetaWaylandSurface *surface,
wl_fixed_t x,
wl_fixed_t y)
{
grab->focus = NULL;
}
static void
move_grab_motion (MetaWaylandPointerGrab *grab,
uint32_t time,
wl_fixed_t x,
wl_fixed_t y)
{
MetaWaylandMoveGrab *move = (MetaWaylandMoveGrab *)grab;
MetaWaylandPointer *pointer = move->base.pointer;
MetaWaylandShellSurface *shell_surface = move->base.shell_surface;
if (!shell_surface)
return;
meta_window_move (shell_surface->surface->window,
TRUE,
wl_fixed_to_int (pointer->x + move->dx),
wl_fixed_to_int (pointer->y + move->dy));
}
static void
move_grab_button (MetaWaylandPointerGrab *pointer_grab,
uint32_t time,
uint32_t button,
uint32_t state_w)
{
MetaWaylandGrab *grab =
wl_container_of (pointer_grab, grab, grab);
MetaWaylandMoveGrab *move = (MetaWaylandMoveGrab *)grab;
MetaWaylandPointer *pointer = grab->pointer;
enum wl_pointer_button_state state = state_w;
if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED)
{
release_pointer (grab);
g_slice_free (MetaWaylandMoveGrab, move);
}
}
static const MetaWaylandPointerGrabInterface move_grab_interface = {
noop_grab_focus,
move_grab_motion,
move_grab_button,
};
static void
start_surface_move (MetaWaylandShellSurface *shell_surface,
MetaWaylandSeat *seat)
{
MetaWaylandMoveGrab *move;
MetaRectangle rect;
g_return_if_fail (shell_surface != NULL);
/* TODO: check if the surface is fullscreen when we support fullscreen */
move = g_slice_new (MetaWaylandMoveGrab);
meta_window_get_input_rect (shell_surface->surface->window,
&rect);
move->dx = wl_fixed_from_int (rect.x) - seat->pointer.grab_x;
move->dy = wl_fixed_from_int (rect.y) - seat->pointer.grab_y;
grab_pointer (&move->base, &move_grab_interface, shell_surface,
&seat->pointer, GRAB_CURSOR_MOVE);
}
static void
shell_surface_move (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
guint32 serial)
{
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
if (seat->pointer.button_count == 0 ||
seat->pointer.grab_serial != serial ||
seat->pointer.focus != shell_surface->surface)
return;
start_surface_move (shell_surface, seat);
}
static void
shell_surface_resize (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat,
guint32 serial,
guint32 edges)
{
g_warning ("TODO: support shell_surface_resize request");
}
static void
ensure_surface_window (MetaWaylandSurface *surface)
{
MetaDisplay *display = meta_get_display ();
if (!surface->window)
{
int width, height;
if (surface->buffer_ref.buffer)
{
MetaWaylandBuffer *buffer = surface->buffer_ref.buffer;
width = buffer->width;
height = buffer->width;
}
else
{
width = 0;
height = 0;
}
surface->window =
meta_window_new_for_wayland (display, width, height, surface);
meta_window_calc_showing (surface->window);
}
}
static void
shell_surface_set_toplevel (struct wl_client *client,
struct wl_resource *resource)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface = shell_surface->surface;
/* NB: Surfaces from xwayland become managed based on X events. */
if (client == compositor->xwayland_client)
return;
ensure_surface_window (surface);
meta_window_unmake_fullscreen (surface->window);
}
static void
shell_surface_set_transient (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *parent,
int x,
int y,
guint32 flags)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface = shell_surface->surface;
/* NB: Surfaces from xwayland become managed based on X events. */
if (client == compositor->xwayland_client)
return;
ensure_surface_window (surface);
}
static void
shell_surface_set_fullscreen (struct wl_client *client,
struct wl_resource *resource,
guint32 method,
guint32 framerate,
struct wl_resource *output)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface = shell_surface->surface;
/* NB: Surfaces from xwayland become managed based on X events. */
if (client == compositor->xwayland_client)
return;
ensure_surface_window (surface);
meta_window_make_fullscreen (surface->window);
}
static void
shell_surface_set_popup (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat,
guint32 serial,
struct wl_resource *parent,
gint32 x,
gint32 y,
guint32 flags)
{
}
static void
shell_surface_set_maximized (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output)
{
g_warning ("TODO: support shell_surface_set_maximized request");
}
static void
shell_surface_set_title (struct wl_client *client,
struct wl_resource *resource,
const char *title)
{
g_warning ("TODO: support shell_surface_set_title request");
}
static void
shell_surface_set_class (struct wl_client *client,
struct wl_resource *resource,
const char *class_)
{
g_warning ("TODO: support shell_surface_set_class request");
}
static const struct wl_shell_surface_interface meta_wayland_shell_surface_interface =
{
shell_surface_pong,
shell_surface_move,
shell_surface_resize,
shell_surface_set_toplevel,
shell_surface_set_transient,
shell_surface_set_fullscreen,
shell_surface_set_popup,
shell_surface_set_maximized,
shell_surface_set_title,
shell_surface_set_class
};
static void
shell_handle_surface_destroy (struct wl_listener *listener,
void *data)
{
MetaWaylandShellSurface *shell_surface =
wl_container_of (listener, shell_surface, surface_destroy_listener);
shell_surface->surface->has_shell_surface = FALSE;
shell_surface->surface = NULL;
wl_resource_destroy (shell_surface->resource);
}
static void
destroy_shell_surface (struct wl_resource *resource)
{
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
/* In case cleaning up a dead client destroys shell_surface first */
if (shell_surface->surface)
{
wl_list_remove (&shell_surface->surface_destroy_listener.link);
shell_surface->surface->has_shell_surface = FALSE;
}
g_free (shell_surface);
}
static void
get_shell_surface (struct wl_client *client,
struct wl_resource *resource,
guint32 id,
struct wl_resource *surface_resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWaylandShellSurface *shell_surface;
if (surface->has_shell_surface)
{
wl_resource_post_error (surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"wl_shell::get_shell_surface already requested");
return;
}
shell_surface = g_new0 (MetaWaylandShellSurface, 1);
/* a shell surface inherits the version from the shell */
shell_surface->resource =
wl_resource_create (client, &wl_shell_surface_interface,
wl_resource_get_version (resource), id);
wl_resource_set_implementation (shell_surface->resource, &meta_wayland_shell_surface_interface,
shell_surface, destroy_shell_surface);
shell_surface->surface = surface;
shell_surface->surface_destroy_listener.notify = shell_handle_surface_destroy;
wl_resource_add_destroy_listener (surface->resource,
&shell_surface->surface_destroy_listener);
surface->has_shell_surface = TRUE;
}
static const struct wl_shell_interface meta_wayland_shell_interface =
{
get_shell_surface
};
static void
bind_shell (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &wl_shell_interface, version, id);
wl_resource_set_implementation (resource, &meta_wayland_shell_interface, data, NULL);
}
static void
stage_destroy_cb (void)
{
@@ -1529,6 +806,9 @@ meta_wayland_init (void)
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
guint event_signal;
MetaMonitorManager *monitors;
ClutterBackend *backend;
CoglContext *cogl_context;
CoglRenderer *cogl_renderer;
memset (compositor, 0, sizeof (MetaWaylandCompositor));
@@ -1565,9 +845,33 @@ meta_wayland_init (void)
clutter_wayland_set_compositor_display (compositor->wayland_display);
if (getenv ("WESTON_LAUNCHER_SOCK"))
compositor->launcher = meta_launcher_new ();
if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
g_error ("Failed to initialize Clutter");
backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (backend);
cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context));
if (cogl_renderer_get_winsys_id (cogl_renderer) == COGL_WINSYS_ID_EGL_KMS)
compositor->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
else
compositor->drm_fd = -1;
if (compositor->drm_fd >= 0)
{
GError *error;
error = NULL;
if (!meta_launcher_set_drm_fd (compositor->launcher, compositor->drm_fd, &error))
{
g_error ("Failed to set DRM fd to weston-launch and become DRM master: %s", error->message);
g_error_free (error);
}
}
meta_monitor_manager_initialize ();
monitors = meta_monitor_manager_get ();
g_signal_connect (monitors, "monitors-changed",
@@ -1584,7 +888,8 @@ meta_wayland_init (void)
meta_wayland_data_device_manager_init (compositor->wayland_display);
compositor->seat = meta_wayland_seat_new (compositor->wayland_display);
compositor->seat = meta_wayland_seat_new (compositor->wayland_display,
compositor->drm_fd >= 0);
g_signal_connect (compositor->stage,
"captured-event",
@@ -1601,10 +906,7 @@ meta_wayland_init (void)
compositor, /* hook_data */
NULL /* data_destroy */);
if (wl_global_create (compositor->wayland_display,
&wl_shell_interface, 1,
compositor, bind_shell) == NULL)
g_error ("Failed to register a global shell object");
meta_wayland_init_shell (compositor);
clutter_actor_show (compositor->stage);
@@ -1630,5 +932,16 @@ meta_wayland_init (void)
void
meta_wayland_finalize (void)
{
meta_xwayland_stop (meta_wayland_compositor_get_default ());
MetaWaylandCompositor *compositor;
compositor = meta_wayland_compositor_get_default ();
meta_xwayland_stop (compositor);
g_clear_object (&compositor->launcher);
}
MetaLauncher *
meta_wayland_compositor_get_launcher (MetaWaylandCompositor *compositor)
{
return compositor->launcher;
}

View File

@@ -0,0 +1,453 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <config.h>
#include <gio/gio.h>
#include <gio/gunixfdmessage.h>
#include <clutter/clutter.h>
#include <clutter/evdev/clutter-evdev.h>
#include <glib.h>
#include <sys/time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <drm.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "meta-weston-launch.h"
struct _MetaLauncherClass
{
GObjectClass parent_class;
void (*enter) (MetaLauncher *);
void (*leave) (MetaLauncher *);
};
struct _MetaLauncher
{
GObject parent;
GSocket *weston_launch;
gboolean vt_switched;
GMainContext *nested_context;
GMainLoop *nested_loop;
GSource *inner_source;
GSource *outer_source;
};
enum {
SIGNAL_ENTER,
SIGNAL_LEAVE,
SIGNAL_LAST
};
static int signals[SIGNAL_LAST];
G_DEFINE_TYPE (MetaLauncher, meta_launcher, G_TYPE_OBJECT);
static void handle_request_vt_switch (MetaLauncher *self);
static gboolean
request_vt_switch_idle (gpointer user_data)
{
handle_request_vt_switch (user_data);
return FALSE;
}
static gboolean
send_message_to_wl (MetaLauncher *self,
void *message,
gsize size,
GSocketControlMessage *out_cmsg,
GSocketControlMessage **in_cmsg,
GError **error)
{
struct weston_launcher_reply reply;
GInputVector in_iov = { &reply, sizeof (reply) };
GOutputVector out_iov = { message, size };
GSocketControlMessage *out_all_cmsg[2];
GSocketControlMessage **in_all_cmsg;
int flags = 0;
int i;
out_all_cmsg[0] = out_cmsg;
out_all_cmsg[1] = NULL;
if (g_socket_send_message (self->weston_launch, NULL,
&out_iov, 1,
out_all_cmsg, -1,
flags, NULL, error) != (gssize)size)
return FALSE;
if (g_socket_receive_message (self->weston_launch, NULL,
&in_iov, 1,
&in_all_cmsg, NULL,
&flags, NULL, error) != sizeof (reply))
return FALSE;
while (reply.header.opcode != ((struct weston_launcher_message*)message)->opcode)
{
/* There were events queued */
g_assert ((reply.header.opcode & WESTON_LAUNCHER_EVENT) == WESTON_LAUNCHER_EVENT);
/* This can never happen, because the only time mutter-launch can queue
this event is after confirming a VT switch, and we don't make requests
during that time.
Note that getting this event would be really bad, because we would be
in the wrong loop/context.
*/
g_assert (reply.header.opcode != WESTON_LAUNCHER_SERVER_VT_ENTER);
switch (reply.header.opcode)
{
case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH:
g_idle_add (request_vt_switch_idle, self);
break;
default:
g_assert_not_reached ();
}
if (g_socket_receive_message (self->weston_launch, NULL,
&in_iov, 1,
NULL, NULL,
&flags, NULL, error) != sizeof (reply))
return FALSE;
}
if (reply.ret != 0)
{
if (reply.ret == -1)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Got failure from weston-launch");
else
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-reply.ret),
"Got failure from weston-launch: %s", strerror (-reply.ret));
for (i = 0; in_all_cmsg && in_all_cmsg[i]; i++)
g_object_unref (in_all_cmsg[i]);
g_free (in_all_cmsg);
return FALSE;
}
if (in_all_cmsg && in_all_cmsg[0])
{
for (i = 1; in_all_cmsg[i]; i++)
g_object_unref (in_all_cmsg[i]);
*in_cmsg = in_all_cmsg[0];
}
g_free (in_all_cmsg);
return TRUE;
}
gboolean
meta_launcher_set_drm_fd (MetaLauncher *self,
int drm_fd,
GError **error)
{
struct weston_launcher_message message;
GSocketControlMessage *cmsg;
gboolean ok;
message.opcode = WESTON_LAUNCHER_DRM_SET_FD;
cmsg = g_unix_fd_message_new ();
if (g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (cmsg),
drm_fd, error) == FALSE)
{
g_object_unref (cmsg);
return FALSE;
}
ok = send_message_to_wl (self, &message, sizeof message, cmsg, NULL, error);
g_object_unref (cmsg);
return ok;
}
int
meta_launcher_open_input_device (MetaLauncher *self,
const char *name,
int flags,
GError **error)
{
struct weston_launcher_open *message;
GSocketControlMessage *cmsg;
gboolean ok;
gsize size;
int *fds, n_fd;
int ret;
size = sizeof (struct weston_launcher_open) + strlen (name) + 1;
message = g_malloc (size);
message->header.opcode = WESTON_LAUNCHER_OPEN;
message->flags = flags;
strcpy (message->path, name);
message->path[strlen(name)] = 0;
ok = send_message_to_wl (self, message, size, NULL, &cmsg, error);
if (ok)
{
g_assert (G_IS_UNIX_FD_MESSAGE (cmsg));
fds = g_unix_fd_message_steal_fds (G_UNIX_FD_MESSAGE (cmsg), &n_fd);
g_assert (n_fd == 1);
ret = fds[0];
g_free (fds);
g_object_unref (cmsg);
}
else
ret = -1;
g_free (message);
return ret;
}
static void
meta_launcher_finalize (GObject *object)
{
MetaLauncher *launcher = META_LAUNCHER (object);
g_source_destroy (launcher->outer_source);
g_source_destroy (launcher->inner_source);
g_main_loop_unref (launcher->nested_loop);
g_main_context_unref (launcher->nested_context);
g_object_unref (launcher->weston_launch);
G_OBJECT_CLASS (meta_launcher_parent_class)->finalize (object);
}
static void
meta_launcher_enter (MetaLauncher *launcher)
{
ClutterBackend *backend;
CoglContext *cogl_context;
CoglDisplay *cogl_display;
backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (backend);
cogl_display = cogl_context_get_display (cogl_context);
cogl_kms_display_queue_modes_reset (cogl_display);
clutter_evdev_reclaim_devices ();
}
static void
meta_launcher_leave (MetaLauncher *launcher)
{
clutter_evdev_release_devices ();
}
static int
on_evdev_device_open (const char *path,
int flags,
gpointer user_data,
GError **error)
{
MetaLauncher *launcher = user_data;
return meta_launcher_open_input_device (launcher, path, flags, error);
}
static void
handle_vt_enter (MetaLauncher *launcher)
{
g_assert (launcher->vt_switched);
g_main_loop_quit (launcher->nested_loop);
}
static void
handle_request_vt_switch (MetaLauncher *launcher)
{
struct weston_launcher_message message;
GError *error;
gboolean ok;
g_signal_emit (launcher, signals[SIGNAL_LEAVE], 0);
message.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH;
error = NULL;
ok = send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, &error);
if (!ok) {
g_warning ("Failed to acknowledge VT switch: %s", error->message);
g_error_free (error);
return;
}
g_assert (!launcher->vt_switched);
launcher->vt_switched = TRUE;
/* We can't do anything at this point, because we don't
have input devices and we don't have the DRM master,
so let's run a nested busy loop until the VT is reentered */
g_main_loop_run (launcher->nested_loop);
g_assert (launcher->vt_switched);
launcher->vt_switched = FALSE;
g_signal_emit (launcher, signals[SIGNAL_ENTER], 0);
}
static gboolean
on_socket_readable (GSocket *socket,
GIOCondition condition,
gpointer user_data)
{
MetaLauncher *launcher = user_data;
struct weston_launcher_event event;
gssize read;
GError *error;
if ((condition & G_IO_IN) == 0)
return TRUE;
error = NULL;
read = g_socket_receive (socket, (char*)&event, sizeof(event), NULL, &error);
if (read < (gssize)sizeof(event))
{
g_warning ("Error reading from weston-launcher socket: %s", error->message);
g_error_free (error);
return TRUE;
}
switch (event.header.opcode)
{
case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH:
handle_request_vt_switch (launcher);
break;
case WESTON_LAUNCHER_SERVER_VT_ENTER:
handle_vt_enter (launcher);
break;
}
return TRUE;
}
static int
env_get_fd (const char *env)
{
const char *value;
value = g_getenv (env);
if (value == NULL)
return -1;
else
return g_ascii_strtoll (value, NULL, 10);
}
static void
meta_launcher_init (MetaLauncher *self)
{
int launch_fd;
launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK");
if (launch_fd < 0)
g_error ("Invalid mutter-launch socket");
self->weston_launch = g_socket_new_from_fd (launch_fd, NULL);
clutter_evdev_set_open_callback (on_evdev_device_open, self);
self->nested_context = g_main_context_new ();
self->nested_loop = g_main_loop_new (self->nested_context, FALSE);
self->outer_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL);
g_source_set_callback (self->outer_source, (GSourceFunc)on_socket_readable, self, NULL);
g_source_attach (self->outer_source, NULL);
g_source_unref (self->outer_source);
self->inner_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL);
g_source_set_callback (self->inner_source, (GSourceFunc)on_socket_readable, self, NULL);
g_source_attach (self->inner_source, self->nested_context);
g_source_unref (self->inner_source);
}
static void
meta_launcher_class_init (MetaLauncherClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_launcher_finalize;
klass->enter = meta_launcher_enter;
klass->leave = meta_launcher_leave;
signals[SIGNAL_ENTER] = g_signal_new ("enter",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MetaLauncherClass, enter),
NULL, NULL, /* accumulator */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[SIGNAL_LEAVE] = g_signal_new ("leave",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MetaLauncherClass, leave),
NULL, NULL, /* accumulator */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
MetaLauncher *
meta_launcher_new (void)
{
return g_object_new (META_TYPE_LAUNCHER, NULL);
}
gboolean
meta_launcher_activate_vt (MetaLauncher *launcher,
int vt,
GError **error)
{
struct weston_launcher_activate_vt message;
message.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT;
message.vt = vt;
return send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, error);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2013 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef META_WESTON_LAUNCH_H
#define META_WESTON_LAUNCH_H
#include <glib-object.h>
#include "weston-launch.h"
#define META_TYPE_LAUNCHER (meta_launcher_get_type())
#define META_LAUNCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_LAUNCHER, MetaLauncher))
#define META_LAUNCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_LAUNCHER, MetaLauncherClass))
#define META_IS_LAUNCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_LAUNCHER))
#define META_IS_LAUNCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_LAUNCHER))
#define META_LAUNCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_LAUNCHER, MetaLauncherClass))
typedef struct _MetaLauncher MetaLauncher;
typedef struct _MetaLauncherClass MetaLauncherClass;
GType meta_launcher_get_type (void) G_GNUC_CONST;
MetaLauncher *meta_launcher_new (void);
gboolean meta_launcher_activate_vt (MetaLauncher *self,
int number,
GError **error);
gboolean meta_launcher_set_drm_fd (MetaLauncher *self,
int drm_fd,
GError **error);
gboolean meta_launcher_set_master (MetaLauncher *self,
gboolean master,
GError **error);
int meta_launcher_open_input_device (MetaLauncher *self,
const char *name,
int flags,
GError **error);
#endif

792
src/wayland/weston-launch.c Normal file
View File

@@ -0,0 +1,792 @@
/*
* Copyright © 2012 Benjamin Franzke
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <poll.h>
#include <errno.h>
#include <error.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/signalfd.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <termios.h>
#include <linux/vt.h>
#include <linux/major.h>
#include <linux/kd.h>
#include <pwd.h>
#include <grp.h>
#include <security/pam_appl.h>
#include <xf86drm.h>
#include <systemd/sd-login.h>
#include "weston-launch.h"
#define MAX_ARGV_SIZE 256
#define DRM_MAJOR 226
enum vt_state {
VT_HAS_VT,
VT_PENDING_CONFIRM,
VT_NOT_HAVE_VT,
};
struct weston_launch {
struct pam_conv pc;
pam_handle_t *ph;
int tty;
int ttynr;
int sock[2];
struct passwd *pw;
int signalfd;
pid_t child;
int verbose;
char *new_user;
struct termios terminal_attributes;
int kb_mode;
enum vt_state vt_state;
int drm_fd;
};
union cmsg_data { unsigned char b[4]; int fd; };
static void quit (struct weston_launch *wl, int status);
static int
weston_launch_allowed(struct weston_launch *wl)
{
char *session, *seat;
int err;
if (getuid() == 0)
return 1;
err = sd_pid_get_session(getpid(), &session);
if (err == 0 && session) {
if (sd_session_is_active(session) &&
sd_session_get_seat(session, &seat) == 0) {
free(seat);
free(session);
return 1;
}
free(session);
}
return 0;
}
static int
setup_launcher_socket(struct weston_launch *wl)
{
if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, wl->sock) < 0)
error(1, errno, "socketpair failed");
fcntl(wl->sock[0], F_SETFD, O_CLOEXEC);
return 0;
}
static int
setup_signals(struct weston_launch *wl)
{
int ret;
sigset_t mask;
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = SIG_DFL;
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
ret = sigaction(SIGCHLD, &sa, NULL);
assert(ret == 0);
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
sigaction(SIGHUP, &sa, NULL);
ret = sigemptyset(&mask);
assert(ret == 0);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGUSR1);
ret = sigprocmask(SIG_BLOCK, &mask, NULL);
assert(ret == 0);
wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (wl->signalfd < 0)
return -errno;
return 0;
}
static void
setenv_fd(const char *env, int fd)
{
char buf[32];
snprintf(buf, sizeof buf, "%d", fd);
setenv(env, buf, 1);
}
static int
handle_setdrmfd(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
struct cmsghdr *cmsg;
union cmsg_data *data;
struct stat s;
reply.header.opcode = WESTON_LAUNCHER_DRM_SET_FD;
reply.ret = -1;
if (wl->drm_fd != -1) {
error(0, 0, "DRM FD already set");
reply.ret = -EINVAL;
goto out;
}
cmsg = CMSG_FIRSTHDR(msg);
if (!cmsg ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
error(0, 0, "invalid control message");
reply.ret = -EINVAL;
goto out;
}
data = (union cmsg_data *) CMSG_DATA(cmsg);
if (data->fd < 0) {
error(0, 0, "missing drm fd in socket request");
reply.ret = -EINVAL;
goto out;
}
if (fstat(data->fd, &s) < 0) {
reply.ret = -errno;
goto out;
}
if (major(s.st_rdev) != DRM_MAJOR) {
fprintf(stderr, "FD is not for DRM\n");
reply.ret = -EPERM;
goto out;
}
wl->drm_fd = data->fd;
reply.ret = drmSetMaster(data->fd);
if (reply.ret < 0)
reply.ret = -errno;
if (wl->verbose)
fprintf(stderr, "weston-launch: set drm FD, ret: %d, fd: %d\n",
reply.ret, data->fd);
out:
do {
len = send(wl->sock[0], &reply, sizeof reply, 0);
} while (len < 0 && errno == EINTR);
if (len < 0)
return -1;
return 0;
}
static int
handle_confirm_vt_switch(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
reply.header.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH;
reply.ret = -1;
if (wl->vt_state != VT_PENDING_CONFIRM) {
error(0, 0, "unexpected CONFIRM_VT_SWITCH");
goto out;
}
if (wl->drm_fd != -1) {
int ret;
ret = drmDropMaster(wl->drm_fd);
if (ret < 0) {
fprintf(stderr, "failed to drop DRM master: %m\n");
} else if (wl->verbose) {
fprintf(stderr, "dropped DRM master for VT switch\n");
}
}
wl->vt_state = VT_NOT_HAVE_VT;
ioctl(wl->tty, VT_RELDISP, 1);
if (wl->verbose)
fprintf(stderr, "weston-launcher: confirmed VT switch\n");
reply.ret = 0;
out:
do {
len = send(wl->sock[0], &reply, sizeof reply, 0);
} while (len < 0 && errno == EINTR);
if (len < 0)
return -1;
return 0;
}
static int
handle_activate_vt(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
struct weston_launcher_activate_vt *message;
reply.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT;
reply.ret = -1;
if (len != sizeof(*message)) {
error(0, 0, "missing value in activate_vt request");
goto out;
}
message = msg->msg_iov->iov_base;
reply.ret = ioctl(wl->tty, VT_ACTIVATE, message->vt);
if (reply.ret < 0)
reply.ret = -errno;
if (wl->verbose)
fprintf(stderr, "weston-launch: activate VT, ret: %d\n", reply.ret);
out:
do {
len = send(wl->sock[0], &reply, sizeof reply, 0);
} while (len < 0 && errno == EINTR);
if (len < 0)
return -1;
return 0;
}
static int
handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
{
struct weston_launcher_reply reply;
int fd = -1;
char control[CMSG_SPACE(sizeof(fd))];
struct cmsghdr *cmsg;
struct stat s;
struct msghdr nmsg;
struct iovec iov;
struct weston_launcher_open *message;
union cmsg_data *data;
reply.header.opcode = WESTON_LAUNCHER_OPEN;
reply.ret = -1;
message = msg->msg_iov->iov_base;
if ((size_t)len < sizeof(*message))
goto err0;
/* Ensure path is null-terminated */
((char *) message)[len-1] = '\0';
if (stat(message->path, &s) < 0) {
reply.ret = -errno;
goto err0;
}
fd = open(message->path, message->flags);
if (fd < 0) {
fprintf(stderr, "Error opening device %s: %m\n",
message->path);
reply.ret = -errno;
goto err0;
}
if (major(s.st_rdev) != INPUT_MAJOR) {
close(fd);
fd = -1;
fprintf(stderr, "Device %s is not an input device\n",
message->path);
reply.ret = -EPERM;
goto err0;
}
err0:
memset(&nmsg, 0, sizeof nmsg);
nmsg.msg_iov = &iov;
nmsg.msg_iovlen = 1;
if (fd != -1) {
nmsg.msg_control = control;
nmsg.msg_controllen = sizeof control;
cmsg = CMSG_FIRSTHDR(&nmsg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
data = (union cmsg_data *) CMSG_DATA(cmsg);
data->fd = fd;
nmsg.msg_controllen = cmsg->cmsg_len;
reply.ret = 0;
}
iov.iov_base = &reply;
iov.iov_len = sizeof reply;
if (wl->verbose)
fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
message->path, reply.ret, fd);
do {
len = sendmsg(wl->sock[0], &nmsg, 0);
} while (len < 0 && errno == EINTR);
close(fd);
if (len < 0)
return -1;
return 0;
}
static int
handle_socket_msg(struct weston_launch *wl)
{
char control[CMSG_SPACE(sizeof(int))];
char buf[BUFSIZ];
struct msghdr msg;
struct iovec iov;
int ret = -1;
ssize_t len;
struct weston_launcher_message *message;
memset(&msg, 0, sizeof(msg));
iov.iov_base = buf;
iov.iov_len = sizeof buf;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof control;
do {
len = recvmsg(wl->sock[0], &msg, 0);
} while (len < 0 && errno == EINTR);
if (len < 1)
return -1;
message = (void *) buf;
switch (message->opcode) {
case WESTON_LAUNCHER_OPEN:
ret = handle_open(wl, &msg, len);
break;
case WESTON_LAUNCHER_DRM_SET_FD:
ret = handle_setdrmfd(wl, &msg, len);
break;
case WESTON_LAUNCHER_CONFIRM_VT_SWITCH:
ret = handle_confirm_vt_switch(wl, &msg, len);
break;
case WESTON_LAUNCHER_ACTIVATE_VT:
ret = handle_activate_vt(wl, &msg, len);
break;
}
return ret;
}
static void
tty_reset(struct weston_launch *wl)
{
struct vt_mode mode = { 0 };
if (ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
fprintf(stderr, "failed to restore keyboard mode: %m\n");
if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
if (tcsetattr(wl->tty, TCSANOW, &wl->terminal_attributes) < 0)
fprintf(stderr, "could not restore terminal to canonical mode\n");
mode.mode = VT_AUTO;
if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
fprintf(stderr, "could not reset vt handling\n");
}
static void
quit(struct weston_launch *wl, int status)
{
int err;
if (wl->child > 0)
kill(wl->child, SIGKILL);
close(wl->signalfd);
close(wl->sock[0]);
if (wl->drm_fd > 0)
close(wl->drm_fd);
tty_reset(wl);
if (wl->new_user) {
err = pam_close_session(wl->ph, 0);
if (err)
fprintf(stderr, "pam_close_session failed: %d: %s\n",
err, pam_strerror(wl->ph, err));
pam_end(wl->ph, err);
}
exit(status);
}
static int
handle_vt_switch(struct weston_launch *wl)
{
struct weston_launcher_event message;
ssize_t len;
if (wl->vt_state == VT_HAS_VT) {
wl->vt_state = VT_PENDING_CONFIRM;
message.header.opcode = WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH;
} else if (wl->vt_state == VT_NOT_HAVE_VT) {
wl->vt_state = VT_HAS_VT;
ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
if (wl->drm_fd != -1) {
int ret;
ret = drmSetMaster(wl->drm_fd);
if (ret < 0) {
fprintf(stderr, "failed to become DRM master: %m\n");
/* This is very, very bad, and the compositor will crash soon,
but oh well... */
} else if (wl->verbose) {
fprintf(stderr, "became DRM master after VT switch\n");
}
}
message.header.opcode = WESTON_LAUNCHER_SERVER_VT_ENTER;
} else
return -1;
message.detail = 0;
do {
len = send(wl->sock[0], &message, sizeof(message), 0);
} while (len < 0 && errno == EINTR);
return 0;
}
static int
handle_signal(struct weston_launch *wl)
{
struct signalfd_siginfo sig;
int pid, status, ret;
if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
error(0, errno, "reading signalfd failed");
return -1;
}
switch (sig.ssi_signo) {
case SIGCHLD:
pid = waitpid(-1, &status, 0);
if (pid == wl->child) {
wl->child = 0;
if (WIFEXITED(status))
ret = WEXITSTATUS(status);
else if (WIFSIGNALED(status))
/*
* If weston dies because of signal N, we
* return 10+N. This is distinct from
* weston-launch dying because of a signal
* (128+N).
*/
ret = 10 + WTERMSIG(status);
else
ret = 0;
quit(wl, ret);
}
break;
case SIGTERM:
case SIGINT:
if (wl->child)
kill(wl->child, sig.ssi_signo);
break;
case SIGUSR1:
return handle_vt_switch(wl);
default:
return -1;
}
return 0;
}
static int
setup_tty(struct weston_launch *wl)
{
struct stat buf;
struct termios raw_attributes;
struct vt_mode mode = { 0 };
char *session, *tty;
char path[PATH_MAX];
int ok;
ok = sd_pid_get_session(getpid(), &session);
if (ok < 0)
error(1, -ok, "could not determine current session");
ok = sd_session_get_tty(session, &tty);
if (ok == 0) {
snprintf(path, PATH_MAX, "/dev/%s", tty);
wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC);
free(tty);
#ifdef HAVE_SD_SESSION_GET_VT
} else if (ok == -ENOENT) {
/* Negative errnos are cool, right?
So cool that we can't distinguish "session not found"
from "key does not exist in the session file"!
Let's assume the latter, as we got the value
from sd_pid_get_session()...
*/
ok = sd_session_get_vt(session, &tty);
if (ok < 0)
error(1, -ok, "could not determine current TTY");
snprintf(path, PATH_MAX, "/dev/tty%s", tty);
wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC);
free(tty);
#endif
} else
error(1, -ok, "could not determine current TTY");
if (wl->tty < 0)
error(1, errno, "failed to open tty");
if (fstat(wl->tty, &buf) < 0)
error(1, errno, "stat %s failed", path);
if (major(buf.st_rdev) != TTY_MAJOR)
error(1, 0, "invalid tty device: %s", path);
wl->ttynr = minor(buf.st_rdev);
if (tcgetattr(wl->tty, &wl->terminal_attributes) < 0)
error(1, errno, "could not get terminal attributes");
/* Ignore control characters and disable echo */
raw_attributes = wl->terminal_attributes;
cfmakeraw(&raw_attributes);
/* Fix up line endings to be normal (cfmakeraw hoses them) */
raw_attributes.c_oflag |= OPOST | OCRNL;
/* Don't generate ttou signals */
raw_attributes.c_oflag &= ~TOSTOP;
if (tcsetattr(wl->tty, TCSANOW, &raw_attributes) < 0)
error(1, errno, "could not put terminal into raw mode");
ioctl(wl->tty, KDGKBMODE, &wl->kb_mode);
ok = ioctl(wl->tty, KDSKBMODE, K_OFF);
if (ok < 0) {
ok = ioctl(wl->tty, KDSKBMODE, K_RAW);
if (ok < 0)
error(1, errno, "failed to set keyboard mode on tty");
}
ok = ioctl(wl->tty, KDSETMODE, KD_GRAPHICS);
if (ok < 0)
error(1, errno, "failed to set KD_GRAPHICS mode on tty");
wl->vt_state = VT_HAS_VT;
mode.mode = VT_PROCESS;
mode.relsig = SIGUSR1;
mode.acqsig = SIGUSR1;
ok = ioctl(wl->tty, VT_SETMODE, &mode);
if (ok < 0)
error(1, errno, "failed to take control of vt handling");
return 0;
}
static void
drop_privileges(struct weston_launch *wl)
{
if (setgid(wl->pw->pw_gid) < 0 ||
#ifdef HAVE_INITGROUPS
initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
#endif
setuid(wl->pw->pw_uid) < 0)
error(1, errno, "dropping privileges failed");
}
static void
launch_compositor(struct weston_launch *wl, int argc, char *argv[])
{
char command[PATH_MAX];
char *child_argv[MAX_ARGV_SIZE];
sigset_t mask;
int i;
if (wl->verbose)
printf("weston-launch: spawned weston with pid: %d\n", getpid());
drop_privileges(wl);
setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
setenv("LD_LIBRARY_PATH", LIBDIR, 1);
unsetenv("DISPLAY");
/* Do not give our signal mask to the new process. */
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
snprintf (command, PATH_MAX, "%s \"$@\"", argv[0]);
child_argv[0] = wl->pw->pw_shell;
child_argv[1] = "-l";
child_argv[2] = "-c";
child_argv[3] = command;
for (i = 0; i < argc; ++i)
child_argv[4 + i] = argv[i];
child_argv[4 + i] = NULL;
execv(child_argv[0], child_argv);
error(1, errno, "exec failed");
}
static void
help(const char *name)
{
fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
fprintf(stderr, " -u, --user Start session as specified username\n");
fprintf(stderr, " -v, --verbose Be verbose\n");
fprintf(stderr, " -h, --help Display this help message\n");
}
int
main(int argc, char *argv[])
{
struct weston_launch wl;
int i, c;
struct option opts[] = {
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ 0, 0, NULL, 0 }
};
memset(&wl, 0, sizeof wl);
wl.drm_fd = -1;
while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
switch (c) {
case 'v':
wl.verbose = 1;
break;
case 'h':
help("weston-launch");
exit(EXIT_FAILURE);
}
}
if ((argc - optind) > (MAX_ARGV_SIZE - 6))
error(1, E2BIG, "Too many arguments to pass to weston");
if (optind >= argc)
error(1, 0, "Expected program argument");
if (strcmp (argv[optind], "mutter-wayland") &&
strcmp (argv[optind], "gnome-shell-wayland"))
error(1, 0, "mutter-launch can only be used to launch mutter or gnome-shell");
wl.pw = getpwuid(getuid());
if (wl.pw == NULL)
error(1, errno, "failed to get username");
if (!weston_launch_allowed(&wl))
error(1, 0, "Permission denied. You must run from an active and local (systemd) session.");
if (setup_tty(&wl) < 0)
exit(EXIT_FAILURE);
if (setup_launcher_socket(&wl) < 0)
exit(EXIT_FAILURE);
if (setup_signals(&wl) < 0)
exit(EXIT_FAILURE);
wl.child = fork();
if (wl.child == -1) {
error(1, errno, "fork failed");
exit(EXIT_FAILURE);
}
if (wl.child == 0)
launch_compositor(&wl, argc - optind, argv + optind);
close(wl.sock[1]);
while (1) {
struct pollfd fds[2];
int n;
fds[0].fd = wl.sock[0];
fds[0].events = POLLIN;
fds[1].fd = wl.signalfd;
fds[1].events = POLLIN;
n = poll(fds, 2, -1);
if (n < 0)
error(0, errno, "poll failed");
if (fds[0].revents & POLLIN)
handle_socket_msg(&wl);
if (fds[1].revents)
handle_signal(&wl);
}
return 0;
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright © 2012 Benjamin Franzke
* 2013 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _WESTON_LAUNCH_H_
#define _WESTON_LAUNCH_H_
enum weston_launcher_message_type {
WESTON_LAUNCHER_REQUEST,
WESTON_LAUNCHER_EVENT,
};
enum weston_launcher_opcode {
WESTON_LAUNCHER_OPEN = (1 << 1 | WESTON_LAUNCHER_REQUEST),
WESTON_LAUNCHER_DRM_SET_FD = (2 << 1 | WESTON_LAUNCHER_REQUEST),
WESTON_LAUNCHER_ACTIVATE_VT = (3 << 1 | WESTON_LAUNCHER_REQUEST),
WESTON_LAUNCHER_CONFIRM_VT_SWITCH = (4 << 1 | WESTON_LAUNCHER_REQUEST),
};
enum weston_launcher_server_opcode {
WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH = (1 << 1 | WESTON_LAUNCHER_EVENT),
WESTON_LAUNCHER_SERVER_VT_ENTER = (2 << 1 | WESTON_LAUNCHER_EVENT),
};
struct weston_launcher_message {
int opcode;
};
struct weston_launcher_open {
struct weston_launcher_message header;
int flags;
char path[0];
};
struct weston_launcher_activate_vt {
struct weston_launcher_message header;
int vt;
};
struct weston_launcher_reply {
struct weston_launcher_message header;
int ret;
};
struct weston_launcher_event {
struct weston_launcher_message header;
int detail; /* unused, but makes sure replies and events are serialized the same */
};
#endif